import * as esbuild from 'esbuild';
import { cpSync, mkdirSync, rmSync, readdirSync, existsSync, statSync, readFileSync } from 'fs';
import { createRequire } from 'module';
import { dirname, join } from 'path';
import { execSync } from 'child_process';

const require = createRequire(import.meta.url);
const DIST = 'dist';
const WASM_DIR = 'wasm';
const XPI_NAME = 'pedantic-picture-packer.xpi';
const watch = process.argv.includes('--watch');
const skipXpi = process.argv.includes('--no-xpi');

// ── esbuild Plugin: WASM-Pfade in Glue-Code auf wasm/ umschreiben ──────────
// libimagequant und @jsquash/oxipng Glue-Code verwenden
// new URL('file.wasm', import.meta.url) um die WASM-Datei zur Laufzeit zu finden.
// Dieser Plugin schreibt den Pfad auf wasm/ um und entfernt ggf. vorhandenes
// ./ Präfix, damit esbuild nicht versucht, die Datei als Asset aufzulösen.
const wasmPathPlugin = {
  name: 'wasm-path-rewrite',
  setup(build) {
    build.onLoad({ filter: /(libimagequant_wasm|squoosh_oxipng).*\.js$/ }, async (args) => {
      let contents = readFileSync(args.path, 'utf8');
      contents = contents.replace(
        /new URL\(\s*['"](?:\.\/)?([^'"]+\.wasm)['"]\s*,\s*import\.meta\.url\s*\)/g,
        (_, file) => `new URL('wasm/${file}', import.meta.url)`
      );
      return { contents, loader: 'js' };
    });
  },
};

const workerConfig = {
  entryPoints: ['src/worker.js'],
  bundle: true,
  outfile: `${DIST}/worker.js`,
  format: 'esm',
  target: ['firefox115'],
  assetNames: '[name]',
  logLevel: 'info',
  plugins: [wasmPathPlugin],
};

// ── Paket-Verzeichnis zuverlässig auflösen ──────────────────────────────────
function resolvePackageDir(pkg) {
  try {
    return dirname(require.resolve(pkg + '/package.json'));
  } catch {}
  try {
    let dir = dirname(require.resolve(pkg));
    for (let i = 0; i < 10; i++) {
      if (existsSync(join(dir, 'package.json'))) return dir;
      const parent = dirname(dir);
      if (parent === dir) break;
      dir = parent;
    }
  } catch {}
  return null;
}

// ── WASM aus node_modules in wasm/ synchronisieren ─────────────────────────
function syncWasmFromPackages() {
  mkdirSync(WASM_DIR, { recursive: true });

  // libimagequant: erste .wasm im Paket
  const liqDir = resolvePackageDir('@fe-daily/libimagequant-wasm');
  if (liqDir) {
    let found = false;
    function searchLiq(dir, depth = 0) {
      if (found || depth > 5 || !existsSync(dir)) return;
      for (const file of readdirSync(dir)) {
        if (file.endsWith('.wasm')) {
          cpSync(join(dir, file), join(WASM_DIR, file));
          console.log(`WASM synchronisiert: ${file} (aus @fe-daily/libimagequant-wasm)`);
          found = true;
          return;
        }
      }
      for (const file of readdirSync(dir)) {
        const full = join(dir, file);
        try { if (statSync(full).isDirectory()) searchLiq(full, depth + 1); } catch {}
      }
    }
    searchLiq(liqDir);
    if (!found) console.warn('Keine .wasm-Datei in @fe-daily/libimagequant-wasm gefunden');
  } else {
    console.warn('Paket @fe-daily/libimagequant-wasm nicht gefunden');
  }

  // oxipng: squoosh_oxipng_bg.wasm aus codec/pkg/ (single-threaded, nicht pkg-parallel)
   const oxipngWasmSrc = join('oxipng-wasm', 'pkg', 'oxipng_wasm_bg.wasm');
   if (existsSync(oxipngWasmSrc)) {
     cpSync(oxipngWasmSrc, join(WASM_DIR, 'oxipng_wasm_bg.wasm'));
     console.log('WASM synchronisiert: oxipng_wasm_bg.wasm aus oxipng-wasm/pkg');
   } else {
     console.warn('oxipng_wasm_bg.wasm nicht gefunden in oxipng-wasm/pkg – zuerst wasm-pack build --target web --release ausführen');
   }

  if (!existsSync(join(WASM_DIR, 'cjpegli.wasm'))) {
    console.warn('WARNUNG: wasm/cjpegli.wasm nicht gefunden — bitte manuell ablegen');
  }
}

// ── Statische Dateien kopieren ─────────────────────────────────────────────
function copyStatic() {
  cpSync('src', DIST, {
    recursive: true,
    filter: (s) => !s.endsWith('/worker.js') && !s.endsWith('.wasm'),
  });

  cpSync('manifest.json', `${DIST}/manifest.json`);

  if (existsSync('_locales')) {
    cpSync('_locales', join(DIST, '_locales'), { recursive: true });
    console.log('Locales kopiert');
  }

  if (existsSync(WASM_DIR)) {
    cpSync(WASM_DIR, join(DIST, 'wasm'), { recursive: true });
    const wasms = readdirSync(WASM_DIR).filter(f => f.endsWith('.wasm'));
    console.log(`WASM kopiert nach dist/wasm/: ${wasms.join(', ') || '(leer!)'}`);
  } else {
    console.error(`FEHLER: Ordner ${WASM_DIR}/ nicht gefunden`);
  }
}

// ── XPI erstellen ──────────────────────────────────────────────────────────
function createXpi() {
  rmSync(XPI_NAME, { force: true });
  try {
    execSync(`7z a -tzip ../${XPI_NAME} * -mx=9 -mfb=273 -bd`, {
      cwd: DIST,
      stdio: 'pipe',
    });
    console.log(`✓ XPI erstellt (7-Zip -mx=9 -mfb=273): ${XPI_NAME}`);
  } catch {
    try {
      execSync(`zip -9 -X -r ../${XPI_NAME} .`, { cwd: DIST, stdio: 'pipe' });
      console.log(`✓ XPI erstellt (zip -9): ${XPI_NAME}`);
    } catch {
      console.error('FEHLER: Weder 7z noch zip gefunden.');
      console.error('Installiere 7-Zip (empfohlen für bessere Kompression) oder zip.');
    }
  }
}

// ── Main ────────────────────────────────────────────────────────────────────
rmSync(DIST, { recursive: true, force: true });
mkdirSync(DIST, { recursive: true });

if (watch) {
  const ctx = await esbuild.context(workerConfig);
  await ctx.watch();
  syncWasmFromPackages();
  await copyStatic();
  console.log('\nWatch-Modus aktiv.');
} else {
  await esbuild.build(workerConfig);
  syncWasmFromPackages();
  await copyStatic();
  if (!skipXpi) createXpi();
  console.log(`\n✓ Build fertig: dist/ + ${XPI_NAME}`);
}
