"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.runDomindsAppJsonViaNpx = runDomindsAppJsonViaNpx;
exports.runDomindsAppJsonViaLocalPackage = runDomindsAppJsonViaLocalPackage;
const child_process_1 = require("child_process");
const promises_1 = __importDefault(require("fs/promises"));
const path_1 = __importDefault(require("path"));
const util_1 = require("util");
const app_json_1 = require("@longrun-ai/kernel/app-json");
const execFileAsync = (0, util_1.promisify)(child_process_1.execFile);
function isRecord(v) {
    return typeof v === 'object' && v !== null && !Array.isArray(v);
}
function asString(v) {
    return typeof v === 'string' ? v : null;
}
async function readPackageJson(packageRootAbs) {
    const raw = await promises_1.default.readFile(path_1.default.resolve(packageRootAbs, 'package.json'), 'utf-8');
    return JSON.parse(raw);
}
async function resolveLocalBinScriptAbs(packageRootAbs) {
    const pkg = await readPackageJson(packageRootAbs);
    if (!isRecord(pkg))
        throw new Error(`Invalid package.json: expected object (${packageRootAbs})`);
    const bin = pkg['bin'];
    if (typeof bin === 'string') {
        return path_1.default.resolve(packageRootAbs, bin);
    }
    if (isRecord(bin)) {
        const entries = Object.entries(bin)
            .filter(([, v]) => typeof v === 'string' && v.trim() !== '')
            .map(([k, v]) => ({ key: k, rel: v }));
        if (entries.length === 0)
            throw new Error(`Invalid package.json: bin has no entries (${packageRootAbs})`);
        // Prefer an entry matching package name segment when possible.
        const name = asString(pkg['name']);
        const preferredKey = name ? (name.split('/').pop() ?? name) : null;
        const found = preferredKey ? entries.find((e) => e.key === preferredKey) : undefined;
        return path_1.default.resolve(packageRootAbs, (found ?? entries[0]).rel);
    }
    throw new Error(`Invalid package.json: bin must be string|object (${packageRootAbs})`);
}
function parseAppJsonFromStdout(stdout, where) {
    const trimmed = stdout.trim();
    if (trimmed === '') {
        throw new Error(`App did not print JSON to stdout (${where})`);
    }
    let parsed;
    try {
        parsed = JSON.parse(trimmed);
    }
    catch (err) {
        throw new Error(`Failed to parse app JSON from stdout (${where}): ${err instanceof Error ? err.message : String(err)}\nstdout=${trimmed}`);
    }
    const res = (0, app_json_1.parseDomindsAppInstallJson)(parsed);
    if (!res.ok)
        throw new Error(`Invalid app JSON (${where}): ${res.errorText}`);
    return res.json;
}
async function runDomindsAppJsonViaNpx(params) {
    const { stdout, stderr } = await execFileAsync('npx', ['-y', params.spec, '--dominds-app'], {
        cwd: params.cwdAbs,
        env: process.env,
        maxBuffer: 10 * 1024 * 1024,
    });
    if (stderr.trim() !== '') {
        // Loud: stderr indicates non-JSON noise that would break parsing determinism.
        throw new Error(`App printed to stderr during --dominds-app handshake (spec=${params.spec}):\n${stderr}`);
    }
    return parseAppJsonFromStdout(stdout, `npx ${params.spec} --dominds-app`);
}
async function runDomindsAppJsonViaLocalPackage(params) {
    const scriptAbs = await resolveLocalBinScriptAbs(params.packageRootAbs);
    const { stdout, stderr } = await execFileAsync(process.execPath, [scriptAbs, '--dominds-app'], {
        cwd: params.packageRootAbs,
        env: process.env,
        maxBuffer: 10 * 1024 * 1024,
    });
    if (stderr.trim() !== '') {
        throw new Error(`Local app printed to stderr during --dominds-app handshake (path=${params.packageRootAbs}):\n${stderr}`);
    }
    return parseAppJsonFromStdout(stdout, `local ${params.packageRootAbs} --dominds-app`);
}
