#!/usr/bin/env node
/**
 * Generates extension bridge files based on clawx-extensions.json and
 * which packages are actually installed in node_modules.
 *
 * Outputs:
 *   electron/extensions/_ext-bridge.generated.ts  (main process)
 *   src/extensions/_ext-bridge.generated.ts        (renderer)
 *
 * Both files are .gitignore'd. When no external extensions are installed,
 * they export no-op functions so the core compiles cleanly.
 *
 * Usage:  node scripts/generate-ext-bridge.mjs
 */
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
import { resolve, dirname } from 'path';
import { fileURLToPath } from 'url';

const __dirname = dirname(fileURLToPath(import.meta.url));
const ROOT = resolve(__dirname, '..');

const MANIFEST_PATH = resolve(ROOT, 'clawx-extensions.json');
const MAIN_OUT = resolve(ROOT, 'electron/extensions/_ext-bridge.generated.ts');
const RENDERER_OUT = resolve(ROOT, 'src/extensions/_ext-bridge.generated.ts');

function readManifest() {
  if (!existsSync(MANIFEST_PATH)) return { extensions: {} };
  try {
    return JSON.parse(readFileSync(MANIFEST_PATH, 'utf-8'));
  } catch {
    return { extensions: {} };
  }
}

function isExternalId(id) {
  return id && !id.startsWith('builtin/');
}

function resolvePackageName(extensionId) {
  // Convention: extension IDs like "@scope/pkg/sub" map to package "@scope/pkg"
  // IDs like "@scope/pkg" map to package "@scope/pkg"
  const parts = extensionId.split('/');
  if (parts[0].startsWith('@') && parts.length >= 2) {
    return parts.slice(0, 2).join('/');
  }
  return parts[0];
}

function isPackageInstalled(pkgName) {
  return existsSync(resolve(ROOT, 'node_modules', ...pkgName.split('/')));
}

function generateMainBridge(manifest) {
  const externalMain = (manifest.extensions?.main ?? []).filter(isExternalId);
  const installedExts = externalMain.filter((id) => isPackageInstalled(resolvePackageName(id)));

  if (installedExts.length === 0) {
    return [
      '// Auto-generated — no external main-process extensions installed.',
      '// To add extensions, configure clawx-extensions.json and link the package.',
      'export function loadExternalMainExtensions(): void { /* no-op */ }',
      '',
    ].join('\n');
  }

  const lines = [
    '// Auto-generated by scripts/generate-ext-bridge.mjs — do not edit.',
    "import { extensionRegistry } from './registry';",
    '',
  ];

  installedExts.forEach((id, i) => {
    const pkg = resolvePackageName(id);
    const subpath = id.slice(pkg.length + 1); // e.g. "enterprise-auth"
    const importPath = subpath ? `${pkg}/${subpath}` : pkg;
    const factoryName = `ext${i}`;
    lines.push(`import { ${guessFactoryExport(subpath || id)} as ${factoryName} } from '${importPath}';`);
  });

  lines.push('');
  lines.push('export function loadExternalMainExtensions(): void {');
  installedExts.forEach((_id, i) => {
    lines.push(`  const e${i} = ext${i}();`);
    lines.push(`  if (e${i}) extensionRegistry.register(e${i});`);
  });
  lines.push('}');
  lines.push('');

  return lines.join('\n');
}

function generateRendererBridge(manifest) {
  const externalRenderer = (manifest.extensions?.renderer ?? []).filter(isExternalId);
  const installedExts = externalRenderer.filter((id) => isPackageInstalled(resolvePackageName(id)));

  if (installedExts.length === 0) {
    return [
      '// Auto-generated — no external renderer extensions installed.',
      '// To add extensions, configure clawx-extensions.json and link the package.',
      'export function loadExternalRendererExtensions(): void { /* no-op */ }',
      '',
    ].join('\n');
  }

  const lines = [
    '// Auto-generated by scripts/generate-ext-bridge.mjs — do not edit.',
    "import { rendererExtensionRegistry } from './registry';",
    '',
  ];

  installedExts.forEach((id, i) => {
    const pkg = resolvePackageName(id);
    const subpath = id.slice(pkg.length + 1);
    const importPath = subpath ? `${pkg}/${subpath}` : pkg;
    const factoryName = `ext${i}`;
    lines.push(`import { ${guessFactoryExport(subpath || id)} as ${factoryName} } from '${importPath}';`);
  });

  lines.push('');
  lines.push('export function loadExternalRendererExtensions(): void {');
  installedExts.forEach((_id, i) => {
    lines.push(`  const e${i} = ext${i}();`);
    lines.push(`  if (e${i}) rendererExtensionRegistry.register(e${i});`);
  });
  lines.push('}');
  lines.push('');

  return lines.join('\n');
}

function guessFactoryExport(subpath) {
  // "enterprise-auth" → "createEnterpriseAuthExtension"
  // "enterprise-ui"   → "createEnterpriseUIExtension"
  // "skillshub-marketplace" → "createSkillshubMarketplaceExtension"
  const camel = subpath
    .replace(/[^a-zA-Z0-9]+(.)/g, (_, c) => c.toUpperCase())
    .replace(/^./, (c) => c.toUpperCase());
  return `create${camel}Extension`;
}

// ─── Main ───
const manifest = readManifest();

mkdirSync(dirname(MAIN_OUT), { recursive: true });
mkdirSync(dirname(RENDERER_OUT), { recursive: true });

writeFileSync(MAIN_OUT, generateMainBridge(manifest));
writeFileSync(RENDERER_OUT, generateRendererBridge(manifest));

const mainExts = (manifest.extensions?.main ?? []).filter(isExternalId);
const rendererExts = (manifest.extensions?.renderer ?? []).filter(isExternalId);
const totalExternal = mainExts.length + rendererExts.length;

if (totalExternal === 0) {
  console.log('[ext-bridge] No external extensions configured — generated empty stubs.');
} else {
  const installedMain = mainExts.filter((id) => isPackageInstalled(resolvePackageName(id)));
  const installedRenderer = rendererExts.filter((id) => isPackageInstalled(resolvePackageName(id)));
  console.log(
    `[ext-bridge] Generated bridges: ${installedMain.length}/${mainExts.length} main, ${installedRenderer.length}/${rendererExts.length} renderer extensions resolved.`,
  );
}
