#!/usr/bin/env node
// Build a signed .zxp for the Dragonflight Premiere panel.
//
//   - Reads version from ../CSXS/manifest.xml (<ExtensionBundleVersion>)
//   - Generates a self-signed cert on first run (cert/dragonflight-selfsigned.p12)
//   - Stages the panel bundle into stage/ (excludes build/ + dev cruft)
//   - Calls zxp-sign-cmd to sign + package into dist/
//
// Usage:  node build-zxp.mjs
// Output: dist/dragonflight-premiere-panel-<version>.zxp

import { mkdirSync, existsSync, readFileSync, writeFileSync, rmSync, cpSync, readdirSync, statSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname, join, resolve } from 'node:path';
import { randomBytes } from 'node:crypto';
import zxp from 'zxp-sign-cmd';

const HERE = dirname(fileURLToPath(import.meta.url));
const PANEL_DIR = resolve(HERE, '..');
const MANIFEST = join(PANEL_DIR, 'CSXS', 'manifest.xml');
const CERT_DIR = join(HERE, 'cert');
const CERT_FILE = join(CERT_DIR, 'dragonflight-selfsigned.p12');
const PASS_FILE = join(CERT_DIR, 'cert-passphrase.txt');
const STAGE_DIR = join(HERE, 'stage');
const DIST_DIR = join(HERE, 'dist');

// Top-level entries to exclude from the staged bundle.
const EXCLUDE = new Set(['build', 'install-windows.ps1', '.git', '.gitignore', 'node_modules']);

function readVersion() {
  const xml = readFileSync(MANIFEST, 'utf8');
  // Accept either <ExtensionBundleVersion>X.Y.Z</ExtensionBundleVersion> (the
  // namespace-style manifest) or ExtensionBundleVersion="X.Y.Z" (the attribute
  // form used by the CSXS 7.0+ schema).
  const m = xml.match(/<ExtensionBundleVersion>([^<]+)<\/ExtensionBundleVersion>/)
         || xml.match(/\bExtensionBundleVersion\s*=\s*"([^"]+)"/);
  if (!m) throw new Error(`Could not find ExtensionBundleVersion in ${MANIFEST}`);
  return m[1].trim();
}

async function ensureCert() {
  mkdirSync(CERT_DIR, { recursive: true });
  if (existsSync(CERT_FILE) && existsSync(PASS_FILE)) {
    return readFileSync(PASS_FILE, 'utf8').trim();
  }
  console.log('No signing cert found — generating self-signed cert (one-time)…');
  const passphrase = randomBytes(24).toString('base64url');
  writeFileSync(PASS_FILE, passphrase + '\n', { mode: 0o600 });
  await zxp.selfSignedCert({
    country: 'US',
    province: 'WA',
    org: 'Wild Dragon LLC',
    name: 'Wild Dragon LLC',
    password: passphrase,
    output: CERT_FILE,
    validityDays: 365 * 25,
  });
  console.log(`  wrote ${CERT_FILE}`);
  console.log(`  wrote ${PASS_FILE}`);
  console.log('  >> COMMIT both files so future builds reuse them. <<');
  return passphrase;
}

function stageBundle() {
  if (existsSync(STAGE_DIR)) rmSync(STAGE_DIR, { recursive: true, force: true });
  mkdirSync(STAGE_DIR, { recursive: true });
  for (const entry of readdirSync(PANEL_DIR)) {
    if (EXCLUDE.has(entry)) continue;
    const src = join(PANEL_DIR, entry);
    const dst = join(STAGE_DIR, entry);
    cpSync(src, dst, { recursive: true });
  }
}

async function signZxp(version, passphrase) {
  mkdirSync(DIST_DIR, { recursive: true });
  const output = join(DIST_DIR, `dragonflight-premiere-panel-${version}.zxp`);
  if (existsSync(output)) rmSync(output);
  await zxp.sign({
    input: STAGE_DIR,
    output,
    cert: CERT_FILE,
    password: passphrase,
  });
  const bytes = statSync(output).size;
  console.log(`Built ${output} (${(bytes / 1024).toFixed(1)} KB)`);
  return output;
}

async function main() {
  const version = readVersion();
  console.log(`Dragonflight Premiere panel — ZXP build v${version}`);
  const passphrase = await ensureCert();
  stageBundle();
  await signZxp(version, passphrase);
  rmSync(STAGE_DIR, { recursive: true, force: true });
}

main().catch((err) => {
  console.error('ZXP build failed:', err);
  process.exit(1);
});
