// Build-time generator for the code-mode engine manifest.
//
// Code mode provisions each agent's native engine on demand by downloading the
// per-platform npm package AT THE EXACT VERSION OUR ACP ADAPTER DEPENDS ON, so the
// adapter <-> engine handshake is always in lockstep. This script reads those pinned
// versions from the installed adapter package.json files, queries the npm registry for
// each platform package's tarball URL + integrity, and writes them to
// `src/code-mode/acp/engine-manifest.ts` (committed; regenerate on a version bump).
//
// Usage:  node scripts/gen-engine-manifest.mjs        (run from packages/core)
//
// Why a committed .ts (not a fetched-at-build .json): it needs no network at app build
// time, works offline in dev, is reviewable in PRs, and esbuild inlines it into the
// packaged main bundle.

import { createRequire } from 'module';
import { writeFileSync } from 'fs';
import { fileURLToPath } from 'url';
import * as path from 'path';

const require = createRequire(import.meta.url);
const REGISTRY = 'https://registry.npmjs.org';

// Platform keys we publish for each agent. These mirror the optionalDependencies of the
// engine packages. claude ships musl variants; codex does not.
const CLAUDE_PLATFORMS = [
    'darwin-arm64', 'darwin-x64',
    'linux-x64', 'linux-arm64', 'linux-x64-musl', 'linux-arm64-musl',
    'win32-x64', 'win32-arm64',
];
const CODEX_PLATFORMS = [
    'darwin-arm64', 'darwin-x64',
    'linux-x64', 'linux-arm64',
    'win32-x64', 'win32-arm64',
];

// Read a pinned dependency version from an adapter's package.json, stripping any range
// prefix (^, ~). createRequire resolves the adapter from the installed node_modules.
function pinnedDep(adapterPkg, depName) {
    const pj = require(`${adapterPkg}/package.json`);
    const spec = (pj.dependencies || {})[depName] || (pj.optionalDependencies || {})[depName];
    if (!spec) throw new Error(`${adapterPkg} has no dependency on ${depName}`);
    return spec.replace(/^[\^~]/, '');
}

// Fetch a single version's manifest from the registry and return its dist coordinates.
async function distFor(pkg, version) {
    const url = `${REGISTRY}/${pkg}/${version}`;
    const res = await fetch(url);
    if (!res.ok) {
        if (res.status === 404) return null; // platform not published for this version
        throw new Error(`registry ${res.status} for ${pkg}@${version}`);
    }
    const doc = await res.json();
    return { tarball: doc.dist.tarball, integrity: doc.dist.integrity };
}

// Build one agent's manifest entry: { version, platforms: { <key>: {tarball, integrity} } }.
// pkgFor(platform) -> { pkg, version } gives the registry coordinates for each platform.
async function buildAgent(version, platforms, pkgFor) {
    const out = { version, platforms: {} };
    for (const key of platforms) {
        const { pkg, version: pv } = pkgFor(key);
        const dist = await distFor(pkg, pv);
        if (!dist) {
            console.warn(`  ! ${pkg}@${pv} not found on registry — skipping ${key}`);
            continue;
        }
        out.platforms[key] = { pkg, pkgVersion: pv, ...dist };
        console.log(`  ✓ ${key}: ${pkg}@${pv}`);
    }
    return out;
}

async function main() {
    // Pinned engine versions, read straight from the adapters we ship.
    const claudeVersion = pinnedDep('@agentclientprotocol/claude-agent-acp', '@anthropic-ai/claude-agent-sdk');
    const codexVersion = pinnedDep('@agentclientprotocol/codex-acp', '@openai/codex');
    console.log(`claude engine: @anthropic-ai/claude-agent-sdk@${claudeVersion}`);
    console.log(`codex engine:  @openai/codex@${codexVersion}`);

    const manifest = {
        claude: await buildAgent(claudeVersion, CLAUDE_PLATFORMS, (key) => ({
            pkg: `@anthropic-ai/claude-agent-sdk-${key}`,
            version: claudeVersion,
        })),
        codex: await buildAgent(codexVersion, CODEX_PLATFORMS, (key) => ({
            // codex publishes platform binaries as VERSIONS of @openai/codex
            // (e.g. 0.128.0-darwin-arm64), not as separate package names.
            pkg: '@openai/codex',
            version: `${codexVersion}-${key}`,
        })),
    };

    const outPath = path.join(
        path.dirname(fileURLToPath(import.meta.url)),
        '..', 'src', 'code-mode', 'acp', 'engine-manifest.ts',
    );
    const banner =
        '// AUTO-GENERATED by packages/core/scripts/gen-engine-manifest.mjs — do not edit by hand.\n' +
        '// Regenerate after bumping the @agentclientprotocol/*-acp adapter (engine) versions.\n' +
        '// Maps each agent + platform to the npm tarball + integrity of its native engine.\n\n';
    const body = `export const ENGINE_MANIFEST = ${JSON.stringify(manifest, null, 4)} as const;\n`;
    writeFileSync(outPath, banner + body);
    console.log(`\nWrote ${outPath}`);
}

main().catch((e) => {
    console.error(e);
    process.exit(1);
});
