import { execFileSync } from 'node:child_process';
import { readdirSync, readFileSync } from 'node:fs';
import { join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { defineConfig } from 'vite';
import { svelte } from '@sveltejs/vite-plugin-svelte';

const EMOJI_DATA_CHUNK = /^assets\/(compact|emojibase)-[^/]*\.js$/;

/**
 * The page isn't SW-controlled on first launch, so the worker chunk and the
 * wasm can bypass fetch interception entirely and never get cached. Instead
 * of relying on runtime fetches, prepend the full list of emitted files
 * (chunks, assets, wasm) to sw.js as a global so install() can precache all
 * of it directly, no separate request needed.
 */
function precacheManifestPlugin(): import('vite').Plugin {
  return {
    name: 'malice-sw-precache-manifest',
    generateBundle(_options, bundle) {
      const urls = Object.keys(bundle)
        .filter((fileName) => fileName !== 'sw.js' && !fileName.startsWith('twemoji/') && !EMOJI_DATA_CHUNK.test(fileName))
        .map((fileName) => `/${fileName}`);
      const swChunk = bundle['sw.js'];
      if (swChunk && swChunk.type === 'chunk') {
        swChunk.code = `self.__MALICE_PRECACHE__=${JSON.stringify(urls)};\n${swChunk.code}`;
      }
    }
  };
}

function twemojiPlugin(): import('vite').Plugin {
  const dir = fileURLToPath(new URL('./node_modules/@twemoji/svg', import.meta.url));
  const svgs = () => readdirSync(dir).filter((f) => f.endsWith('.svg'));
  return {
    name: 'malice-twemoji',
    configureServer(server) {
      server.middlewares.use('/twemoji', (req, res, next) => {
        const name = decodeURIComponent((req.url ?? '').split('?')[0]).replace(/^\//, '');
        if (!/^[0-9a-f-]+\.svg$/.test(name)) return next();
        try {
          res.setHeader('Content-Type', 'image/svg+xml');
          res.end(readFileSync(join(dir, name)));
        } catch {
          res.statusCode = 404;
          res.end();
        }
      });
    },
    generateBundle() {
      for (const file of svgs()) {
        this.emitFile({ type: 'asset', fileName: `twemoji/${file}`, source: readFileSync(join(dir, file)) });
      }
    }
  };
}

function buildId(): string {
  if (process.env.MALICE_BUILD) return process.env.MALICE_BUILD;
  let rev = 'dev';
  try {
    rev = execFileSync('git', ['rev-parse', '--short', 'HEAD'], { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim() || rev;
  } catch {
  }
  return `${Math.floor(Date.now() / 1000)}-${rev}`;
}

export default defineConfig(({ mode }) => {
  const device = mode === 'device';
  const build = buildId();
  return {
    plugins: [svelte(), twemojiPlugin(), precacheManifestPlugin()],
    define: { __MALICE_DEVICE__: JSON.stringify(device), __MALICE_BUILD__: JSON.stringify(build) },
    assetsInclude: ['**/*.wasm'],
    worker: { format: 'es' },
    resolve: {
      alias: {
        'malice:device-transport': device ? '/src/lib/device-transport.ts' : '/src/lib/device-transport.stub.ts'
      }
    },
    server: {
      host: '127.0.0.1',
      port: 5173,
      proxy: {
        '/api': 'http://localhost:3000'
      }
    },
    preview: {
      host: '127.0.0.1',
      port: 4173,
      proxy: {
        '/api': 'http://localhost:3000'
      }
    },
    build: {
      outDir: device ? 'dist-device' : 'dist',
      emptyOutDir: true,
      rollupOptions: {
        input: {
          main: 'index.html',
          sw: 'src/sw.ts'
        },
        output: {
          entryFileNames: (chunk) => (chunk.name === 'sw' ? 'sw.js' : 'assets/[name]-[hash].js')
        }
      }
    }
  };
});
