"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveReleaseBuildPreflight = resolveReleaseBuildPreflight;
exports.runReleaseBuildPreflight = runReleaseBuildPreflight;
exports.createReleaseBuildPreflightSteps = createReleaseBuildPreflightSteps;
exports.inspectReleaseTestReceipt = inspectReleaseTestReceipt;
exports.isDefaultV2TestScript = isDefaultV2TestScript;
exports.createExecutedTestReceiptEvidence = createExecutedTestReceiptEvidence;
exports.createInvalidTestReceiptEvidence = createInvalidTestReceiptEvidence;
exports.createSkippedTestReceiptEvidence = createSkippedTestReceiptEvidence;
exports.createNotApplicableTestReceiptEvidence = createNotApplicableTestReceiptEvidence;
exports.runPackageScript = runPackageScript;
exports.readDistPayloadEvidence = readDistPayloadEvidence;
exports.collectFiles = collectFiles;
exports.assertFreshDistPayload = assertFreshDistPayload;
exports.createSkippedDistEvidence = createSkippedDistEvidence;
exports.createNotApplicableDistEvidence = createNotApplicableDistEvidence;
// Responsibility-focused extraction from index-build-preflight.ts.
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
const node_module_1 = require("node:module");
const errors_1 = require("../../../lib/errors");
const build_input_manifest_1 = require("../../../lib/runtime/build-input-manifest");
const index_build_preflight_scripts_1 = require("./index-build-preflight-scripts");
function resolveReleaseBuildPreflight(options) {
    if (options.releaseSubcommand.name !== 'build') {
        return {
            required: false,
            status: 'not-applicable',
            steps: [],
            testReceipt: createNotApplicableTestReceiptEvidence(),
            dist: createNotApplicableDistEvidence(),
        };
    }
    if (options.dryRun) {
        return {
            required: true,
            status: 'skipped-dry-run',
            steps: createReleaseBuildPreflightSteps().map((step) => ({
                ...step,
                status: 'skipped-dry-run',
            })),
            testReceipt: createSkippedTestReceiptEvidence(),
            dist: createSkippedDistEvidence(),
        };
    }
    return runReleaseBuildPreflight({
        repositoryRoot: options.repositoryRoot,
    });
}
function runReleaseBuildPreflight(options) {
    const completedSteps = [];
    let testReceipt = createNotApplicableTestReceiptEvidence();
    for (const step of createReleaseBuildPreflightSteps()) {
        try {
            if (step.name === 'repository tests') {
                const before = inspectReleaseTestReceipt(options.repositoryRoot);
                if (before.applicable && before.evidence.receiptStatus === 'complete') {
                    testReceipt = {
                        ...before.evidence,
                        status: 'reused',
                        suiteExecutions: 0,
                        reason: 'verified-complete-receipt',
                    };
                }
                else {
                    runPackageScript(options.repositoryRoot, step.scriptName);
                    const after = inspectReleaseTestReceipt(options.repositoryRoot);
                    if (before.applicable && after.evidence.receiptStatus !== 'complete') {
                        throw new Error('Configured V2 suite did not produce a verified complete current receipt.');
                    }
                    testReceipt = before.applicable
                        ? createExecutedTestReceiptEvidence(before.evidence, after.evidence)
                        : {
                            ...before.evidence,
                            status: 'executed',
                            suiteExecutions: 1,
                            reason: 'non-v2-test-script',
                        };
                }
            }
            else {
                runPackageScript(options.repositoryRoot, step.scriptName);
            }
            completedSteps.push({
                ...step,
                status: 'passed',
            });
        }
        catch (error) {
            throw (0, errors_1.createUsageError)(`Release build preflight failed during ${step.name}.`, [
                `Command: ${step.command.join(' ')}`,
                `Reason: ${step.name} failed`,
                `Recovery: fix the failing ${step.name} command and rerun release build.`,
                'Exit behavior: exits before generated release output directories or artifacts are created.',
                ...(0, index_build_preflight_scripts_1.formatPreflightFailureOutput)(error),
            ]);
        }
    }
    const distAfterBuild = readDistPayloadEvidence(options.repositoryRoot, 'fresh');
    assertFreshDistPayload(distAfterBuild);
    return {
        required: true,
        status: 'passed',
        steps: completedSteps,
        testReceipt,
        dist: distAfterBuild,
    };
}
function createReleaseBuildPreflightSteps() {
    return [
        {
            name: 'repository verification',
            scriptName: 'check',
            command: ['pnpm', 'run', 'check'],
            status: 'passed',
        },
        {
            name: 'repository tests',
            scriptName: 'test',
            command: ['pnpm', 'run', 'test'],
            status: 'passed',
        },
        {
            name: 'production build',
            scriptName: 'build',
            command: ['pnpm', 'run', 'build'],
            status: 'passed',
        },
    ];
}
function inspectReleaseTestReceipt(repositoryRoot) {
    const packageJson = JSON.parse(node_fs_1.default.readFileSync(node_path_1.default.join(repositoryRoot, 'package.json'), 'utf8'));
    const testScript = packageJson.scripts?.test;
    if (typeof testScript !== 'string' || !isDefaultV2TestScript(testScript)) {
        return { applicable: false, evidence: createNotApplicableTestReceiptEvidence() };
    }
    const testsRoot = node_path_1.default.join(repositoryRoot, 'tests_v2');
    const testFiles = node_fs_1.default.existsSync(testsRoot)
        ? collectFiles(testsRoot)
            .filter((filePath) => filePath.endsWith('.test.js'))
            .map((filePath) => node_path_1.default.relative(repositoryRoot, filePath).split(node_path_1.default.sep).join('/'))
            .sort((left, right) => left.localeCompare(right, 'en'))
        : [];
    const authorityPath = node_path_1.default.join(repositoryRoot, 'scripts', 'lib', 'v2-test-receipts.js');
    if (testFiles.length === 0 || !node_fs_1.default.existsSync(authorityPath)) {
        return {
            applicable: true,
            evidence: createInvalidTestReceiptEvidence(testFiles.length, 'receipt-authority-unavailable'),
        };
    }
    try {
        const authority = (0, node_module_1.createRequire)(node_path_1.default.join(repositoryRoot, 'package.json'))(authorityPath);
        const result = authority.verifyCompleteReceipt({
            repositoryRoot,
            testFiles,
            scope: { label: 'tests_v2' },
        });
        return {
            applicable: true,
            evidence: {
                status: 'not-applicable',
                receiptStatus: result.inspection.status,
                suiteIdentity: result.context.suiteIdentity,
                suiteExecutions: 0,
                reason: `receipt-${result.inspection.status}`,
                counters: { ...result.inspection.counters },
            },
        };
    }
    catch {
        return {
            applicable: true,
            evidence: createInvalidTestReceiptEvidence(testFiles.length, 'receipt-verification-failed'),
        };
    }
}
function isDefaultV2TestScript(script) {
    const commands = (0, index_build_preflight_scripts_1.splitPackageScriptCommands)(script);
    if (commands.length !== 1)
        return false;
    const args = (0, index_build_preflight_scripts_1.tokenizePackageScriptCommand)(commands[0]);
    if (args[0] !== 'node' || args[1]?.split(node_path_1.default.sep).join('/') !== 'scripts/run-v2-test-files.js')
        return false;
    return args.slice(2).every((arg) => ['--resume', '--keep-sandbox', '--verbose'].includes(arg));
}
function createExecutedTestReceiptEvidence(before, after) {
    const selected = after.counters.selected || before.counters.selected;
    return {
        status: 'executed',
        receiptStatus: after.receiptStatus,
        suiteIdentity: after.suiteIdentity,
        suiteExecutions: 1,
        reason: `recovered-from-${before.receiptStatus}`,
        counters: {
            selected,
            reused: before.counters.reused,
            executed: Math.max(0, selected - before.counters.reused),
            invalidated: before.counters.invalidated,
            remaining: after.counters.remaining,
        },
    };
}
function createInvalidTestReceiptEvidence(selected, reason) {
    return {
        status: 'not-applicable',
        receiptStatus: 'invalid',
        suiteIdentity: null,
        suiteExecutions: 0,
        reason,
        counters: { selected, reused: 0, executed: 0, invalidated: 0, remaining: selected },
    };
}
function createSkippedTestReceiptEvidence() {
    return {
        ...createNotApplicableTestReceiptEvidence(),
        status: 'skipped-dry-run',
        reason: 'release-dry-run',
    };
}
function createNotApplicableTestReceiptEvidence() {
    return {
        status: 'not-applicable',
        receiptStatus: 'not-applicable',
        suiteIdentity: null,
        suiteExecutions: 0,
        reason: 'not-applicable',
        counters: { selected: 0, reused: 0, executed: 0, invalidated: 0, remaining: 0 },
    };
}
function runPackageScript(repositoryRoot, scriptName, seenScripts = []) {
    if (seenScripts.includes(scriptName)) {
        throw new Error(`Recursive package script invocation: ${[...seenScripts, scriptName].join(' -> ')}`);
    }
    const packageJson = JSON.parse(node_fs_1.default.readFileSync(node_path_1.default.join(repositoryRoot, 'package.json'), 'utf8'));
    const script = packageJson.scripts?.[scriptName];
    if (typeof script !== 'string' || script.trim().length === 0) {
        throw new Error(`Missing package script: ${scriptName}`);
    }
    for (const command of (0, index_build_preflight_scripts_1.splitPackageScriptCommands)(script)) {
        (0, index_build_preflight_scripts_1.runPackageScriptCommand)(repositoryRoot, command, [...seenScripts, scriptName]);
    }
}
function readDistPayloadEvidence(repositoryRoot, fallbackStatus) {
    const distPath = node_path_1.default.join(repositoryRoot, 'dist');
    const requiredEntrypoint = 'dist/cli/index.js';
    if (!node_fs_1.default.existsSync(distPath) || !node_fs_1.default.statSync(distPath).isDirectory()) {
        return {
            status: 'missing',
            path: 'dist',
            requiredEntrypoint,
            requiredEntrypointPresent: false,
            fileCount: 0,
            manifestPath: null,
            manifestDigest: null,
            distributionIdentity: null,
            freshnessReason: 'dist-missing',
        };
    }
    const files = collectFiles(distPath);
    const manifest = (0, build_input_manifest_1.inspectBuildInputManifest)(repositoryRoot);
    const status = manifest.status === 'current'
        ? fallbackStatus
        : manifest.status === 'malformed' || manifest.status === 'unknown'
            ? 'incompatible'
            : manifest.status;
    return {
        status,
        path: 'dist',
        requiredEntrypoint,
        requiredEntrypointPresent: node_fs_1.default.existsSync(node_path_1.default.join(repositoryRoot, requiredEntrypoint)),
        fileCount: files.length,
        manifestPath: manifest.manifestPath,
        manifestDigest: manifest.aggregateDigest,
        distributionIdentity: manifest.distributionIdentity,
        freshnessReason: manifest.reason,
    };
}
function collectFiles(directoryPath) {
    const files = [];
    for (const entry of node_fs_1.default.readdirSync(directoryPath, { withFileTypes: true })) {
        const entryPath = node_path_1.default.join(directoryPath, entry.name);
        if (entry.isDirectory()) {
            files.push(...collectFiles(entryPath));
        }
        else if (entry.isFile()) {
            files.push(entryPath);
        }
    }
    return files;
}
function assertFreshDistPayload(evidence) {
    if (evidence.fileCount === 0) {
        evidence.status = 'missing';
        throw (0, errors_1.createUsageError)('Release build dist payload is missing after production build.', [
            'Reason: missing-dist',
            'Recovery: fix the production build so it emits compiled files under dist/.',
            'Exit behavior: exits before generated release output directories or artifacts are created.',
        ]);
    }
    if (!evidence.requiredEntrypointPresent) {
        evidence.status = 'incompatible';
        throw (0, errors_1.createUsageError)('Release build dist payload is inconsistent after production build.', [
            'Reason: dist-entrypoint-missing',
            `Required entrypoint: ${evidence.requiredEntrypoint}`,
            'Recovery: fix the production build so dist/ contains the compiled CLI entrypoint.',
            'Exit behavior: exits before generated release output directories or artifacts are created.',
        ]);
    }
    if (evidence.status !== 'fresh') {
        throw (0, errors_1.createUsageError)(`Release build dist payload is ${evidence.status} after production build.`, [
            `Reason: ${evidence.freshnessReason ?? 'build-input-manifest-unverifiable'}`,
            `Manifest: ${evidence.manifestPath ?? '(none)'}`,
            `Manifest digest: ${evidence.manifestDigest ?? '(none)'}`,
            'Recovery: run the production build that emits a manifest matching current inputs and compiled dist output.',
            'Exit behavior: exits before generated release output directories or artifacts are created.',
        ]);
    }
}
function createSkippedDistEvidence() {
    return {
        status: 'skipped-dry-run',
        path: 'dist',
        requiredEntrypoint: 'dist/cli/index.js',
        requiredEntrypointPresent: false,
        fileCount: 0,
        manifestPath: null,
        manifestDigest: null,
        distributionIdentity: null,
        freshnessReason: null,
    };
}
function createNotApplicableDistEvidence() {
    return {
        status: 'not-applicable',
        path: null,
        requiredEntrypoint: null,
        requiredEntrypointPresent: false,
        fileCount: 0,
        manifestPath: null,
        manifestDigest: null,
        distributionIdentity: null,
        freshnessReason: null,
    };
}
