import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { verifyAccessToken } from '@/lib/jwt';
import { authLimiter, apiLimiter, strictLimiter } from '@/lib/ratelimit';

// ─── RUTAS PÚBLICAS ───────────────────────────────────────────────────────────
const PUBLIC_PATHS = [
  '/api/auth/login',
  '/api/auth/refresh',
  '/api/auth/forgot-password',
  '/api/health',
];

// Paths under PUBLIC_PATHS that still need the strict 5/min auth rate limit
// (as opposed to /api/health, which doesn't).
const PUBLIC_AUTH_PATHS = ['/api/auth/login', '/api/auth/forgot-password'];

// ─── RUTAS CON RATE LIMIT ESTRICTO ───────────────────────────────────────────
const STRICT_PATHS = [
  '/api/dispense',
  '/api/invoices',
  '/api/shifts',
  '/api/inventory/adjust',
];

// ─── PERMISOS RBAC POR PREFIJO ────────────────────────────────────────────────
const ROLE_PERMISSIONS: Record<string, string[]> = {
  '/api/users': ['ADMIN'],
  '/api/audit-logs': ['ADMIN'],
  '/api/reports': ['ADMIN', 'MANAGER'],
  '/api/inventory': ['ADMIN', 'MANAGER', 'BARTENDER'],
  '/api/dispense': ['ADMIN', 'MANAGER', 'BARTENDER'],
  '/api/invoices': ['ADMIN', 'MANAGER', 'CASHIER'],
  '/api/shifts': ['ADMIN', 'MANAGER', 'CASHIER'],
  '/api/warehouses': ['ADMIN', 'MANAGER'],
};

// Prefixes where reads (GET) stay open to any authenticated role (staff need
// to browse the menu/floor plan), but mutations require ADMIN/MANAGER.
const WRITE_ROLE_PERMISSIONS: Record<string, string[]> = {
  '/api/products': ['ADMIN', 'MANAGER'],
  '/api/categories': ['ADMIN', 'MANAGER'],
  '/api/tables': ['ADMIN', 'MANAGER'],
  '/api/zones': ['ADMIN', 'MANAGER'],
};

// ─── CORS: allowlist de orígenes (no wildcard) ────────────────────────────────
// ALLOWED_ORIGINS en .env, separado por comas (ej: "https://app.depakingbar.com").
// Sin esa variable, en NODE_ENV != production se acepta cualquier puerto de
// localhost/127.0.0.1 (Vite cambia de puerto según cuál esté libre).
function getAllowedOrigins(): string[] | null {
  const fromEnv = process.env.ALLOWED_ORIGINS;
  if (fromEnv) return fromEnv.split(',').map((o) => o.trim()).filter(Boolean);
  return null;
}

/** Devuelve el origin a reflejar en la respuesta, o null si no está permitido
 *  (o si el cliente no envía Origin, como una app móvil nativa — a la que CORS
 *  no le aplica). */
function resolveCorsOrigin(request: NextRequest): string | null {
  const origin = request.headers.get('origin');
  if (!origin) return null;

  const allowlist = getAllowedOrigins();
  if (allowlist) return allowlist.includes(origin) ? origin : null;

  if (process.env.NODE_ENV !== 'production') {
    try {
      const hostname = new URL(origin).hostname;
      if (hostname === 'localhost' || hostname === '127.0.0.1') return origin;
    } catch {
      return null;
    }
  }

  return null;
}

function corsHeaders(origin: string | null): Record<string, string> {
  const headers: Record<string, string> = {
    'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
    'Access-Control-Allow-Headers': 'Content-Type, Authorization, x-user-id, x-user-role, x-user-email, x-user-name',
    'Access-Control-Max-Age': '86400',
    Vary: 'Origin',
  };
  if (origin) headers['Access-Control-Allow-Origin'] = origin;
  return headers;
}

/** NextResponse.json helper que SIEMPRE adjunta las cabeceras CORS resueltas
 *  para este request — evita el bug de que solo el camino feliz las tuviera. */
function json(corsOrigin: string | null, body: unknown, init?: ResponseInit): NextResponse {
  const res = NextResponse.json(body, init);
  for (const [key, value] of Object.entries(corsHeaders(corsOrigin))) {
    res.headers.set(key, value);
  }
  return res;
}

export async function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl;

  // 1. Sólo actuar sobre rutas /api/
  if (!pathname.startsWith('/api/')) {
    return NextResponse.next();
  }

  const corsOrigin = resolveCorsOrigin(request);

  // 1b. Manejar preflight OPTIONS para CORS
  if (request.method === 'OPTIONS') {
    return new NextResponse(null, { status: 200, headers: corsHeaders(corsOrigin) });
  }

  // 2. Extraer IP para rate limiting
  const ip =
    request.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ||
    request.headers.get('x-real-ip') ||
    '127.0.0.1';

  // 3. Rutas públicas: solo aplicar rate limit de auth
  if (PUBLIC_PATHS.some((p) => pathname.startsWith(p))) {
    if (PUBLIC_AUTH_PATHS.some((p) => pathname.startsWith(p))) {
      const limit = authLimiter.check(ip);
      if (!limit.success) {
        return json(
          corsOrigin,
          { success: false, error: { code: 'TOO_MANY_REQUESTS', message: 'Demasiados intentos. Intenta en 1 minuto.' } },
          {
            status: 429,
            headers: {
              'Retry-After': '60',
              'X-RateLimit-Remaining': '0',
              'X-RateLimit-Reset': String(Math.ceil(limit.resetAt / 1000)),
            },
          }
        );
      }
    }
    const res = NextResponse.next();
    for (const [key, value] of Object.entries(corsHeaders(corsOrigin))) {
      res.headers.set(key, value);
    }
    return res;
  }

  // 4. Rate limiting general por usuario/IP
  const limiter = STRICT_PATHS.some((p) => pathname.startsWith(p)) ? strictLimiter : apiLimiter;
  const limitResult = limiter.check(ip);
  if (!limitResult.success) {
    return json(
      corsOrigin,
      { success: false, error: { code: 'TOO_MANY_REQUESTS', message: 'Límite de peticiones excedido.' } },
      { status: 429, headers: { 'Retry-After': '60' } }
    );
  }

  // 5. Extraer y verificar Bearer token
  const authHeader = request.headers.get('authorization');
  const token = authHeader?.startsWith('Bearer ') ? authHeader.substring(7) : null;

  if (!token) {
    return json(
      corsOrigin,
      { success: false, error: { code: 'UNAUTHORIZED', message: 'Token de autorización requerido' } },
      { status: 401 }
    );
  }

  const payload = await verifyAccessToken(token);
  if (!payload) {
    return json(
      corsOrigin,
      { success: false, error: { code: 'TOKEN_EXPIRED', message: 'Token inválido o expirado' } },
      { status: 401 }
    );
  }

  // 6. Verificar RBAC
  for (const [routePrefix, allowedRoles] of Object.entries(ROLE_PERMISSIONS)) {
    if (pathname.startsWith(routePrefix) && !allowedRoles.includes(payload.role)) {
      return json(
        corsOrigin,
        { success: false, error: { code: 'FORBIDDEN', message: 'No tienes permisos para este recurso' } },
        { status: 403 }
      );
    }
  }

  // 6b. RBAC solo para escritura (GET queda abierto a cualquier rol autenticado)
  if (request.method !== 'GET') {
    for (const [routePrefix, allowedRoles] of Object.entries(WRITE_ROLE_PERMISSIONS)) {
      if (pathname.startsWith(routePrefix) && !allowedRoles.includes(payload.role)) {
        return json(
          corsOrigin,
          { success: false, error: { code: 'FORBIDDEN', message: 'No tienes permisos para modificar este recurso' } },
          { status: 403 }
        );
      }
    }
  }

  // 7. Pasar contexto de usuario al Route Handler via headers
  const requestHeaders = new Headers(request.headers);
  requestHeaders.set('x-user-id', payload.userId);
  requestHeaders.set('x-user-role', payload.role);
  requestHeaders.set('x-user-email', payload.email);
  requestHeaders.set('x-user-name', payload.name);

  const response = NextResponse.next({ request: { headers: requestHeaders } });
  for (const [key, value] of Object.entries(corsHeaders(corsOrigin))) {
    response.headers.set(key, value);
  }
  return response;
}

export const config = {
  matcher: ['/api/:path*'],
};
