import { type Application, type Request, type Response, type RequestHandler } from 'express';
import type { Repository } from '../db/repository.js';
import { requireAdmin } from './auth.js';
import { deleteUserFolder } from '../user-folder/paths.js';

const passthrough: RequestHandler = (_req, _res, next) => next();

export function mountAdminApi(
  app: Application,
  repo: Repository,
  authActive = true,
  userFolderRoot = './data/users',
): void {
  const guard = authActive ? requireAdmin : passthrough;

  app.get('/api/admin/users', guard, (_req: Request, res: Response) => {
    const users = authActive ? repo.listUsers() : [];
    const enriched = users.map(u => ({
      ...u,
      orgs: repo.listUserGiteaOrgs(u.id),
      hasLocalPassword: repo.hasLocalCredential(u.id),
    }));
    res.json(enriched);
  });

  // Create a local (email + password) account. Admin-created accounts are
  // pre-approved (status=active). Rejects an email that already exists.
  app.post('/api/admin/users', guard, (req: Request, res: Response) => {
    if (!authActive) { res.status(403).json({ error: 'Auth is not configured' }); return; }
    const { email, password, role } = (req.body ?? {}) as { email?: string; password?: string; role?: string };
    if (typeof email !== 'string' || !email.trim() || typeof password !== 'string' || password.length < 8) {
      res.status(400).json({ error: 'email and a password of at least 8 characters are required' });
      return;
    }
    const wantRole = role === 'admin' ? 'admin' : 'user';
    try {
      const user = repo.createLocalUser({ email: email.trim(), password, role: wantRole, status: 'active' });
      res.status(201).json(user);
    } catch {
      res.status(409).json({ error: 'a user with that email already exists' });
    }
  });

  // Reset (or set) a user's local password. Invalidates their sessions so the
  // new password must be used.
  app.post('/api/admin/users/:id/password', guard, (req: Request, res: Response) => {
    if (!authActive) { res.status(403).json({ error: 'Auth is not configured' }); return; }
    const { id } = req.params;
    const { password } = (req.body ?? {}) as { password?: string };
    if (typeof password !== 'string' || password.length < 8) {
      res.status(400).json({ error: 'password must be at least 8 characters' });
      return;
    }
    if (!repo.getUserById(id)) { res.status(404).json({ error: 'User not found' }); return; }
    repo.setLocalPassword(id, password);
    repo.deleteSessionsByUserId(id);
    res.status(204).end();
  });

  app.patch('/api/admin/users/:id', guard, (req: Request, res: Response) => {
    if (!authActive) { res.status(403).json({ error: 'Auth is not configured' }); return; }
    const { id } = req.params;
    const { status, role } = req.body;
    const user = repo.getUserById(id);
    if (!user) {
      res.status(404).json({ error: 'User not found' });
      return;
    }
    repo.updateUser(id, { status, role });

    // Invalidate sessions on status/role change
    if (status === 'disabled' || status === 'pending' || role) {
      repo.deleteSessionsByUserId(id);
    }

    const updated = repo.getUserById(id);
    res.json(updated);
  });

  app.delete('/api/admin/users/:id', guard, (req: Request, res: Response) => {
    if (!authActive) { res.status(403).json({ error: 'Auth is not configured' }); return; }
    const { id } = req.params;
    // The shared `local` system/admin user is the no-auth fallback owner and
    // owns all single-user-mode data — never deletable.
    if (id === 'local') {
      res.status(400).json({ error: 'the local/system user cannot be deleted' });
      return;
    }
    const user = repo.getUserById(id);
    if (!user) {
      res.status(404).json({ error: 'User not found' });
      return;
    }
    repo.deleteSessionsByUserId(id);
    repo.deleteUser(id);
    // Remove the on-disk user folder too — the DB cascade only handles rows.
    // Without this the user's scripts/macros/recordings/notes orphaned on disk.
    deleteUserFolder(userFolderRoot, id);
    res.status(204).end();
  });

  // ── Local organizations (admin) ───────────────────────────────────────
  // A provider-agnostic 'org' visibility scope for local accounts. See
  // docs/superpowers/plans/2026-06-09-local-orgs.md.

  app.get('/api/admin/orgs', guard, (_req: Request, res: Response) => {
    if (!authActive) { res.json([]); return; }
    const orgs = repo.listLocalOrgs().map(o => ({ ...o, members: repo.listOrgMembers(o.id) }));
    res.json(orgs);
  });

  app.post('/api/admin/orgs', guard, (req: Request, res: Response) => {
    if (!authActive) { res.status(403).json({ error: 'Auth is not configured' }); return; }
    const { name } = (req.body ?? {}) as { name?: string };
    if (typeof name !== 'string' || !name.trim()) {
      res.status(400).json({ error: 'name is required' });
      return;
    }
    const createdBy = (req.user as { id?: string } | undefined)?.id ?? null;
    res.status(201).json(repo.createLocalOrg(name.trim(), createdBy));
  });

  app.patch('/api/admin/orgs/:id', guard, (req: Request, res: Response) => {
    if (!authActive) { res.status(403).json({ error: 'Auth is not configured' }); return; }
    const { id } = req.params;
    const { name } = (req.body ?? {}) as { name?: string };
    if (!repo.getLocalOrg(id)) { res.status(404).json({ error: 'Org not found' }); return; }
    if (typeof name !== 'string' || !name.trim()) { res.status(400).json({ error: 'name is required' }); return; }
    repo.renameLocalOrg(id, name.trim());
    res.json(repo.getLocalOrg(id));
  });

  app.delete('/api/admin/orgs/:id', guard, (req: Request, res: Response) => {
    if (!authActive) { res.status(403).json({ error: 'Auth is not configured' }); return; }
    const { id } = req.params;
    if (!repo.getLocalOrg(id)) { res.status(404).json({ error: 'Org not found' }); return; }
    // repo downgrades org-scoped tasks/schedules/jobs/notes to private first.
    repo.deleteLocalOrg(id);
    res.status(204).end();
  });

  app.post('/api/admin/orgs/:id/members', guard, (req: Request, res: Response) => {
    if (!authActive) { res.status(403).json({ error: 'Auth is not configured' }); return; }
    const { id } = req.params;
    const { userId, role } = (req.body ?? {}) as { userId?: string; role?: string };
    if (!repo.getLocalOrg(id)) { res.status(404).json({ error: 'Org not found' }); return; }
    if (typeof userId !== 'string' || !repo.getUserById(userId)) {
      res.status(400).json({ error: 'a valid userId is required' });
      return;
    }
    repo.addOrgMember(id, userId, role === 'owner' ? 'owner' : 'member');
    // The added user's session orgIds change — drop their sessions so the next
    // request re-derives membership (and 'org'-visible resources appear).
    repo.deleteSessionsByUserId(userId);
    res.status(204).end();
  });

  app.delete('/api/admin/orgs/:id/members/:userId', guard, (req: Request, res: Response) => {
    if (!authActive) { res.status(403).json({ error: 'Auth is not configured' }); return; }
    const { id, userId } = req.params;
    if (!repo.getLocalOrg(id)) { res.status(404).json({ error: 'Org not found' }); return; }
    repo.removeOrgMember(id, userId);
    repo.deleteSessionsByUserId(userId);
    res.status(204).end();
  });
}
