/**
 * API Token REST endpoints.
 *
 * GET    /tokens          — list tokens (admin: all, user: own)
 * POST   /tokens          — create a new API token (admin only)
 * GET    /tokens/count    — count active tokens
 * PATCH  /tokens/:id      — revoke a token (admin only)
 * DELETE /tokens/:id      — delete a token (super_admin only)
 */

import type { FastifyInstance } from 'fastify';
import crypto from 'crypto';
import { BadRequestError, ForbiddenError, UnauthorizedError } from '../../lib/errors.js';
import { CreateTokenSchema, PatchTokenSchema, type ApiTokenDoc } from './types.js';
import * as repo from './repository.js';

function getRuntimeEnvironment(): 'dev' | 'staging' | 'prod' {
  const explicit = process.env.PLATFORM_RUNTIME_ENV;
  if (explicit === 'dev' || explicit === 'staging' || explicit === 'prod') {
    return explicit;
  }

  switch (process.env.NODE_ENV) {
    case 'production':
      return 'prod';
    case 'test':
      return 'dev';
    default:
      return 'dev';
  }
}

export async function tokenRoutes(app: FastifyInstance) {
  function requireAuth(req: import('fastify').FastifyRequest) {
    const payload = req.jwtPayload;
    if (!payload?.sub || !payload?.productId) {
      throw new UnauthorizedError('Authentication required');
    }
    return payload;
  }

  function requireAdmin(req: import('fastify').FastifyRequest) {
    const payload = requireAuth(req);
    if (!['super_admin', 'admin'].includes(payload.role ?? '')) {
      throw new ForbiddenError('Admin access required');
    }
    return payload;
  }

  // List tokens
  app.get('/tokens', async req => {
    const payload = requireAuth(req);
    const isAdmin = ['super_admin', 'admin'].includes(payload.role ?? '');
    if (isAdmin) {
      const tokens = await repo.list(payload.productId!);
      return { tokens };
    }
    const tokens = await repo.listByUser(payload.sub, payload.productId!);
    return { tokens };
  });

  // Create token (admin only)
  app.post('/tokens', async (req, reply) => {
    const payload = requireAdmin(req);
    const parsed = CreateTokenSchema.safeParse(req.body);
    if (!parsed.success) {
      throw new BadRequestError(parsed.error.issues.map(i => i.message).join('; '));
    }
    const { name, tokenType, environment, scopes, expiresInDays } = parsed.data;
    const resolvedEnvironment = environment ?? getRuntimeEnvironment();

    if (tokenType === 'user_api') {
      throw new BadRequestError('user_api tokens are not supported for machine credential routes');
    }

    if (tokenType === 'service_api' && payload.role !== 'super_admin') {
      throw new ForbiddenError('Super admin access required for service_api tokens');
    }

    const rawToken = `wai_${crypto.randomBytes(32).toString('hex')}`;
    const prefix = rawToken.slice(0, 12);
    const tokenHash = await repo.hashToken(rawToken);
    const now = new Date();
    const expiresAt = new Date(now.getTime() + expiresInDays * 86400000);

    const doc: ApiTokenDoc = {
      id: `tok_${crypto.randomUUID()}`,
      productId: payload.productId!,
      userId: payload.sub,
      userName: payload.email ?? '',
      name,
      tokenType,
      environment: resolvedEnvironment,
      prefix,
      tokenHash,
      status: 'active',
      scopes,
      createdAt: now.toISOString(),
      expiresAt: expiresAt.toISOString(),
      lastUsed: null,
      ttl: expiresInDays * 86400,
    };

    const token = await repo.create(doc);
    reply.code(201);
    return { ...token, rawToken };
  });

  // Count active tokens
  app.get('/tokens/count', async req => {
    const payload = requireAdmin(req);
    const count = await repo.countActive(payload.productId!);
    return { count };
  });

  // Revoke token (admin only)
  app.patch('/tokens/:id', async req => {
    const payload = requireAdmin(req);
    const { id } = req.params as { id: string };
    const parsed = PatchTokenSchema.safeParse(req.body);
    if (!parsed.success) {
      throw new BadRequestError(parsed.error.issues.map(i => i.message).join('; '));
    }

    if (parsed.data.action === 'revoke') {
      const success = await repo.revoke(id, payload.productId);
      if (!success) throw new BadRequestError('Token not found');
      return { success: true };
    }

    throw new BadRequestError('Unknown action');
  });

  // Delete token (super_admin only)
  app.delete('/tokens/:id', async req => {
    const payload = requireAuth(req);
    if (payload.role !== 'super_admin') {
      throw new ForbiddenError('Super admin access required');
    }
    const { id } = req.params as { id: string };
    const success = await repo.remove(id);
    if (!success) throw new BadRequestError('Token not found');
    return { success: true };
  });
}
