/**
 * Vite config for the Chrome extension build.
 *
 * Produces dist/extension/ with:
 * - index.html (side panel UI — bundled from packages/webapp/src/ui/main.ts)
 * - service-worker.js (built from packages/chrome-extension/src/service-worker.ts)
 * - offscreen.html + offscreen entry (built from packages/chrome-extension/src/offscreen.ts)
 * - sandbox.html, manifest.json (copied from packages/chrome-extension/)
 */

import { defineConfig } from 'vite';
import { resolve, dirname } from 'path';
import { fileURLToPath } from 'url';
import { copyFileSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'fs';

const __dirname = dirname(fileURLToPath(import.meta.url));
const repoRoot = resolve(__dirname, '../..');
const rootPkg = JSON.parse(readFileSync(resolve(repoRoot, 'package.json'), 'utf-8')) as {
  version: string;
};
const sliccReleasedAt = process.env['SLICC_RELEASED_AT'] ?? null;

export default defineConfig(({ mode }) => ({
  root: repoRoot,
  publicDir: resolve(repoRoot, 'packages/assets'),
  define: {
    __DEV__: JSON.stringify(mode !== 'production'),
    __SLICC_VERSION__: JSON.stringify(rootPkg.version),
    __SLICC_RELEASED_AT__: JSON.stringify(sliccReleasedAt),
  },
  resolve: {
    alias: {
      // Workspace `@slicc/shared-ts` points at source so esbuild/Rolldown for the
      // SW IIFE and the extension's worker entries resolve without requiring
      // `packages/shared-ts/dist/` to exist at build time.
      '@slicc/shared-ts': resolve(repoRoot, 'packages/shared-ts/src/index.ts'),
      // The pinned isomorphic-git package resolves "." to index.cjs, and that
      // CJS entry imports Node crypto. Force the browser-safe ESM entry
      // instead.
      'isomorphic-git': resolve(repoRoot, 'node_modules/isomorphic-git/index.js'),
      'node:zlib': resolve(__dirname, '../webapp/src/shims/empty.ts'),
      'node:module': resolve(__dirname, '../webapp/src/shims/empty.ts'),
      stream: resolve(__dirname, '../webapp/src/shims/stream.ts'),
      http: resolve(__dirname, '../webapp/src/shims/http.ts'),
      https: resolve(__dirname, '../webapp/src/shims/https.ts'),
      http2: resolve(__dirname, '../webapp/src/shims/http2.ts'),
      // Deep import into pi-coding-agent's compaction submodule (see vite.config.ts)
      '@earendil-works/pi-coding-agent/dist/core/compaction/compaction.js': resolve(
        repoRoot,
        'node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/compaction.js'
      ),
      '@earendil-works/pi-ai/dist/providers/transform-messages.js': resolve(
        repoRoot,
        'node_modules/@earendil-works/pi-ai/dist/providers/transform-messages.js'
      ),
      '@earendil-works/pi-ai/dist/providers/simple-options.js': resolve(
        repoRoot,
        'node_modules/@earendil-works/pi-ai/dist/providers/simple-options.js'
      ),
    },
  },
  esbuild: {
    target: 'esnext',
  },
  optimizeDeps: {
    exclude: ['@earendil-works/pi-coding-agent'],
    esbuildOptions: {
      target: 'esnext',
    },
  },
  build: {
    outDir: resolve(repoRoot, 'dist/extension'),
    emptyOutDir: true,
    target: 'esnext',
    rollupOptions: {
      input: {
        index: resolve(__dirname, '../webapp/index.html'),
        offscreen: resolve(__dirname, 'offscreen.html'),
      },
      output: {
        entryFileNames: 'assets/[name]-[hash].js',
      },
    },
  },
  plugins: [
    {
      name: 'stub-pi-node-internals',
      enforce: 'pre' as const,
      resolveId(source, importer) {
        const normalizedImporter = importer?.replace(/\\/g, '/');
        if (normalizedImporter?.includes('@earendil-works/pi-coding-agent')) {
          if (source.endsWith('/session-manager.js')) {
            return resolve(__dirname, '../webapp/src/stubs/pi-session-manager-stub.ts');
          }
          if (source.endsWith('/config.js') || source === '../config.js') {
            return resolve(__dirname, '../webapp/src/stubs/pi-config-stub.ts');
          }
        }
      },
    },
    {
      name: 'build-extension-service-worker',
      async closeBundle() {
        // MV3 service workers are classic scripts, not ES modules.
        // Bundle the service worker as one self-contained file so Chrome
        // never sees Rollup-generated shared-chunk imports.
        const esbuild = await import('esbuild');
        await esbuild.build({
          entryPoints: [resolve(__dirname, 'src/service-worker.ts')],
          bundle: true,
          outfile: resolve(repoRoot, 'dist/extension/service-worker.js'),
          format: 'iife',
          target: 'esnext',
          minify: true,
          alias: {
            // Workspace package — resolve to source so the IIFE bundle does
            // not require `packages/shared-ts/dist/` to exist at build time.
            '@slicc/shared-ts': resolve(repoRoot, 'packages/shared-ts/src/index.ts'),
          },
          define: {
            __DEV__: JSON.stringify(mode !== 'production'),
            global: 'globalThis',
          },
        });
      },
    },
    {
      name: 'build-preview-sw',
      async closeBundle() {
        // Build preview-sw as a self-contained IIFE via esbuild.
        // Rollup would code-split LightningFS into a shared chunk, which SWs can't import.
        const esbuild = await import('esbuild');
        await esbuild.build({
          entryPoints: [resolve(__dirname, '../webapp/src/ui/preview-sw.ts')],
          bundle: true,
          outfile: resolve(repoRoot, 'dist/extension/preview-sw.js'),
          format: 'iife',
          target: 'esnext',
          minify: true,
          define: { __DEV__: 'false', global: 'globalThis' },
        });
      },
    },
    {
      name: 'build-secrets-page',
      async closeBundle() {
        // The Mount Secrets options page (secrets.html) loads
        // dist/extension/secrets.js as a classic script. Bundle the
        // TypeScript entry to a single self-contained IIFE so it
        // works without ES-module imports.
        const esbuild = await import('esbuild');
        await esbuild.build({
          entryPoints: [resolve(__dirname, 'src/secrets-entry.ts')],
          bundle: true,
          outfile: resolve(repoRoot, 'dist/extension/secrets.js'),
          format: 'iife',
          target: 'esnext',
          minify: true,
          define: { __DEV__: 'false', global: 'globalThis' },
          // Same alias as the SW build above — standalone esbuild doesn't
          // inherit Vite's resolve.alias, so without this the
          // `@slicc/shared-ts` import in secrets-entry.ts fails to resolve.
          alias: {
            '@slicc/shared-ts': resolve(repoRoot, 'packages/shared-ts/src/index.ts'),
          },
        });
      },
    },
    {
      name: 'build-slicc-editor',
      async closeBundle() {
        const esbuild = await import('esbuild');
        await esbuild.build({
          entryPoints: [resolve(__dirname, '../webapp/src/ui/slicc-editor-entry.ts')],
          bundle: true,
          outfile: resolve(repoRoot, 'dist/extension/slicc-editor.js'),
          format: 'iife',
          target: 'esnext',
          minify: true,
          define: { __DEV__: 'false', global: 'globalThis' },
        });
        // Also build lucide-icons.js for sprinkles
        await esbuild.build({
          entryPoints: [resolve(__dirname, '../webapp/src/ui/lucide-icons.ts')],
          bundle: true,
          outfile: resolve(repoRoot, 'dist/extension/lucide-icons.js'),
          format: 'iife',
          target: 'esnext',
          minify: true,
          define: { __DEV__: 'false', global: 'globalThis' },
        });
      },
    },
    {
      name: 'build-slicc-diff',
      async closeBundle() {
        const esbuild = await import('esbuild');
        await esbuild.build({
          entryPoints: [resolve(__dirname, '../webapp/src/ui/slicc-diff-entry.ts')],
          bundle: true,
          outfile: resolve(repoRoot, 'dist/extension/slicc-diff.js'),
          format: 'iife',
          target: 'esnext',
          minify: true,
          define: { __DEV__: 'false', global: 'globalThis' },
          plugins: [
            {
              name: 'resolve-pierre-diffs-internals',
              setup(build) {
                build.onResolve({ filter: /^@pierre\/diffs\/dist\// }, (args) => ({
                  path: resolve(repoRoot, 'node_modules', args.path.replace(/\.js$/, '') + '.js'),
                }));
              },
            },
          ],
        });
      },
    },
    {
      name: 'copy-extension-assets',
      closeBundle() {
        const outDir = resolve(repoRoot, 'dist/extension');
        mkdirSync(outDir, { recursive: true });
        // Always override manifest.version from root package.json — the
        // committed source value is a sentinel and never read at runtime.
        // SLICC_EXT_DEV=1 also strips "key" so Chrome assigns a random ID
        // (avoids stale storage from previous installs).
        const manifestSrc = resolve(__dirname, 'manifest.json');
        const manifestDest = resolve(outDir, 'manifest.json');
        const manifest = JSON.parse(readFileSync(manifestSrc, 'utf-8'));
        manifest.version = rootPkg.version;
        if (process.env['SLICC_EXT_DEV']) {
          delete manifest.key;
        }
        writeFileSync(manifestDest, JSON.stringify(manifest, null, 2));
        copyFileSync(resolve(__dirname, 'sandbox.html'), resolve(outDir, 'sandbox.html'));
        copyFileSync(
          resolve(__dirname, 'sprinkle-sandbox.html'),
          resolve(outDir, 'sprinkle-sandbox.html')
        );
        copyFileSync(
          resolve(__dirname, 'tool-ui-sandbox.html'),
          resolve(outDir, 'tool-ui-sandbox.html')
        );
        copyFileSync(resolve(__dirname, 'voice-popup.html'), resolve(outDir, 'voice-popup.html'));
        copyFileSync(resolve(__dirname, 'voice-popup.js'), resolve(outDir, 'voice-popup.js'));
        copyFileSync(resolve(__dirname, 'mount-popup.html'), resolve(outDir, 'mount-popup.html'));
        copyFileSync(resolve(__dirname, 'mount-popup.js'), resolve(outDir, 'mount-popup.js'));
        copyFileSync(resolve(__dirname, 'secrets.html'), resolve(outDir, 'secrets.html'));
        // secrets.js is built from src/secrets-entry.ts via esbuild below;
        // see the 'build-secrets-page' plugin.

        // Copy logo files for extension icons and header
        const logosSrc = resolve(__dirname, '../assets/logos');
        const logosDest = resolve(outDir, 'logos');
        mkdirSync(logosDest, { recursive: true });
        for (const file of readdirSync(logosSrc)) {
          if (file.endsWith('.png') || file.endsWith('.ico')) {
            try {
              copyFileSync(resolve(logosSrc, file), resolve(logosDest, file));
            } catch {
              /* skip */
            }
          }
        }

        // Copy fonts if present (Adobe Clean — local dev only, gitignored)
        const fontsSrc = resolve(__dirname, '../assets/fonts');
        const fontsDest = resolve(outDir, 'fonts');
        try {
          mkdirSync(fontsDest, { recursive: true });
          for (const file of readdirSync(fontsSrc)) {
            if (file.endsWith('.otf') || file.endsWith('.woff2')) {
              try {
                copyFileSync(resolve(fontsSrc, file), resolve(fontsDest, file));
              } catch {
                /* skip */
              }
            }
          }
        } catch {
          /* fonts dir doesn't exist — fine, fallback fonts will be used */
        }

        // Bundle Pyodide for extension (both main page and sandbox CSP block CDN scripts)
        const pyodideSrc = resolve(repoRoot, 'node_modules/pyodide');
        const pyodideDest = resolve(outDir, 'pyodide');
        mkdirSync(pyodideDest, { recursive: true });
        for (const file of [
          'pyodide.asm.js',
          'pyodide.asm.wasm',
          'pyodide.js',
          'pyodide-lock.json',
          'python_stdlib.zip',
        ]) {
          try {
            copyFileSync(resolve(pyodideSrc, file), resolve(pyodideDest, file));
          } catch {
            /* optional file */
          }
        }

        // Bundle ImageMagick WASM for extension (CDN blocked by extension CSP)
        try {
          copyFileSync(
            resolve(repoRoot, 'node_modules/@imagemagick/magick-wasm/dist/magick.wasm'),
            resolve(outDir, 'magick.wasm')
          );
        } catch {
          /* @imagemagick/magick-wasm not installed */
        }

        copyFileSync(resolve(outDir, 'packages/webapp/index.html'), resolve(outDir, 'index.html'));
        copyFileSync(
          resolve(outDir, 'packages/chrome-extension/offscreen.html'),
          resolve(outDir, 'offscreen.html')
        );
      },
    },
  ],
}));
