"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.planTaskCycleRuntimeBaseline = planTaskCycleRuntimeBaseline;
exports.readTaskCycleRuntimeBaseline = readTaskCycleRuntimeBaseline;
exports.readAttemptRuntimeBaseline = readAttemptRuntimeBaseline;
exports.readPreferredRuntimeBaseline = readPreferredRuntimeBaseline;
exports.readEligibleRuntimeTargets = readEligibleRuntimeTargets;
const node_crypto_1 = require("node:crypto");
const errors_1 = require("../../errors");
const planning_document_utils_1 = require("../planning-document-utils");
const task_lifecycle_1 = require("../task-lifecycle");
const baseline_context_1 = require("./baseline-context");
const contracts_1 = require("./contracts");
const predecessor_evidence_1 = require("./predecessor-evidence");
const repository_support_1 = require("./repository-support");
const serialization_1 = require("./serialization");
const supplemental_baseline_1 = require("./supplemental-baseline");
const target_inspection_1 = require("./target-inspection");
function planTaskCycleRuntimeBaseline(repositoryRoot, options) {
    const context = (0, baseline_context_1.resolveTaskBaselineContext)(repositoryRoot, options);
    if (context.attemptBaseline !== null) {
        throw (0, errors_1.createUsageError)(`Current attempt '${context.currentAttemptId}' already records baseline '${context.attemptBaseline.baselineId}'.`, ["Use --resume with the exact stored identities; replacement planning is not supported."]);
    }
    if (context.legacyBaseline !== null) {
        throw (0, errors_1.createUsageError)(`Task definition '${context.taskDefinitionPath}' records legacy baseline '${context.legacyBaseline.baselineId}'.`, ["Use --resume with exact attempt/readiness identities to migrate it without replacement."]);
    }
    const capturedBy = (0, repository_support_1.requireActor)(options.capturedBy);
    const capturedAt = (0, repository_support_1.requireCaptureTime)(options.capturedAt, context.currentAttempt.openedAt);
    const adoptionSelections = (0, target_inspection_1.parseAdoptions)(options.adoptions ?? [], context.declaredTargetPaths);
    const chronologyRepairRationale = context.currentAttempt.successorKind === 'chronology_repair' &&
        context.currentAttempt.predecessorAttemptId !== null &&
        context.currentAttempt.predecessorChronologyRecoveryId !== null
        ? `chronology-repair-predecessor:${context.currentAttempt.predecessorAttemptId}:${context.currentAttempt.predecessorChronologyRecoveryId}`
        : null;
    const declaredPredecessors = (0, predecessor_evidence_1.readDeclaredPredecessorTaskPaths)(context.taskDefinitionPath, context.taskText);
    const predecessorPaths = (0, repository_support_1.unique)([
        ...declaredPredecessors,
        ...adoptionSelections.acceptedRecoverySources.values()
    ]);
    const boundedPredecessorPaths = predecessorPaths.slice(0, contracts_1.MAX_BASELINE_PLAN_PREDECESSOR_CANDIDATES);
    const globalBlockers = predecessorPaths.length > boundedPredecessorPaths.length
        ? [
            `Accepted-predecessor discovery found ${predecessorPaths.length} candidates, exceeding the bounded ${contracts_1.MAX_BASELINE_PLAN_PREDECESSOR_CANDIDATES}-candidate limit.`
        ]
        : [];
    const sourceRevision = (0, repository_support_1.runGit)(context.pair.repositoryRoot, ["rev-parse", "--verify", "HEAD"]);
    if (sourceRevision === null || !contracts_1.SOURCE_REVISION_PATTERN.test(sourceRevision)) {
        globalBlockers.push("Task baseline planning requires a readable Git HEAD revision.");
    }
    let statusBytes = null;
    try {
        statusBytes = (0, repository_support_1.readBoundedGitStatus)(context.pair.repositoryRoot);
    }
    catch (error) {
        globalBlockers.push((0, target_inspection_1.errorMessage)(error));
    }
    const targets = context.declaredTargetPaths.map((targetPath) => {
        let observation;
        let targetStatus;
        try {
            observation = (0, predecessor_evidence_1.inspectTarget)(context.pair.repositoryRoot, targetPath, null);
            targetStatus = (0, repository_support_1.runGit)(context.pair.repositoryRoot, ["status", "--porcelain=v1", "--untracked-files=all", "--", targetPath]);
            if (targetStatus === null) {
                throw (0, errors_1.createUsageError)(`Unable to inspect Git dirty state for declared target '${targetPath}'.`);
            }
        }
        catch (error) {
            return {
                path: targetPath,
                status: "unsupported",
                exists: null,
                kind: "unsupported",
                repositoryVisibility: null,
                ignoreRule: null,
                dirty: null,
                contentSha256: null,
                statusSha256: null,
                rationaleRequired: false,
                rationaleProvided: false,
                selectedPredecessorPath: null,
                predecessorCandidates: [],
                blockers: [(0, target_inspection_1.errorMessage)(error)],
                suggestedAdoption: null
            };
        }
        const manualRationale = adoptionSelections.manualAdoptions.get(targetPath) ?? null;
        const selectedPredecessorPath = adoptionSelections.acceptedRecoverySources.get(targetPath) ?? null;
        const candidates = observation.dirty
            ? boundedPredecessorPaths.map((sourcePath) => (0, predecessor_evidence_1.inspectAcceptedPredecessorCandidate)({
                repositoryRoot: context.pair.repositoryRoot,
                sourceTaskDefinitionPath: sourcePath,
                target: observation
            }))
            : [];
        const exactCandidates = candidates.filter((candidate) => candidate.provenance !== null);
        const selectedCandidate = selectedPredecessorPath === null
            ? null
            : candidates.find((candidate) => candidate.taskDefinitionPath === selectedPredecessorPath) ?? null;
        const rationaleProvided = chronologyRepairRationale === null
            ? manualRationale !== null || (selectedCandidate !== null && selectedCandidate.provenance !== null)
            : manualRationale === chronologyRepairRationale;
        const blockers = [];
        let status;
        let suggestedAdoption = null;
        if (!observation.dirty) {
            if (manualRationale !== null || selectedPredecessorPath !== null) {
                status = "unsupported";
                blockers.push(`Adoption path '${targetPath}' is not dirty at baseline capture.`);
            }
            else {
                status = "clean";
            }
        }
        else if (chronologyRepairRationale !== null) {
            status = 'dirty-needs-rationale';
            if (manualRationale === null) {
                suggestedAdoption = `${targetPath}::${chronologyRepairRationale}`;
                blockers.push(`Dirty chronology-repair target '${targetPath}' requires explicit attribution to predecessor attempt '${context.currentAttempt.predecessorAttemptId}'.`);
            }
            else if (manualRationale !== chronologyRepairRationale) {
                suggestedAdoption = `${targetPath}::${chronologyRepairRationale}`;
                blockers.push(`Dirty chronology-repair target '${targetPath}' must use the exact predecessor recovery attribution '${chronologyRepairRationale}'.`);
            }
        }
        else if (exactCandidates.length > 1) {
            status = "ambiguous";
            if (!rationaleProvided) {
                blockers.push(`Dirty target '${targetPath}' has multiple current exact predecessor candidates: ${exactCandidates.map((candidate) => candidate.taskDefinitionPath).join(", ")}. Select one explicitly or supply a concrete ownership rationale.`);
            }
        }
        else if (exactCandidates.length === 1) {
            status = "accepted-predecessor-candidate";
            if (!rationaleProvided) {
                suggestedAdoption = `${targetPath}::accepted-predecessor:${exactCandidates[0].taskDefinitionPath}`;
                blockers.push(`Dirty target '${targetPath}' requires explicit selection of current predecessor '${exactCandidates[0].taskDefinitionPath}' or a concrete ownership rationale.`);
            }
        }
        else if (selectedPredecessorPath !== null) {
            status = "unsupported";
            blockers.push(...(selectedCandidate?.mismatchReasons ?? [
                `Selected accepted predecessor '${selectedPredecessorPath}' was not inspected within the bounded candidate set.`
            ]));
            suggestedAdoption = `${targetPath}::<concrete ownership rationale>`;
            blockers.push(`Exact predecessor attribution remains rejected; ordinary adoption is a separate explicit recovery: --adopt '${suggestedAdoption}'.`);
        }
        else {
            status = "dirty-needs-rationale";
            if (!rationaleProvided) {
                for (const candidate of candidates) {
                    blockers.push(...candidate.mismatchReasons);
                }
                blockers.push(`Dirty target '${targetPath}' requires an explicit concrete --adopt ${targetPath}::rationale argument; no rationale was inferred.`);
            }
        }
        return {
            path: targetPath,
            status,
            exists: observation.exists,
            kind: observation.kind,
            repositoryVisibility: observation.repositoryVisibility,
            ignoreRule: observation.ignoreRule,
            dirty: observation.dirty,
            contentSha256: observation.contentSha256,
            statusSha256: `sha256:${(0, node_crypto_1.createHash)("sha256").update(targetStatus).digest("hex")}`,
            rationaleRequired: observation.dirty,
            rationaleProvided,
            selectedPredecessorPath,
            predecessorCandidates: candidates,
            blockers,
            suggestedAdoption
        };
    });
    const blockers = [
        ...globalBlockers,
        ...targets.flatMap((target) => target.blockers.map((blocker) => `${target.path}: ${blocker}`))
    ];
    const callable = blockers.length === 0;
    const canonicalAdoptions = context.declaredTargetPaths.flatMap((targetPath) => {
        const manualRationale = adoptionSelections.manualAdoptions.get(targetPath);
        if (manualRationale !== undefined)
            return [`${targetPath}::${manualRationale}`];
        const selectedPredecessor = adoptionSelections.acceptedRecoverySources.get(targetPath);
        return selectedPredecessor === undefined
            ? []
            : [`${targetPath}::accepted-predecessor:${selectedPredecessor}`];
    });
    const baseArgv = [
        "day-shift", "task", "baseline",
        "--task-definition", context.taskDefinitionPath,
        "--task-cycle-id", options.taskCycleId,
        "--checkpoint-id", options.checkpointId,
        "--captured-by", capturedBy,
        "--captured-at", capturedAt,
        ...(context.contractImpact.contractState === "valid"
            ? ["--contract-impact-fingerprint", context.contractImpact.fingerprint]
            : []),
        ...canonicalAdoptions.flatMap((adoption) => ["--adopt", adoption]),
        "--format", "json"
    ];
    const observationSeed = targets.map((target) => ({
        path: target.path,
        contentSha256: target.contentSha256,
        statusSha256: target.statusSha256,
        dirty: target.dirty,
        status: target.status
    }));
    return {
        taskDefinitionPath: context.taskDefinitionPath,
        implementationSummaryPath: context.implementationSummaryPath,
        taskCycleId: options.taskCycleId,
        checkpointId: options.checkpointId,
        readOnly: true,
        writeApplied: false,
        writeOutcome: "no-write",
        bounded: true,
        taskRevision: (0, target_inspection_1.hashFile)(context.pair.task.runtimePath),
        implementationSummaryRevision: (0, target_inspection_1.hashFile)(context.pair.summary.runtimePath),
        sourceRevision: sourceRevision !== null && contracts_1.SOURCE_REVISION_PATTERN.test(sourceRevision)
            ? sourceRevision
            : null,
        workingTreeDigest: sourceRevision !== null && contracts_1.SOURCE_REVISION_PATTERN.test(sourceRevision)
            ? (0, repository_support_1.hashCanonical)({ sourceRevision, targets: observationSeed })
            : null,
        statusDigest: statusBytes === null
            ? null
            : `sha256:${(0, node_crypto_1.createHash)("sha256").update(statusBytes).digest("hex")}`,
        targets,
        contractImpact: context.contractImpact,
        callable,
        blockers,
        baselineArgv: callable ? baseArgv : null,
        previewArgv: callable
            ? [...baseArgv.slice(0, -2), "--dry-run", ...baseArgv.slice(-2)]
            : null
    };
}
function readTaskCycleRuntimeBaseline(artifactPath, markdownText) {
    const parsed = (0, planning_document_utils_1.parseMarkdownFrontMatter)(markdownText);
    const lines = (0, serialization_1.readFrontMatterBlock)(parsed.frontMatterLines, contracts_1.BASELINE_BLOCK);
    if (lines === null)
        return null;
    const schemaVersion = Number((0, serialization_1.readScalar)(lines, "schema_version") ?? "0");
    const baselineId = (0, serialization_1.readScalar)(lines, "baseline_id") ?? "";
    const taskCycleId = (0, serialization_1.readScalar)(lines, "task_cycle_id") ?? "";
    const checkpointId = (0, serialization_1.readScalar)(lines, "checkpoint_id") ?? "";
    const sourceRevision = (0, serialization_1.readScalar)(lines, "source_revision") ?? "";
    const repositoryDirtyState = (0, serialization_1.readScalar)(lines, "repository_dirty_state") ?? "";
    const cliRuntimeSource = (0, serialization_1.readScalar)(lines, "cli_runtime_source") ?? "";
    const cliBuildIdentity = (0, serialization_1.readScalar)(lines, "cli_build_identity") ?? "";
    const capturedBy = (0, repository_support_1.nullableScalarValue)((0, serialization_1.readScalar)(lines, "captured_by"));
    const capturedAt = (0, repository_support_1.nullableScalarValue)((0, serialization_1.readScalar)(lines, "captured_at"));
    const workingTreeDigest = (0, repository_support_1.nullableScalarValue)((0, serialization_1.readScalar)(lines, "working_tree_digest"));
    const statusDigest = (0, repository_support_1.nullableScalarValue)((0, serialization_1.readScalar)(lines, "status_digest"));
    const contractImpactFingerprint = (0, repository_support_1.nullableScalarValue)((0, serialization_1.readScalar)(lines, "contract_impact_fingerprint"));
    const observationsComplete = schemaVersion === 1
        ? true
        : (0, serialization_1.readScalar)(lines, "observations_complete") === "true";
    const capturedAfterReadiness = (0, serialization_1.readScalar)(lines, "captured_after_readiness") === "true";
    const capturedBeforeImplementationWrites = (0, serialization_1.readScalar)(lines, "captured_before_implementation_writes") === "true";
    const declaredTargets = (0, serialization_1.readTargetList)(lines);
    const issues = [];
    if (schemaVersion !== 1 && schemaVersion !== 2)
        issues.push("schema_version must be 1 or 2.");
    if (!contracts_1.CONTENT_HASH_PATTERN.test(baselineId))
        issues.push("baseline_id must be a sha256 identity.");
    if (!contracts_1.IDENTITY_PATTERN.test(taskCycleId))
        issues.push("task_cycle_id is missing or invalid.");
    if (!contracts_1.IDENTITY_PATTERN.test(checkpointId))
        issues.push("checkpoint_id is missing or invalid.");
    if (!capturedAfterReadiness)
        issues.push("captured_after_readiness must be true.");
    if (!capturedBeforeImplementationWrites)
        issues.push("captured_before_implementation_writes must be true.");
    if (!contracts_1.SOURCE_REVISION_PATTERN.test(sourceRevision))
        issues.push("source_revision is missing or invalid.");
    if (repositoryDirtyState !== "clean" && repositoryDirtyState !== "dirty") {
        issues.push("repository_dirty_state must be clean or dirty.");
    }
    if (cliRuntimeSource === "" || cliBuildIdentity === "")
        issues.push("CLI runtime/build provenance is incomplete.");
    if (schemaVersion === 2) {
        if (capturedBy === null)
            issues.push("captured_by is required for schema 2.");
        if (capturedAt === null || !(0, task_lifecycle_1.isTaskLifecycleTimestamp)(capturedAt))
            issues.push("captured_at is invalid for schema 2.");
        if (workingTreeDigest === null || !contracts_1.CONTENT_HASH_PATTERN.test(workingTreeDigest))
            issues.push("working_tree_digest is invalid for schema 2.");
        if (statusDigest === null || !contracts_1.CONTENT_HASH_PATTERN.test(statusDigest))
            issues.push("status_digest is invalid for schema 2.");
        if (!observationsComplete)
            issues.push("observations_complete must be true for a capturable schema 2 baseline.");
    }
    if (declaredTargets.length === 0)
        issues.push("declared_targets must not be empty.");
    issues.push(...(0, target_inspection_1.validateTargetEvidence)(declaredTargets));
    if (issues.length > 0) {
        throw (0, errors_1.createUsageError)(`Runtime-attribution baseline is invalid at '${(0, planning_document_utils_1.toRepositoryLikePath)(artifactPath)}'.`, issues);
    }
    return {
        schemaVersion: schemaVersion,
        baselineId,
        taskCycleId,
        checkpointId,
        capturedAfterReadiness: true,
        capturedBeforeImplementationWrites: true,
        sourceRevision,
        repositoryDirtyState: repositoryDirtyState,
        cliRuntimeSource,
        cliBuildIdentity,
        capturedBy,
        capturedAt,
        workingTreeDigest,
        statusDigest,
        observationsComplete,
        contractImpactFingerprint,
        declaredTargets
    };
}
function readAttemptRuntimeBaseline(artifactPath, summaryMarkdownText, attemptId) {
    const lines = (0, planning_document_utils_1.parseMarkdownFrontMatter)(summaryMarkdownText).frontMatterLines;
    const range = (0, supplemental_baseline_1.locateAttemptRange)(lines, attemptId);
    if (range === null)
        return null;
    const line = lines.find((candidate, index) => index > range.start && index < range.end && /^ {4}runtime_attribution_baseline:\s*/.test(candidate));
    if (line === undefined)
        return null;
    const raw = line.match(/^ {4}runtime_attribution_baseline:\s*(.*)$/)?.[1] ?? "";
    let decoded;
    try {
        const encoded = JSON.parse(raw);
        if (typeof encoded !== "string")
            throw new TypeError("baseline scalar must encode JSON text");
        decoded = JSON.parse(encoded);
    }
    catch {
        throw (0, errors_1.createUsageError)(`Attempt-owned runtime baseline is malformed at '${artifactPath}'.`);
    }
    return readTaskCycleRuntimeBaseline(artifactPath, `---\n${(0, serialization_1.renderBaselineBlock)(decoded).join("\n")}\n---\n`);
}
function readPreferredRuntimeBaseline(taskDefinitionPath, taskMarkdownText, summaryMarkdownText) {
    const projection = (0, task_lifecycle_1.projectTaskLifecyclePair)({
        taskDefinition: (0, serialization_1.removeFrontMatterBlock)(taskMarkdownText, contracts_1.BASELINE_BLOCK),
        implementationSummary: summaryMarkdownText
    });
    const currentAttemptId = projection.summary?.currentAttempt?.attemptId ?? null;
    const attempt = currentAttemptId === null
        ? null
        : readAttemptRuntimeBaseline("implementation-summary.md", summaryMarkdownText, currentAttemptId);
    const legacy = readTaskCycleRuntimeBaseline(taskDefinitionPath, taskMarkdownText);
    if (attempt !== null && legacy !== null && !(0, supplemental_baseline_1.sameBaselineIdentity)(attempt, legacy)) {
        throw (0, errors_1.createUsageError)("Contradictory task-definition and attempt-owned runtime baselines were found.", ["Do not select one owner implicitly; migrate or repair through the explicit baseline command."]);
    }
    return attempt ?? legacy;
}
function readEligibleRuntimeTargets(options) {
    const projection = (0, task_lifecycle_1.projectTaskLifecyclePair)({
        taskDefinition: (0, serialization_1.removeFrontMatterBlock)(options.taskMarkdownText, contracts_1.BASELINE_BLOCK),
        implementationSummary: options.summaryMarkdownText
    });
    const attemptId = projection.summary?.currentAttempt?.attemptId ?? null;
    if (attemptId === null)
        return options.baseline.declaredTargets;
    const supplemental = (0, supplemental_baseline_1.readAttemptSupplementalRuntimeBaseline)({
        taskDefinitionPath: options.taskDefinitionPath,
        taskMarkdownText: options.taskMarkdownText,
        summaryMarkdownText: options.summaryMarkdownText,
        attemptId,
        primaryTargets: options.baseline.declaredTargets
    });
    if (supplemental === null)
        return options.baseline.declaredTargets;
    return [
        ...options.baseline.declaredTargets,
        ...supplemental.targets.map((target) => ({
            ...target,
            repositoryVisibility: target.exists ? "tracked" : "absent",
            ignoreRule: null,
            preExistingClassification: "clean",
            adoptionRationale: "",
            acceptedPredecessor: null
        }))
    ];
}
