import { isTrustedLedgerUnit } from "../pipeline-execution-ledger.js";
function normalizeRequestedTargetUnitId(unitId) {
    if (unitId.startsWith("lens:"))
        return unitId.slice("lens:".length);
    if (unitId.startsWith("deliberation:")) {
        return `deliberation-${unitId.slice("deliberation:".length)}`;
    }
    if (unitId.startsWith("issue-artifact:")) {
        return unitId.slice("issue-artifact:".length);
    }
    if (unitId === "controlled-lens-deliberation") {
        return "controlled-deliberation";
    }
    return unitId;
}
function lensIdFromUnit(unit) {
    if (unit.unitKind === "lens")
        return unit.unitId;
    if (unit.unitId.startsWith("deliberation-")) {
        return unit.unitId.slice("deliberation-".length) || null;
    }
    return null;
}
function toContinuationUnit(unit, dispatchDecision, reason) {
    const lensId = lensIdFromUnit(unit);
    return {
        unitId: unit.unitId,
        unitKind: unit.unitKind,
        ...(lensId !== null ? { lensId } : {}),
        packetPath: unit.packetRef ?? null,
        outputPath: unit.outputRefs[0] ?? null,
        priorStatus: unit.status,
        dispatchDecision,
        reason,
    };
}
function trustedUnitIds(ledger) {
    return new Set(ledger.units
        .filter((unit) => isTrustedLedgerUnit(unit))
        .map((unit) => unit.unitId));
}
function isFrontierUnit(unit, trustedIds) {
    if (isTrustedLedgerUnit(unit))
        return false;
    return unit.upstreamUnitIds.every((unitId) => trustedIds.has(unitId));
}
function reachableDownstreamUnitIds(args) {
    const byId = new Map(args.ledger.units.map((unit) => [unit.unitId, unit]));
    const result = new Set();
    const queue = [...args.frontierUnitIds];
    while (queue.length > 0) {
        const unitId = queue.shift();
        if (!unitId)
            continue;
        const unit = byId.get(unitId);
        if (!unit)
            continue;
        for (const downstreamUnitId of unit.downstreamUnitIds) {
            if (args.frontierUnitIds.has(downstreamUnitId))
                continue;
            if (result.has(downstreamUnitId))
                continue;
            result.add(downstreamUnitId);
            queue.push(downstreamUnitId);
        }
    }
    return result;
}
function targetRejectedUnits(args) {
    const byId = new Map(args.ledger.units.map((unit) => [unit.unitId, unit]));
    return args.targetUnits.flatMap((unitId) => {
        const unit = byId.get(unitId);
        if (!unit) {
            return [
                {
                    unitId,
                    unitKind: "unknown",
                    packetPath: null,
                    outputPath: null,
                    priorStatus: "missing",
                    dispatchDecision: "reject",
                    reason: "Target unit is not present in the pipeline execution ledger.",
                },
            ];
        }
        if (isTrustedLedgerUnit(unit)) {
            return [
                toContinuationUnit(unit, "reject", "Target unit is already trusted and completed; completed outputs are reuse-only."),
            ];
        }
        if (!args.naturalFrontierUnitIds.has(unitId)) {
            return [
                toContinuationUnit(unit, "reject", "Target unit is not the current continuation frontier; continue from the earliest untrusted unit first."),
            ];
        }
        return [];
    });
}
export function buildReviewContinuationPlan(params) {
    const trustedIds = trustedUnitIds(params.ledger);
    const requestedTargets = (params.targetUnits ?? []).map(normalizeRequestedTargetUnitId);
    const naturalFrontier = params.ledger.units.filter((unit) => isFrontierUnit(unit, trustedIds));
    const naturalFrontierUnitIds = new Set(naturalFrontier.map((unit) => unit.unitId));
    const rejectedTargets = targetRejectedUnits({
        ledger: params.ledger,
        targetUnits: requestedTargets,
        naturalFrontierUnitIds,
    });
    const requestedTargetSet = new Set(requestedTargets);
    const missingFrontierTargets = requestedTargetSet.size === 0 || rejectedTargets.length > 0
        ? []
        : naturalFrontier.filter((unit) => !requestedTargetSet.has(unit.unitId));
    const rejectedMissingFrontierTargets = missingFrontierTargets.map((unit) => toContinuationUnit(unit, "reject", "Target units must match the current continuation frontier so no untrusted sibling unit is skipped."));
    const allRejectedTargets = [...rejectedTargets, ...rejectedMissingFrontierTargets];
    const targetFrontier = requestedTargetSet.size === 0
        ? naturalFrontier
        : params.ledger.units.filter((unit) => requestedTargetSet.has(unit.unitId) && !isTrustedLedgerUnit(unit));
    const frontierUnits = allRejectedTargets.length > 0
        ? allRejectedTargets
        : targetFrontier.map((unit) => toContinuationUnit(unit, "run", requestedTargetSet.size === 0
            ? "Earliest untrusted unit whose upstream units are trusted."
            : "Requested untrusted target unit."));
    const frontierUnitIds = new Set(frontierUnits
        .filter((unit) => unit.dispatchDecision === "run")
        .map((unit) => unit.unitId));
    const downstreamIds = reachableDownstreamUnitIds({
        ledger: params.ledger,
        frontierUnitIds,
    });
    const downstreamUnits = params.ledger.units
        .filter((unit) => downstreamIds.has(unit.unitId) && !isTrustedLedgerUnit(unit))
        .map((unit) => toContinuationUnit(unit, "run", "Downstream unit must be recomputed after the continuation frontier."));
    const preservedArtifactRefs = params.ledger.units
        .filter((unit) => isTrustedLedgerUnit(unit))
        .flatMap((unit) => unit.producedArtifactRefs);
    const supersededArtifactRefs = [...frontierUnits, ...downstreamUnits]
        .filter((unit) => unit.dispatchDecision === "run" && unit.outputPath !== null)
        .map((unit) => unit.outputPath)
        .filter((outputPath) => preservedArtifactRefs.includes(outputPath) === false);
    const eligible = allRejectedTargets.length === 0 &&
        (frontierUnits.length > 0 || downstreamUnits.length > 0);
    const ineligibleReason = eligible
        ? null
        : allRejectedTargets.length > 0
            ? "One or more requested target units cannot be continued."
            : "No untrusted continuation frontier remains.";
    return {
        schemaVersion: "1",
        sessionId: params.ledger.sessionId,
        eligible,
        ineligibleReason,
        sourceRefs: params.ledger.sourceRefs,
        validationRefs: params.ledger.sourceRefs,
        unitLedger: params.ledger,
        frontierUnits,
        downstreamUnits,
        preservedArtifactRefs,
        supersededArtifactRefs: [...new Set(supersededArtifactRefs)].sort(),
    };
}
