/**
 * Credential-specific rate limiting middleware
 */

import { FastifyInstance, FastifyRequest } from 'fastify';
import fastifyRateLimit from '@fastify/rate-limit';
import { getEnv } from './env';

export interface CredentialRateLimitConfig {
  perUser?: {
    max: number;
    timeWindow: string | number;
  };
  perIP?: {
    max: number;
    timeWindow: string | number;
  };
  perCredentialType?: {
    [credentialType: string]: {
      max: number;
      timeWindow: string | number;
    };
  };
  burstProtection?: {
    max: number;
    timeWindow: string | number;
  };
}

/**
 * Get user ID from request (from JWT or other auth)
 */
function getUserId(request: FastifyRequest): string | null {
  // Try to get from JWT payload
  const user = (request as any).user;
  if (user?.id || user?.sub) {
    return user.id || user.sub;
  }
  return null;
}

/**
 * Get credential type from request body
 */
function getCredentialType(request: FastifyRequest): string | null {
  const body = request.body as { credential_type?: string[]; credentialType?: string[] };
  if (body?.credential_type && body.credential_type.length > 0) {
    return body.credential_type[0]!;
  }
  if (body?.credentialType && body.credentialType.length > 0) {
    return body.credentialType[0]!;
  }
  return null;
}

/**
 * Register credential-specific rate limiting
 */
export async function registerCredentialRateLimit(
  server: FastifyInstance,
  config?: CredentialRateLimitConfig
): Promise<void> {
  const env = getEnv();

  // Default configuration
  const defaultConfig: Required<CredentialRateLimitConfig> = {
    perUser: {
      max: parseInt(env.CREDENTIAL_RATE_LIMIT_PER_USER || '10', 10),
      timeWindow: '1 hour',
    },
    perIP: {
      max: parseInt(env.CREDENTIAL_RATE_LIMIT_PER_IP || '50', 10),
      timeWindow: '1 hour',
    },
    perCredentialType: {
      'IdentityCredential': { max: 5, timeWindow: '1 hour' },
      'JudicialCredential': { max: 2, timeWindow: '24 hours' },
      'DiplomaticCredential': { max: 1, timeWindow: '24 hours' },
      'FinancialCredential': { max: 3, timeWindow: '1 hour' },
    },
    burstProtection: {
      max: 20,
      timeWindow: '1 minute',
    },
  };

  const finalConfig = { ...defaultConfig, ...config };

  // Global burst protection
  await server.register(fastifyRateLimit, {
    max: finalConfig.burstProtection.max,
    timeWindow: finalConfig.burstProtection.timeWindow,
    keyGenerator: (request: FastifyRequest) => {
      return `burst:${request.ip}`;
    },
    errorResponseBuilder: (_request, context) => {
      return {
        error: {
          code: 'RATE_LIMIT_EXCEEDED',
          message: `Burst rate limit exceeded, retry in ${Math.ceil(context.ttl / 1000)} seconds`,
        },
      };
    },
    skipOnError: false,
  });

  // Per-user rate limiting
  await server.register(fastifyRateLimit, {
    max: finalConfig.perUser.max,
    timeWindow: finalConfig.perUser.timeWindow,
    keyGenerator: (request: FastifyRequest) => {
      const userId = getUserId(request);
      if (!userId) {
        return `user:anonymous:${request.ip}`;
      }
      return `user:${userId}`;
    },
    errorResponseBuilder: (_request, context) => {
      return {
        error: {
          code: 'RATE_LIMIT_EXCEEDED',
          message: `User rate limit exceeded, retry in ${Math.ceil(context.ttl / 1000)} seconds`,
        },
      };
    },
    skipOnError: false,
  });

  // Per-IP rate limiting
  await server.register(fastifyRateLimit, {
    max: finalConfig.perIP.max,
    timeWindow: finalConfig.perIP.timeWindow,
    keyGenerator: (request: FastifyRequest) => {
      return `ip:${request.ip}`;
    },
    errorResponseBuilder: (_request, context) => {
      return {
        error: {
          code: 'RATE_LIMIT_EXCEEDED',
          message: `IP rate limit exceeded, retry in ${Math.ceil(context.ttl / 1000)} seconds`,
        },
      };
    },
    skipOnError: false,
  });

  // Per-credential-type rate limiting (dynamic)
  for (const [credentialType, limitConfig] of Object.entries(finalConfig.perCredentialType)) {
    await server.register(fastifyRateLimit, {
      max: limitConfig.max,
      timeWindow: limitConfig.timeWindow,
      keyGenerator: (request: FastifyRequest) => {
        const userId = getUserId(request);
        const type = getCredentialType(request) || credentialType;
        if (userId) {
          return `credential-type:${type}:user:${userId}`;
        }
        return `credential-type:${type}:ip:${request.ip}`;
      },
      errorResponseBuilder: (_request, context) => {
        return {
          error: {
            code: 'RATE_LIMIT_EXCEEDED',
            message: `Credential type rate limit exceeded, retry in ${Math.ceil(context.ttl / 1000)} seconds`,
          },
        };
      },
      skipOnError: false,
    });
  }
}

/**
 * Create a rate limit plugin for specific credential endpoints
 */
export function createCredentialRateLimitPlugin(config?: CredentialRateLimitConfig) {
  return async function (server: FastifyInstance) {
    await registerCredentialRateLimit(server, config);
  };
}

