/**
 * User Impersonation routes.
 * Admin-only: start/stop impersonation, list sessions.
 */

import type { FastifyInstance } from 'fastify';
import {
  UnauthorizedError,
  ForbiddenError,
  NotFoundError,
  BadRequestError,
} from '../../lib/errors.js';
import { getRequestProductId } from '../../lib/request-context.js';
import { StartImpersonationSchema, StopImpersonationSchema } from './types.js';
import {
  startImpersonation,
  stopImpersonation,
  getActiveSession,
  listSessions,
} from './repository.js';

function requireAdmin(req: { jwtPayload?: { sub: string; role?: string } }): string {
  if (!req.jwtPayload?.sub) throw new UnauthorizedError('Authentication required');
  if (req.jwtPayload.role !== 'admin') throw new ForbiddenError('Admin access required');
  return req.jwtPayload.sub;
}

export async function impersonationRoutes(app: FastifyInstance): Promise<void> {
  // ── Start impersonation ───────────────────────────────────
  app.post('/impersonation/start', async (req, reply) => {
    const adminUserId = requireAdmin(req);
    const productId = getRequestProductId(req);
    const input = StartImpersonationSchema.parse(req.body);

    if (input.targetUserId === adminUserId) {
      throw new BadRequestError('Cannot impersonate yourself');
    }

    // Check for existing active session
    const existing = await getActiveSession(productId, adminUserId);
    if (existing) {
      throw new BadRequestError('Already impersonating a user. Stop current session first.');
    }

    const session = await startImpersonation(productId, adminUserId, input);
    req.log.info(
      { adminUserId, targetUserId: input.targetUserId, sessionId: session.id },
      'impersonation started'
    );
    reply.status(201);
    return session;
  });

  // ── Stop impersonation ────────────────────────────────────
  app.post('/impersonation/stop', async req => {
    const adminUserId = requireAdmin(req);
    const productId = getRequestProductId(req);
    const { sessionId } = StopImpersonationSchema.parse(req.body);
    const session = await stopImpersonation(sessionId, productId);
    if (!session) throw new NotFoundError('Session not found or already ended');
    req.log.info({ adminUserId, sessionId }, 'impersonation stopped');
    return session;
  });

  // ── Get my active session ─────────────────────────────────
  app.get('/impersonation/active', async req => {
    const adminUserId = requireAdmin(req);
    const productId = getRequestProductId(req);
    const session = await getActiveSession(productId, adminUserId);
    return { session };
  });

  // ── List all sessions (audit trail) ───────────────────────
  app.get('/impersonation/sessions', async req => {
    requireAdmin(req);
    const productId = getRequestProductId(req);
    return listSessions(productId);
  });
}
