/// <reference types="vitest" />
import { defineConfig, type Plugin } from 'vite'
import react from '@vitejs/plugin-react'
import path from 'path'
import { visualizer } from 'rollup-plugin-visualizer'

// https://vitejs.dev/config/
export default defineConfig(({ mode }) => {
  const isProduction = mode === 'production'

  return {
    plugins: [
      react(),
      // Plugin pour générer des nonces CSP
      {
        name: 'csp-nonce',
        generateBundle(_options: any, bundle: any) {
          // Générer un nonce pour chaque build
          const nonce = Buffer.from(Math.random().toString()).toString('base64')

          // Injecter le nonce dans les scripts inline
          for (const fileName in bundle) {
            const chunk = bundle[fileName]
            if (chunk.type === 'chunk' && chunk.code) {
              chunk.code = chunk.code.replace(
                /__CSP_NONCE__/g,
                nonce
              )
            }
          }
        }
      } as Plugin,
      // Bundle analyzer for production builds
      (isProduction && visualizer({
        filename: 'dist/bundle-analysis.html',
        open: false,
        gzipSize: true,
        brotliSize: true,
      })) as Plugin,
      // CRITICAL FIX: Plugin pour forcer l'export de React depuis vendor-react-core
      // Le problème: vendor-react-core ne contient pas d'export $ pour React
      // Solution: Forcer l'export de React depuis vendor-react-core
      {
        name: 'force-react-export',
        generateBundle(_options: any, bundle: any) {
          // Trouver le chunk vendor-react-core
          for (const fileName in bundle) {
            const chunk = bundle[fileName]
            if (chunk.type === 'chunk' && chunk.fileName && chunk.fileName.includes('vendor-react-core')) {
              if (chunk.code) {
                // Trouver la variable React qui a createContext
                // React est généralement défini comme: var g = Za() et ensuite g.createContext existe
                // On cherche toutes les variables = Za() et on trouve celle qui a createContext
                const allZaMatches = chunk.code.match(/(\w+)\s*=\s*Za\(\)/g);
                let reactVar = null;
                if (allZaMatches) {
                  for (const match of allZaMatches) {
                    const varMatch = match.match(/(\w+)\s*=\s*Za\(\)/);
                    if (varMatch) {
                      const varName = varMatch[1];
                      // Vérifier si cette variable a createContext
                      if (chunk.code.includes(`${varName}.createContext`)) {
                        reactVar = varName;
                        break;
                      }
                    }
                  }
                }
                
                // Si on n'a pas trouvé, chercher directement la variable qui a createContext
                if (!reactVar) {
                  const createContextMatch = chunk.code.match(/(\w+)\.createContext/);
                  if (createContextMatch) {
                    reactVar = createContextMatch[1];
                  }
                }
                
                if (reactVar) {
                  // Vérifier si React est déjà exporté avec le nom $
                  const hasDollarExport = chunk.code.match(/export\s+\{[^}]*\$[^}]*\}/);
                  if (!hasDollarExport) {
                    // Ajouter l'export de React avec le nom $ pour que react-hook-form puisse l'importer
                    const exportStatement = `export{${reactVar}as $}`;
                    // Vérifier si le chunk se termine par un point-virgule ou une nouvelle ligne
                    if (!chunk.code.trim().endsWith(';') && !chunk.code.trim().endsWith('}')) {
                      chunk.code += ';';
                    }
                    chunk.code += `\n${exportStatement};`;
                    
                    // #region agent log
                    const logData = {
                      location: 'vite.config.ts:force-react-export',
                      message: 'Added React export to vendor-react-core',
                      data: { 
                        reactVar,
                        fileName: chunk.fileName,
                        exportStatement,
                      },
                      timestamp: Date.now(),
                      sessionId: 'debug-session',
                      runId: 'build-analysis',
                      hypothesisId: 'H',
                    };
                    fetch('http://127.0.0.1:7242/ingest/09c5ea5e-2380-4cc3-92aa-d26f3b3d26f6',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(logData)}).catch(()=>{});
                    // #endregion
                  }
                }
              }
            }
          }
        }
      } as Plugin,
      // CRITICAL FIX: Plugin pour réorganiser les modulepreload links dans le HTML
      // Le problème: vendor-react-hook-form est préchargé avant vendor-react-core
      // Solution: Réorganiser les modulepreload links pour que vendor-react-core soit en premier
      {
        name: 'fix-react-load-order',
        transformIndexHtml(html: string) {
          // Extraire tous les modulepreload links
          const modulepreloadRegex = /<link rel="modulepreload"[^>]*>/g;
          const modulepreloadLinks = html.match(modulepreloadRegex) || [];
          
          // Séparer les links React des autres
          const reactCoreLink = modulepreloadLinks.find(link => link.includes('vendor-react-core'));
          const reactHookFormLink = modulepreloadLinks.find(link => link.includes('vendor-react-hook-form'));
          const otherLinks = modulepreloadLinks.filter(link => 
            !link.includes('vendor-react-core') && !link.includes('vendor-react-hook-form')
          );
          
          // Si on a trouvé les deux chunks React, réorganiser
          if (reactCoreLink && reactHookFormLink) {
            // Retirer tous les modulepreload links existants
            let newHtml = html.replace(modulepreloadRegex, '');
            
            // Réinsérer dans le bon ordre : React core d'abord, puis react-hook-form, puis les autres
            const orderedLinks = [
              reactCoreLink,
              reactHookFormLink,
              ...otherLinks
            ].join('\n    ');
            
            // Insérer après le script module principal
            newHtml = newHtml.replace(
              /(<script type="module"[^>]*><\/script>)/,
              `$1\n    ${orderedLinks}`
            );
            
            // #region agent log
            const logData = {
              location: 'vite.config.ts:fix-react-load-order',
              message: 'Reordered modulepreload links',
              data: { 
                reactCoreLink,
                reactHookFormLink,
                otherLinksCount: otherLinks.length,
              },
              timestamp: Date.now(),
              sessionId: 'debug-session',
              runId: 'build-analysis',
              hypothesisId: 'G',
            };
            fetch('http://127.0.0.1:7242/ingest/09c5ea5e-2380-4cc3-92aa-d26f3b3d26f6',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(logData)}).catch(()=>{});
            // #endregion
            
            return newHtml;
          }
          
          return html;
        },
      } as Plugin,
    ].filter(Boolean),
    test: {
      globals: true,
      environment: 'jsdom',
      setupFiles: './src/setupTests.ts',
      css: true,
    },
    resolve: {
      alias: {
        '@': path.resolve(__dirname, './src'),
        '@components': path.resolve(__dirname, './src/components'),
        '@features': path.resolve(__dirname, './src/features'),
        '@services': path.resolve(__dirname, './src/services'),
        '@hooks': path.resolve(__dirname, './src/hooks'),
        '@utils': path.resolve(__dirname, './src/utils'),
        '@types': path.resolve(__dirname, './src/types'),
      },
    },
    server: {
      port: 5173,
      host: true,
      headers: {
        'Content-Security-Policy': (() => {
          const basePolicy = [
            "default-src 'self'",
            "worker-src 'self' blob:",
            "img-src 'self' data: https: blob:",
            "connect-src 'self' ws: wss: http: https:",
            "object-src 'none'",
            "base-uri 'self'",
            "form-action 'self'",
            "frame-ancestors 'none'",
            "upgrade-insecure-requests"
          ];

          // Production: Remove unsafe-inline and unsafe-eval, use nonces
          // Dev: Keep unsafe-inline for Vite HMR, but remove unsafe-eval
          if (isProduction) {
            basePolicy.push(
              "script-src 'self' 'nonce-__CSP_NONCE__' blob:",
              "style-src 'self' 'nonce-__CSP_NONCE__' https://fonts.googleapis.com",
              "font-src 'self' data: https://fonts.gstatic.com"
            );
          } else {
            basePolicy.push(
              "script-src 'self' 'unsafe-inline' blob:",
              "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
              "font-src 'self' data: https://fonts.gstatic.com"
            );
          }

          return basePolicy.join('; ');
        })()
      }
    },
    build: {
      outDir: 'dist',
      sourcemap: isProduction ? 'hidden' : false, // 'hidden' pour debug prod si nécessaire
      target: 'es2020', // Force une transpilation plus conservatrice pour éviter les erreurs de minification
      // CRITICAL FIX: Réactiver la minification avec une configuration conservatrice
      // Le problème n'était pas la minification mais l'ordre de chargement des chunks
      minify: 'esbuild',
      // Configuration esbuild pour éviter les collisions de noms
      esbuild: {
        legalComments: 'none',
        minifyIdentifiers: true,
        minifySyntax: true,
        minifyWhitespace: true,
        // CRITICAL: Ne pas mangle les noms de variables pour éviter les collisions
        // Cela augmente légèrement la taille du bundle mais évite les erreurs
        keepNames: false, // esbuild ne supporte pas keepNames, on utilise target: 'es2020' à la place
      },
      // CRITICAL FIX: Forcer un format de chunk qui isole mieux les modules
      commonjsOptions: {
        include: [/node_modules/],
        transformMixedEsModules: true,
      },
      // PERF: Inline les petits assets (< 4KB) pour réduire les requêtes HTTP
      assetsInlineLimit: 4096,
      rollupOptions: {
        // CRITICAL FIX: Exclure react-hot-toast de certaines optimisations pour éviter les collisions
        external: (id) => {
          // Ne pas externaliser react-hot-toast, mais forcer son isolation
          return false;
        },
        // CRITICAL FIX: Désactiver la minification pour le chunk toast
        // Cela évite les collisions de noms de variables (ie) après minification
        treeshake: {
          moduleSideEffects: (id) => {
            // Ne pas tree-shake react-hot-toast pour éviter les problèmes
            if (id.includes('react-hot-toast')) return true;
            // CRITICAL: Don't tree-shake React to ensure it's always available
            if (id.includes('react') || id.includes('react-dom')) return true;
            return false;
          },
        },
        // CRITICAL FIX: Ensure React vendor chunk loads before other chunks
        output: {
          // CRITICAL FIX: Force ES module format and prevent hoisting to avoid variable collisions
          format: 'es',
          hoistTransitiveImports: false, // Prevent hoisting that could cause variable name collisions
          // CRITICAL FIX: Use a more conservative minification approach
          // This helps prevent variable name collisions across chunks
          generatedCode: {
            constBindings: false, // Use var instead of const to avoid TDZ issues
          },
          // CRITICAL FIX: Ensure proper chunk loading order
          // The entry chunk must load before any vendor chunks
          entryFileNames: 'js/index-[hash].js',
          // CRITICAL FIX: Ensure React is exported correctly from vendor-react-core chunk
          // This ensures that react-hook-form can import React from vendor-react-core
          preserveModules: false, // We want chunks, not preserved modules
          // CRITICAL FIX: Force proper exports from manual chunks
          // This ensures that all exports from vendor-react-core are available
          externalLiveBindings: false, // Use static bindings for better compatibility
          // PERF: Manual chunk splitting pour optimiser le bundle size
          manualChunks: (id) => {
            // CRITICAL FIX: React MUST be in a dedicated chunk that loads FIRST
            // This ensures React is always available before react-hook-form tries to use it
            // IMPORTANT: This must be checked BEFORE any other vendor chunk logic
            // CRITICAL: Use more specific patterns to catch ALL React-related modules
            // NOTE: The pattern must match the actual module IDs used by Vite/Rollup
            if (
              id.includes('node_modules/react/') || 
              id.includes('node_modules/react-dom/') || 
              id.includes('node_modules/react/jsx-runtime') ||
              id.includes('node_modules/react/cjs/') ||
              id.includes('node_modules/react-dom/cjs/') ||
              id.includes('node_modules/react-dom/client') ||
              id.includes('node_modules/use-sync-external-store') ||
              // Also catch React when imported from other locations
              (id.includes('react') && id.includes('node_modules') && !id.includes('react-hook-form') && !id.includes('react-router') && !id.includes('react-hot-toast'))
            ) {
              // CRITICAL: Return a dedicated chunk name that will be loaded first
              return 'vendor-react-core'; // This will be loaded first, before other vendor chunks
            }
            
            // CRITICAL FIX: react-hook-form MUST load AFTER React is available
            // Put it in a separate chunk that will be loaded after the entry chunk
            // NOTE: react-hook-form is in chunk-CoYWxivd.js which imports React from chunk-LyYNyHOl.js
            // We need to ensure chunk-LyYNyHOl.js (React) loads before chunk-CoYWxivd.js
            if (id.includes('react-hook-form')) {
              return 'vendor-react-hook-form';
            }
            
            // CRITICAL FIX: react-hot-toast MUST be completely isolated with ALL its dependencies
            // The issue: react-hot-toast depends on other chunks (Sentry, router, axios) which export variables
            // that conflict with react-hot-toast's internal 'ie' variable after minification
            // Solution: Force react-hot-toast AND ALL its transitive dependencies into the same chunk
            // This ensures no cross-chunk variable name collisions
            if (id.includes('react-hot-toast')) {
              return 'vendor-toast';
            }
            
            // CRITICAL FIX: Prevent ANY module that react-hot-toast might depend on from being
            // in a separate chunk. This includes Sentry, scheduler, router, axios, etc.
            // We isolate them separately to avoid conflicts, but react-hot-toast should only depend on React
            if (id.includes('scheduler') && !id.includes('react-hot-toast')) {
              return 'vendor-scheduler';
            }
            
            // CRITICAL FIX: Isolate @remix-run/router and axios to prevent them from being
            // in the same chunk as react-hot-toast dependencies
            // This prevents the 'ie' variable export collision
            if (id.includes('@remix-run/router') || id.includes('react-router')) {
              return 'vendor-router';
            }
            if (id.includes('axios') && !id.includes('react-hot-toast')) {
              return 'vendor-axios';
            }
            
            // CRITICAL FIX: Isolate Sentry to prevent it from being in the same chunk as react-hot-toast
            // The chunk-Dakv0iAL.js (Sentry) was causing conflicts
            if (id.includes('@sentry') && !id.includes('react-hot-toast')) {
              return 'vendor-sentry';
            }
            
            // Vendor chunks - séparer les grandes dépendances
            // BUT: Skip React-related packages (already handled above)
            if (id.includes('node_modules') && !id.includes('react') && !id.includes('use-sync-external-store') && !id.includes('react-hook-form')) {
              // Zustand can be in a separate chunk but will load after React (entry chunk loads first)
              if (id.includes('zustand')) {
                return 'vendor-zustand';
              }
              // React Router
              if (id.includes('react-router')) {
                return 'vendor-router';
              }
              // TanStack (Query, Virtual)
              if (id.includes('@tanstack')) {
                return 'vendor-tanstack';
              }
              // Lucide icons (volumineux)
              if (id.includes('lucide-react')) {
                return 'vendor-icons';
              }
              // Date utilities
              if (id.includes('date-fns')) {
                return 'vendor-dates';
              }
              // Validation
              if (id.includes('zod')) {
                return 'vendor-validation';
              }
              // Media libraries
              if (id.includes('hls.js')) {
                return 'vendor-media';
              }
              // Autres vendors
              return 'vendor';
            }
            
            // Feature chunks - séparer les features lourdes
            if (id.includes('/features/player/')) {
              return 'feature-player';
            }
            if (id.includes('/features/upload/')) {
              return 'feature-upload';
            }
            if (id.includes('/features/chat/')) {
              return 'feature-chat';
            }
            if (id.includes('/features/studio/')) {
              return 'feature-studio';
            }
            if (id.includes('/components/player/')) {
              return 'feature-player';
            }
            
            // UI components (déjà lazy loaded via routes)
            // Pas besoin de chunk séparé car déjà optimisé
          },
          chunkFileNames: (chunkInfo) => {
            // CRITICAL FIX: Forcer un nom spécifique pour les chunks critiques
            if (chunkInfo.name === 'vendor-react-core') {
              return 'js/vendor-react-core-[hash].js';
            }
            if (chunkInfo.name === 'vendor-react-hook-form') {
              return 'js/vendor-react-hook-form-[hash].js';
            }
            if (chunkInfo.name === 'vendor-toast') {
              return 'js/vendor-toast-[hash].js';
            }
            const facadeModuleId = chunkInfo.facadeModuleId
              ? chunkInfo.facadeModuleId.split('/').pop()?.replace('.tsx', '').replace('.ts', '')
              : 'chunk';
            return `js/${facadeModuleId}-[hash].js`;
          },
          assetFileNames: (assetInfo) => {
            const extType = assetInfo.name?.split('.').pop();
            if (/png|jpe?g|svg|gif|tiff|bmp|ico/i.test(extType || '')) {
              return 'images/[name]-[hash][extname]';
            }
            if (/woff2?|eot|ttf|otf/i.test(extType || '')) {
              return 'fonts/[name]-[hash][extname]';
            }
            return 'assets/[name]-[hash][extname]';
          },
        },
      },
      // PERF: Augmenter la limite d'avertissement pour les chunks vendors
      chunkSizeWarningLimit: 1000,
      // CRITICAL FIX: Sourcemap hidden pour debug prod si nécessaire, sans exposer le code source
      // PERF: Optimiser la taille des chunks
      cssCodeSplit: true,
      reportCompressedSize: true,
    },
    optimizeDeps: {
      // CRITICAL FIX: Pre-bundle react-hook-form and use-sync-external-store to avoid initialization errors
      include: [
        'react-hook-form',
        'react',
        'react-dom',
        'zustand',
        'use-sync-external-store',
        'use-sync-external-store/shim',
        'use-sync-external-store/shim/with-selector',
        'date-fns',
        'zod',
        'dompurify',
        '@tanstack/react-virtual',
        // NOTE: react-hot-toast et scheduler sont EXCLUS du pre-bundling
        // pour forcer des chunks séparés et éviter les collisions de noms (ie)
      ],
      exclude: [
        '@vite/client', 
        '@vite/env',
        'react-hot-toast', // CRITICAL FIX: Exclure react-hot-toast du pre-bundling
        'scheduler', // CRITICAL FIX: Exclure scheduler pour éviter les collisions avec react-hot-toast
      ],
      esbuildOptions: {
        supported: { 
          'top-level-await': true 
        }
      }
    },
    css: {
      devSourcemap: !isProduction,
    },
    define: {
      __CSP_NONCE__: JSON.stringify('__CSP_NONCE__')
    }
  }
})