/**
 * Content Security Policy (CSP) utilities
 * Gère les nonces et la configuration CSP pour la sécurité
 */

// Nonce généré côté serveur pour les scripts inline
let cspNonce: string | null = null;

/**
 * Définit le nonce CSP pour la session courante
 */
export function setCSPNonce(nonce: string): void {
  cspNonce = nonce;
}

/**
 * Récupère le nonce CSP actuel
 */
export function getCSPNonce(): string | null {
  return cspNonce;
}

/**
 * Génère un nonce CSP sécurisé
 */
export function generateCSPNonce(): string {
  const array = new Uint8Array(16);
  crypto.getRandomValues(array);
  return Array.from(array, (byte) => byte.toString(16).padStart(2, '0')).join(
    '',
  );
}

/**
 * Configuration CSP pour la production
 * SECURITY: Cette configuration utilise des nonces stricts, pas d'unsafe-inline ni unsafe-eval
 * Pour Tailwind CSS, on peut utiliser des nonces ou 'unsafe-inline' uniquement pour style-src
 * (moins critique que script-src car les styles ne peuvent pas exécuter de code)
 */
export const CSP_POLICY = {
  'default-src': ["'self'"],
  'script-src': [
    "'self'",
    "'nonce-__CSP_NONCE__'", // Nonce pour scripts inline - PAS d'unsafe-inline ni unsafe-eval
    'https://cdn.jsdelivr.net', // Pour les CDN si nécessaire
  ],
  'style-src': [
    "'self'",
    "'nonce-__CSP_NONCE__'", // Nonce pour styles inline (préféré)
    "'unsafe-inline'", // Fallback pour Tailwind CSS - acceptable car style-src ne peut pas exécuter de code
    'https://fonts.googleapis.com',
  ],
  'img-src': ["'self'", 'data:', 'https:', 'blob:'],
  'connect-src': ["'self'", 'ws:', 'wss:', 'http:', 'https:'],
  'font-src': ["'self'", 'data:', 'https://fonts.gstatic.com'],
  'frame-src': ["'self'", 'http://localhost:8080', 'https://localhost:8080'],
  'object-src': ["'none'"],
  'base-uri': ["'self'"],
  'form-action': ["'self'"],
  'frame-ancestors': ["'none'"],
  'upgrade-insecure-requests': [],
} as const;

/**
 * Construit la chaîne CSP à partir de la configuration
 */
export function buildCSPHeader(nonce?: string): string {
  const policy: Record<string, string[]> = {
    ...CSP_POLICY,
  } as unknown as Record<string, string[]>;

  if (nonce) {
    policy['script-src'] = policy['script-src'].map((src) =>
      src === "'nonce-__CSP_NONCE__'" ? `'nonce-${nonce}'` : src,
    );
  }

  return Object.entries(policy)
    .map(([directive, sources]) => {
      if (sources.length === 0) {
        return directive;
      }
      return `${directive} ${sources.join(' ')}`;
    })
    .join('; ');
}

/**
 * Valide qu'un script peut être exécuté selon la CSP
 */
export function validateScriptExecution(scriptContent: string): boolean {
  // Vérifications de base pour les scripts inline
  const dangerousPatterns = [
    /eval\s*\(/,
    /Function\s*\(/,
    /setTimeout\s*\(\s*["']/,
    /setInterval\s*\(\s*["']/,
    /document\.write/,
    /innerHTML\s*=/,
    /outerHTML\s*=/,
  ];

  return !dangerousPatterns.some((pattern) => pattern.test(scriptContent));
}

/**
 * Sanitise le contenu HTML pour éviter les violations CSP
 */
export function sanitizeForCSP(content: string): string {
  return content
    .replace(/javascript:/gi, '')
    .replace(/on\w+\s*=/gi, '') // Supprimer les event handlers inline
    .replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '') // Supprimer les scripts
    .replace(/<iframe[^>]*>[\s\S]*?<\/iframe>/gi, ''); // Supprimer les iframes
}

/**
 * Configuration CSP pour le développement (plus permissive)
 * SECURITY NOTE: unsafe-eval est nécessaire pour Vite HMR en développement uniquement
 * Cette configuration NE DOIT JAMAIS être utilisée en production
 */
export const CSP_POLICY_DEV = {
  'default-src': ["'self'"],
  'script-src': [
    "'self'",
    "'unsafe-inline'", // Nécessaire pour Vite HMR
    "'unsafe-eval'", // Nécessaire pour Vite HMR en dev uniquement - NE PAS utiliser en production
  ],
  'style-src': ["'self'", "'unsafe-inline'"], // Nécessaire pour Tailwind CSS et Vite HMR
  'img-src': ["'self'", 'data:', 'https:', 'blob:'],
  'connect-src': ["'self'", 'ws:', 'wss:', 'http:', 'https:'],
  'font-src': ["'self'", 'data:', 'https:'],
  'frame-src': ["'self'", 'http://localhost:8080', 'https://localhost:8080'],
  'object-src': ["'none'"],
  'base-uri': ["'self'"],
  'form-action': ["'self'"],
  'frame-ancestors': ["'none'"],
};

/**
 * Construit la CSP pour le développement
 * SECURITY: Cette fonction ne doit être utilisée qu'en mode développement
 * En production, utiliser buildCSPHeader() avec des nonces stricts
 */
export function buildCSPHeaderDev(): string {
  // Vérifier qu'on est bien en mode développement
  if (import.meta.env.MODE === 'production') {
    console.error('[CSP] SECURITY WARNING: buildCSPHeaderDev() called in production mode! Using strict CSP instead.');
    return buildCSPHeader();
  }
  
  return Object.entries(CSP_POLICY_DEV)
    .map(([directive, sources]) => {
      if (sources.length === 0) {
        return directive;
      }
      return `${directive} ${sources.join(' ')}`;
    })
    .join('; ');
}

/**
 * Hook pour utiliser le nonce CSP dans les composants React
 */
export function useCSPNonce(): string | null {
  return getCSPNonce();
}

/**
 * Middleware pour injecter le nonce CSP dans les réponses
 * NOTE: Ce middleware est destiné à être utilisé côté serveur (Node.js/Express)
 * Si utilisé côté client, il ne sera pas exécuté
 */
export function createCSPMiddleware() {
  // Type pour les paramètres du middleware Express
  // NOTE: Ces types ne sont pas disponibles côté client, donc on utilise des types génériques
  // Si ce middleware est utilisé côté serveur, installer @types/express pour les types corrects
  return (
    _req: { headers?: Record<string, string | string[] | undefined> },
    res: {
      setHeader: (name: string, value: string) => void;
    },
    next: () => void,
  ) => {
    const nonce = generateCSPNonce();
    setCSPNonce(nonce);

    const cspHeader =
      import.meta.env.MODE === 'production'
        ? buildCSPHeader(nonce)
        : buildCSPHeaderDev();

    res.setHeader('Content-Security-Policy', cspHeader);
    res.setHeader('X-Content-Type-Options', 'nosniff');
    res.setHeader('X-Frame-Options', 'DENY');
    res.setHeader('X-XSS-Protection', '1; mode=block');
    res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');

    next();
  };
}
