"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.computeLifecycleContractRevisionImpact = computeLifecycleContractRevisionImpact;
exports.assessLifecycleContractRevisionImpactBudget = assessLifecycleContractRevisionImpactBudget;
exports.createLifecycleContractRevisionImpactAcknowledgement = createLifecycleContractRevisionImpactAcknowledgement;
const node_crypto_1 = require("node:crypto");
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
const task_contract_impact_1 = require("../planning/task-contract-impact");
function computeLifecycleContractRevisionImpact(options) {
    const invalidatedIds = [...new Set(options.invalidatedEntryIds)].sort();
    const invalidatedIdSet = new Set(invalidatedIds);
    const entries = options.manifest.entries
        .filter((entry) => invalidatedIdSet.has(entry.entryId))
        .sort((left, right) => left.sequence - right.sequence);
    const acceptedValidations = entries.filter((entry) => entry.commandIdentity === "task validation-record"
        && readValidationStatus(options.repositoryRoot, entry) === "passed").length;
    const readinessAuthorizations = countCommitted(entries, "task readiness-authorize");
    const openAttempts = countCommitted(entries, "task attempt-open");
    const closedTasks = countCommitted(entries, "task close");
    const reconciliations = countCommitted(entries, "reconciliation accept");
    const downstreamArtifacts = inspectDeclaredDownstreamArtifacts({
        repositoryRoot: options.repositoryRoot,
        producerPaths: [
            options.manifest.phasePath,
            options.manifest.activeArtifactPath,
            ...entries.map((entry) => entry.artifactPath)
        ]
    });
    const evidenceBytes = entries.reduce((total, entry) => total + entry.evidenceBytes, 0);
    const digestDocument = {
        schemaVersion: 1,
        invalidatedEntryIds: invalidatedIds,
        counts: {
            affectedManifestEntries: entries.length,
            acceptedValidations,
            readinessAuthorizations,
            openAttempts,
            closedTasks,
            reconciliations,
            downstreamArtifacts: downstreamArtifacts.paths.length,
            unknownRelationships: downstreamArtifacts.unknownProducerPaths.length
        },
        evidenceBytes,
        downstreamArtifacts
    };
    return {
        ...digestDocument,
        impactDigest: sha256(JSON.stringify(digestDocument))
    };
}
function assessLifecycleContractRevisionImpactBudget(options) {
    const candidates = [
        {
            key: "max_invalidated_entries",
            actual: options.impact.counts.affectedManifestEntries,
            threshold: options.budget.thresholds.maxInvalidatedEntries
        },
        {
            key: "max_evidence_bytes",
            actual: options.impact.evidenceBytes,
            threshold: options.budget.thresholds.maxEvidenceBytes
        }
    ];
    const exceededThresholds = candidates
        .filter((candidate) => candidate.actual > candidate.threshold.value)
        .map((candidate) => ({
        key: candidate.key,
        actual: candidate.actual,
        limit: candidate.threshold.value,
        sourceKind: candidate.threshold.sourceKind
    }));
    return {
        schemaVersion: 1,
        budget: options.budget,
        overBudget: exceededThresholds.length > 0,
        exceededThresholds
    };
}
function createLifecycleContractRevisionImpactAcknowledgement(options) {
    const binding = {
        runId: options.runId,
        oldPhaseHash: options.oldPhaseHash,
        newPhaseHash: options.newPhaseHash,
        oldActiveArtifactHash: options.oldActiveArtifactHash,
        newActiveArtifactHash: options.newActiveArtifactHash,
        reviewReceiptId: options.reviewReceiptId,
        impactDigest: options.impactDigest,
        impactBudgetDigest: sha256(JSON.stringify(options.impactBudget)),
        actor: options.actor,
        recordedAt: options.recordedAt
    };
    const acknowledgementId = `lifecycle-impact-acknowledgement-${sha256(JSON.stringify({ schemaVersion: 1, ...binding })).slice("sha256:".length, "sha256:".length + 24)}`;
    const providedAcknowledgement = options.providedAcknowledgement?.trim() || null;
    const satisfied = !options.required || providedAcknowledgement === acknowledgementId;
    return {
        schemaVersion: 1,
        acknowledgementId,
        required: options.required,
        satisfied,
        status: !options.required
            ? "not-required"
            : providedAcknowledgement === null
                ? "required"
                : satisfied
                    ? "accepted"
                    : "stale",
        providedAcknowledgement,
        binding
    };
}
function countCommitted(entries, commandIdentity) {
    return entries.filter((entry) => entry.commandIdentity === commandIdentity
        && entry.status === "ok"
        && entry.writeOutcome === "committed").length;
}
function readValidationStatus(repositoryRoot, entry) {
    try {
        const document = JSON.parse(node_fs_1.default.readFileSync(node_path_1.default.join(repositoryRoot, entry.evidencePath), "utf8"));
        const envelope = record(document.result) ?? document;
        const data = record(envelope.data);
        const validation = record(data?.validation);
        return typeof validation?.status === "string" ? validation.status : null;
    }
    catch {
        return null;
    }
}
function inspectDeclaredDownstreamArtifacts(options) {
    const contractAbsolute = node_path_1.default.join(options.repositoryRoot, task_contract_impact_1.TASK_CONTRACT_IMPACT_PATH);
    if (!node_fs_1.default.existsSync(contractAbsolute)) {
        return {
            contractPath: task_contract_impact_1.TASK_CONTRACT_IMPACT_PATH,
            contractFingerprint: sha256("absent"),
            contractState: "absent",
            relationshipIds: [],
            paths: [],
            unknownProducerPaths: unique(options.producerPaths),
            issues: ["The declared transitive contract map is absent; affected producer relationships are unknown."]
        };
    }
    const bytes = node_fs_1.default.readFileSync(contractAbsolute);
    let raw;
    try {
        raw = JSON.parse(bytes.toString("utf8"));
    }
    catch (error) {
        return {
            contractPath: task_contract_impact_1.TASK_CONTRACT_IMPACT_PATH,
            contractFingerprint: sha256(bytes),
            contractState: "invalid",
            relationshipIds: [],
            paths: [],
            unknownProducerPaths: unique(options.producerPaths),
            issues: [error instanceof Error ? error.message : "The contract map is invalid JSON."]
        };
    }
    const parsed = (0, task_contract_impact_1.parseTaskContractImpact)(raw);
    if (parsed.contract === null) {
        return {
            contractPath: task_contract_impact_1.TASK_CONTRACT_IMPACT_PATH,
            contractFingerprint: sha256(bytes),
            contractState: "invalid",
            relationshipIds: [],
            paths: [],
            unknownProducerPaths: unique(options.producerPaths),
            issues: [...parsed.issues]
        };
    }
    const producerPaths = unique(options.producerPaths);
    const relationships = parsed.contract.relationships.filter((relationship) => producerPaths.includes(relationship.producerPath));
    const provenProducers = new Set(relationships.map((relationship) => relationship.producerPath));
    return {
        contractPath: task_contract_impact_1.TASK_CONTRACT_IMPACT_PATH,
        contractFingerprint: sha256(bytes),
        contractState: "valid",
        relationshipIds: relationships.map((relationship) => relationship.id).sort(),
        paths: unique(relationships.flatMap((relationship) => relationship.consumers.map((consumer) => consumer.path))),
        unknownProducerPaths: producerPaths.filter((producerPath) => !provenProducers.has(producerPath)),
        issues: []
    };
}
function unique(values) {
    return [...new Set(values)].sort((left, right) => left.localeCompare(right));
}
function record(value) {
    return typeof value === "object" && value !== null && !Array.isArray(value)
        ? value
        : null;
}
function sha256(value) {
    return `sha256:${(0, node_crypto_1.createHash)("sha256").update(value).digest("hex")}`;
}
