import { Router, Request, Response } from 'express';
import { prisma } from '../db/prisma';
import { requireAuth, requires } from '../middleware/auth';
import { generateApiKey } from '../services/apiKeys';
import { isValidPermissionKey } from '../constants/permissions';

const router = Router();

function serialize(key: {
  id: string;
  name: string;
  prefix: string;
  permissions: string;
  createdByPubkey: string;
  lastUsedAt: Date | null;
  revokedAt: Date | null;
  createdAt: Date;
}) {
  let permissions: string[] = [];
  try {
    const parsed = JSON.parse(key.permissions);
    if (Array.isArray(parsed)) permissions = parsed.filter((p): p is string => typeof p === 'string');
  } catch {
    permissions = [];
  }
  return {
    id: key.id,
    name: key.name,
    prefix: key.prefix,
    permissions,
    createdByPubkey: key.createdByPubkey,
    lastUsedAt: key.lastUsedAt,
    revokedAt: key.revokedAt,
    createdAt: key.createdAt,
  };
}

router.get(
  '/',
  requireAuth,
  requires('api_keys.manage'),
  async (_req: Request, res: Response) => {
    try {
      const keys = await prisma.apiKey.findMany({ orderBy: { createdAt: 'desc' } });
      res.json(keys.map(serialize));
    } catch (err) {
      console.error('List API keys error:', err);
      res.status(500).json({ error: 'Internal server error' });
    }
  }
);

router.post(
  '/',
  requireAuth,
  requires('api_keys.manage'),
  async (req: Request, res: Response) => {
    try {
      const { name, permissions } = req.body as { name?: string; permissions?: unknown };

      const trimmedName = typeof name === 'string' ? name.trim() : '';
      if (!trimmedName) {
        res.status(400).json({ error: 'name is required' });
        return;
      }
      if (trimmedName.length > 100) {
        res.status(400).json({ error: 'name must be 100 characters or fewer' });
        return;
      }

      if (!Array.isArray(permissions) || permissions.length === 0) {
        res.status(400).json({ error: 'At least one permission is required' });
        return;
      }

      const requested = [...new Set(permissions.filter((p): p is string => typeof p === 'string'))];
      const invalid = requested.filter((p) => !isValidPermissionKey(p));
      if (invalid.length > 0) {
        res.status(400).json({ error: `Unknown permissions: ${invalid.join(', ')}` });
        return;
      }

      // A key can never grant more than its creator holds. SuperAdmins hold all.
      const caller = req.access!;
      if (!caller.isSuperAdmin) {
        const exceeded = requested.filter((p) => !caller.permissions.has(p));
        if (exceeded.length > 0) {
          res
            .status(403)
            .json({ error: `You cannot grant permissions you do not hold: ${exceeded.join(', ')}` });
          return;
        }
      }

      const { rawKey, prefix, keyHash } = generateApiKey();
      const created = await prisma.apiKey.create({
        data: {
          name: trimmedName,
          prefix,
          keyHash,
          permissions: JSON.stringify(requested),
          createdByPubkey: req.user!.pubkey,
        },
      });

      // The raw key is returned exactly once and never stored in plaintext.
      res.status(201).json({ ...serialize(created), key: rawKey });
    } catch (err) {
      console.error('Create API key error:', err);
      res.status(500).json({ error: 'Internal server error' });
    }
  }
);

router.delete(
  '/:id',
  requireAuth,
  requires('api_keys.manage'),
  async (req: Request, res: Response) => {
    try {
      const idRaw = req.params.id;
      const id = typeof idRaw === 'string' ? idRaw : Array.isArray(idRaw) ? idRaw[0] : '';
      if (!id) {
        res.status(400).json({ error: 'id is required' });
        return;
      }

      const existing = await prisma.apiKey.findUnique({ where: { id } });
      if (!existing) {
        res.status(404).json({ error: 'API key not found' });
        return;
      }
      if (existing.revokedAt) {
        res.json(serialize(existing));
        return;
      }

      const revoked = await prisma.apiKey.update({
        where: { id },
        data: { revokedAt: new Date() },
      });
      res.json(serialize(revoked));
    } catch (err) {
      console.error('Revoke API key error:', err);
      res.status(500).json({ error: 'Internal server error' });
    }
  }
);

export default router;
