import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { wasm } from '@rollup/plugin-wasm';
import { viteStaticCopy } from 'vite-plugin-static-copy';
import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin';
import { NodeGlobalsPolyfillPlugin } from '@esbuild-plugins/node-globals-polyfill';
import inject from '@rollup/plugin-inject';
import topLevelAwait from 'vite-plugin-top-level-await';
import { VitePWA } from 'vite-plugin-pwa';
import fs from 'fs';
import path from 'path';
import buildConfig from './build.config';
import { liveTsxPlugin, readDefaultLiveSource } from './playground-liveTsxPlugin';

const projectRoot = path.resolve();

const copyFiles = {
  targets: [
    {
      src: 'node_modules/pdfjs-dist/build/pdf.worker.min.mjs',
      dest: '',
      rename: 'pdf.worker.min.js',
    },
    {
      src: 'node_modules/gif.js/dist/gif.worker.js',
      dest: '',
    },
    {
      src: 'netlify.toml',
      dest: '',
    },
    {
      src: 'config.json',
      dest: '',
    },
    {
      src: 'public/manifest.json',
      dest: '',
    },
    {
      src: 'public/res/android',
      dest: 'public/',
    },
    {
      src: 'public/locales',
      dest: 'public/',
    },
    {
      src: 'public/sound',
      dest: 'public/',
    },
    {
      src: 'public/font',
      dest: 'public/',
    },
  ],
};

function serverMatrixSdkCryptoWasm(wasmFilePath) {
  return {
    name: 'vite-plugin-serve-matrix-sdk-crypto-wasm',
    configureServer(server) {
      server.middlewares.use((req, res, next) => {
        if (req.url === wasmFilePath) {
          const resolvedPath = path.join(path.resolve(), "/node_modules/@matrix-org/matrix-sdk-crypto-wasm/pkg/matrix_sdk_crypto_wasm_bg.wasm");

          if (fs.existsSync(resolvedPath)) {
            res.setHeader('Content-Type', 'application/wasm');
            res.setHeader('Cache-Control', 'no-cache');

            const fileStream = fs.createReadStream(resolvedPath);
            fileStream.pipe(res);
          } else {
            res.writeHead(404);
            res.end('File not found');
          }
        } else {
          next();
        }
      });
    },
  };
}

function serveGifWorker() {
  return {
    name: 'vite-plugin-serve-gif-worker',
    configureServer(server) {
      server.middlewares.use((req, res, next) => {
        if (req.url === '/gif.worker.js') {
          const resolvedPath = path.join(path.resolve(), '/node_modules/gif.js/dist/gif.worker.js');

          if (fs.existsSync(resolvedPath)) {
            res.setHeader('Content-Type', 'application/javascript');
            res.setHeader('Cache-Control', 'no-cache');

            const fileStream = fs.createReadStream(resolvedPath);
            fileStream.pipe(res);
          } else {
            res.writeHead(404);
            res.end('File not found');
          }
        } else {
          next();
        }
      });
    },
  };
}

function servePublicAssets() {
  return {
    name: 'vite-plugin-serve-public-assets',
    configureServer(server) {
      server.middlewares.use((req, res, next) => {
        // Serve sound files
        if (req.url?.startsWith('/sound/')) {
          const fileName = req.url.replace('/sound/', '');
          const resolvedPath = path.join(path.resolve(), 'public', 'sound', fileName);

          if (fs.existsSync(resolvedPath)) {
            const ext = path.extname(fileName);
            const contentType = ext === '.ogg' ? 'audio/ogg' : 'application/octet-stream';
            res.setHeader('Content-Type', contentType);
            res.setHeader('Cache-Control', 'no-cache');

            const fileStream = fs.createReadStream(resolvedPath);
            fileStream.pipe(res);
          } else {
            res.writeHead(404);
            res.end('File not found');
          }
        } else {
          next();
        }
      });
    },
  };
}

// CORS proxy for Matrix homeservers during development
function corsProxyMiddleware() {
  return {
    name: 'vite-plugin-cors-proxy',
    configureServer(server) {
      server.middlewares.use(async (req, res, next) => {
        // Proxy /.well-known requests: /_matrix-cors-proxy/homeserver.com/.well-known/matrix/client
        const corsProxyMatch = req.url?.match(/^\/_matrix-cors-proxy\/([^/]+)(\/.*)/);
        if (corsProxyMatch) {
          const [, hostname, pathname] = corsProxyMatch;
          const targetUrl = `https://${hostname}${pathname}`;
          
          console.log(`[CORS Proxy] ${req.method} ${req.url}`);
          console.log(`[CORS Proxy] Target: ${targetUrl}`);
          
          // Handle preflight OPTIONS request
          if (req.method === 'OPTIONS') {
            res.setHeader('Access-Control-Allow-Origin', '*');
            res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
            res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
            res.writeHead(204);
            res.end();
            return;
          }
          
          try {
            // Collect request body for POST/PUT
            let body = null;
            if (req.method === 'POST' || req.method === 'PUT') {
              const chunks = [];
              for await (const chunk of req) {
                chunks.push(chunk);
              }
              body = Buffer.concat(chunks).toString();
              console.log(`[CORS Proxy] Request body:`, body);
            }
            
            // Forward request with method, headers, and body
            const fetchOptions = {
              method: req.method,
              headers: {
                'Content-Type': req.headers['content-type'] || 'application/json',
                'Authorization': req.headers['authorization'] || '',
              },
            };
            
            if (body) {
              fetchOptions.body = body;
            }
            
            const response = await fetch(targetUrl, fetchOptions);
            const data = await response.text();
            
            console.log(`[CORS Proxy] Response status: ${response.status}`);
            console.log(`[CORS Proxy] Response data:`, data.substring(0, 500));
            
            // Set CORS headers
            res.setHeader('Access-Control-Allow-Origin', '*');
            res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
            res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
            res.setHeader('Content-Type', response.headers.get('content-type') || 'application/json');
            
            res.writeHead(response.status);
            res.end(data);
          } catch (error) {
            console.error(`[CORS Proxy] Error for ${targetUrl}:`, error);
            res.writeHead(500);
            res.end(JSON.stringify({ error: error.message }));
          }
        } else {
          next();
        }
      });
    },
  };
}

export default defineConfig({
  appType: 'spa',
  publicDir: false,
  base: buildConfig.base,
  assetsInclude: ['**/*.ogg', '**/*.mp3', '**/*.wav'],
  resolve: {
    // Keep a single React instance — dual copies cause resolveDispatcher() === null.
    dedupe: ['react', 'react-dom'],
    alias: [
      { find: '@cinny', replacement: path.resolve(projectRoot, 'src') },
      {
        find: '@playground/mocks',
        replacement: path.resolve(projectRoot, 'src/playground/mocks/index.ts'),
      },
      // Exact match only — do not rewrite loglevel/lib/...
      {
        find: /^loglevel$/,
        replacement: path.resolve(projectRoot, 'src/playground/shims/loglevel.js'),
      },
      {
        find: /^react$/,
        replacement: path.resolve(projectRoot, 'node_modules/react'),
      },
      {
        find: /^react-dom$/,
        replacement: path.resolve(projectRoot, 'node_modules/react-dom'),
      },
    ],
  },
  server: {
    port: 38347,
    host: '0.0.0.0',
    cors: true, // Enable CORS for dev server
    open: false,
    fs: {
      // Allow serving files from one level up to the project root
      allow: ['..'],
    },
  },
  plugins: [
    corsProxyMiddleware(), // Add CORS proxy for development
    serverMatrixSdkCryptoWasm('/node_modules/.vite/deps/pkg/matrix_sdk_crypto_wasm_bg.wasm'),
    serveGifWorker(),
    servePublicAssets(),
    liveTsxPlugin(projectRoot, readDefaultLiveSource(projectRoot)),
    topLevelAwait({
      // The export name of top-level await promise for each chunk module
      promiseExportName: '__tla',
      // The function to generate import names of top-level await promise in each chunk module
      promiseImportName: (i) => `__tla_${i}`,
    }),
    viteStaticCopy(copyFiles),
    vanillaExtractPlugin(),
    wasm(),
    react(),
    VitePWA({
      srcDir: 'src',
      filename: 'sw.ts',
      strategies: 'injectManifest',
      injectRegister: false,
      manifest: false,
      injectManifest: {
        injectionPoint: undefined,
      },
      devOptions: {
        enabled: false,
        type: 'module'
      }
    }),
  ],
  optimizeDeps: {
    include: [
      'react',
      'react-dom',
      'react/jsx-runtime',
      'react/jsx-dev-runtime',
      'loglevel',
      'matrix-js-sdk',
      '@monaco-editor/react',
    ],
    needsInterop: ['loglevel'],
    rolldownOptions: {
      define: {
        global: 'globalThis',
      },
      plugins: [
        // Enable polyfill plugins
        NodeGlobalsPolyfillPlugin({
          process: false,
          buffer: true,
        }),
      ],
    },
  },
  build: {
    target: 'es2022',
    outDir: 'dist',
    sourcemap: true,
    copyPublicDir: false,
    rollupOptions: {
      input: {
        main: path.resolve(projectRoot, 'index.html'),
        playground: path.resolve(projectRoot, 'playground.html'),
      },
      plugins: [inject({ Buffer: ['buffer', 'Buffer'] })],
    },
  },
});
