/**
 * Middleware Next.js pour la protection des routes avec Keycloak
 * Gère l'authentification et l'autorisation au niveau des routes
 */

import { NextRequest, NextResponse } from 'next/server';
import { jwtVerify, JWTPayload } from 'jose';

// Configuration des routes protégées
const PROTECTED_ROUTES = [
  '/dashboard',
  '/chantiers',
  '/clients',
  '/devis',
  '/factures',
  '/materiels',
  '/employes',
  '/equipes',
  '/planning',
  '/reports',
  '/admin',
  '/profile',
] as const;

// Configuration des routes publiques (toujours accessibles)
const PUBLIC_ROUTES = [
  '/',
  '/api/health',
  '/api/auth/login',
  '/api/auth/logout',
  '/api/auth/token',
] as const;

// Configuration des routes par rôle
const ROLE_BASED_ROUTES = {
  '/admin': ['super_admin', 'admin'],
  '/reports': ['super_admin', 'admin', 'directeur', 'manager'],
  '/employes': ['super_admin', 'admin', 'directeur', 'manager', 'chef_chantier'],
  '/equipes': ['super_admin', 'admin', 'directeur', 'manager', 'chef_chantier'],
  '/materiels/manage': ['super_admin', 'admin', 'logisticien'],
  '/clients/manage': ['super_admin', 'admin', 'commercial'],
  '/devis/manage': ['super_admin', 'admin', 'commercial'],
  '/factures/manage': ['super_admin', 'admin', 'comptable'],
} as const;

// Interface pour le token JWT décodé
interface KeycloakToken extends JWTPayload {
  preferred_username?: string;
  email?: string;
  given_name?: string;
  family_name?: string;
  realm_access?: {
    roles: string[];
  };
  resource_access?: {
    [key: string]: {
      roles: string[];
    };
  };
}

// Fonction pour vérifier si une route est protégée
function isProtectedRoute(pathname: string): boolean {
  return PROTECTED_ROUTES.some(route => pathname.startsWith(route));
}

// Fonction pour vérifier si une route est publique
function isPublicRoute(pathname: string): boolean {
  return PUBLIC_ROUTES.some(route => pathname === route || pathname.startsWith(route));
}

// Fonction pour extraire les rôles du token
function extractRoles(token: KeycloakToken): string[] {
  const realmRoles = token.realm_access?.roles || [];
  const clientRoles = token.resource_access?.['btpxpress-frontend']?.roles || [];
  return [...realmRoles, ...clientRoles];
}

// Fonction pour vérifier les permissions de rôle
function hasRequiredRole(userRoles: string[], requiredRoles: string[]): boolean {
  return requiredRoles.some(role => userRoles.includes(role));
}

// Fonction pour vérifier et décoder le token JWT
async function verifyToken(token: string): Promise<KeycloakToken | null> {
  try {
    // Configuration de la clé publique Keycloak
    const keycloakUrl = process.env.NEXT_PUBLIC_KEYCLOAK_URL || 'https://security.lions.dev';
    const realm = process.env.NEXT_PUBLIC_KEYCLOAK_REALM || 'btpxpress';
    
    // Récupérer la clé publique depuis Keycloak
    const jwksUrl = `${keycloakUrl}/realms/${realm}/protocol/openid_connect/certs`;
    
    // Pour la vérification côté serveur, nous utilisons une approche simplifiée
    // En production, il faudrait implémenter une vérification complète avec JWKS
    
    // Décoder le token sans vérification pour le middleware (vérification côté client)
    const payload = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString());
    
    // Vérifier l'expiration
    if (payload.exp && payload.exp < Date.now() / 1000) {
      return null;
    }
    
    return payload as KeycloakToken;
  } catch (error) {
    console.error('Erreur lors de la vérification du token:', error);
    return null;
  }
}

// Fonction principale du middleware
export async function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl;
  
  // Ignorer les fichiers statiques et les API routes internes
  if (
    pathname.startsWith('/_next') ||
    pathname.startsWith('/api') ||
    pathname.includes('.') ||
    pathname.startsWith('/favicon')
  ) {
    return NextResponse.next();
  }

  // Ignorer les requêtes vers /dashboard qui contiennent un code d'autorisation
  // Cela permet à la page de traiter le code sans que le middleware ne redirige
  if (pathname === '/dashboard' && request.nextUrl.searchParams.has('code')) {
    console.log('🔓 Middleware: Autorisant /dashboard avec code d\'autorisation');
    return NextResponse.next();
  }

  // Permettre l'accès aux routes publiques
  if (isPublicRoute(pathname)) {
    return NextResponse.next();
  }

  // Vérifier si la route nécessite une authentification
  if (isProtectedRoute(pathname)) {
    // Récupérer le token depuis les cookies ou headers
    const authHeader = request.headers.get('authorization');
    const tokenFromCookie = request.cookies.get('keycloak-token')?.value;
    const pkceVerifier = request.cookies.get('pkce_code_verifier')?.value;

    console.log(`🔍 Middleware: Vérification de ${pathname}:`, {
      hasAuthHeader: !!authHeader,
      hasTokenCookie: !!tokenFromCookie,
      hasPkceVerifier: !!pkceVerifier,
      hasCode: request.nextUrl.searchParams.has('code')
    });

    let token: string | null = null;

    if (authHeader && authHeader.startsWith('Bearer ')) {
      token = authHeader.substring(7);
    } else if (tokenFromCookie) {
      token = tokenFromCookie;
    }

    // Si pas de token, vérifier si un processus d'authentification est en cours
    if (!token) {
      // Si on a un code verifier PKCE, cela signifie qu'un processus d'authentification est en cours
      // Autoriser l'accès pour permettre à la page de terminer l'échange du code
      if (pkceVerifier && pathname === '/dashboard') {
        console.log('🔓 Middleware: Autorisant /dashboard avec PKCE verifier (authentification en cours)');
        return NextResponse.next();
      }

      console.log(`🔒 Middleware: Redirection vers /api/auth/login pour ${pathname} (pas de token ni de PKCE verifier)`);
      const loginUrl = new URL('/api/auth/login', request.url);
      loginUrl.searchParams.set('redirect', pathname);
      return NextResponse.redirect(loginUrl);
    }

    // Vérifier et décoder le token
    const decodedToken = await verifyToken(token);
    
    if (!decodedToken) {
      // Token invalide ou expiré
      const loginUrl = new URL('/api/auth/login', request.url);
      loginUrl.searchParams.set('redirect', pathname);
      return NextResponse.redirect(loginUrl);
    }

    // Extraire les rôles de l'utilisateur
    const userRoles = extractRoles(decodedToken);

    // Vérifier les permissions basées sur les rôles pour des routes spécifiques
    for (const [route, requiredRoles] of Object.entries(ROLE_BASED_ROUTES)) {
      if (pathname.startsWith(route)) {
        if (!hasRequiredRole(userRoles, [...requiredRoles])) {
          // Utilisateur n'a pas les rôles requis
          return NextResponse.redirect(new URL('/api/auth/login', request.url));
        }
        break;
      }
    }

    // Ajouter les informations utilisateur aux headers pour les composants
    const response = NextResponse.next();
    response.headers.set('x-user-id', decodedToken.sub || '');
    response.headers.set('x-user-email', decodedToken.email || '');
    response.headers.set('x-user-roles', JSON.stringify(userRoles));
    
    return response;
  }

  // Par défaut, permettre l'accès
  return NextResponse.next();
}

// Configuration du matcher pour spécifier quelles routes le middleware doit traiter
export const config = {
  matcher: [
    /*
     * Match all request paths except for the ones starting with:
     * - api (API routes)
     * - _next/static (static files)
     * - _next/image (image optimization files)
     * - favicon.ico (favicon file)
     * - public files (images, etc.)
     */
    '/((?!api|_next/static|_next/image|favicon.ico|.*\\..*|public).*)',
  ],
};
