"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.HISTORICAL_COMPATIBILITY_DISPOSITION_CODES = exports.HISTORICAL_COMPATIBILITY_DISPOSITION_PATH = exports.HISTORICAL_COMPATIBILITY_DISPOSITION_PROTOCOL_VERSION = exports.HISTORICAL_COMPATIBILITY_DISPOSITION_SCHEMA_VERSION = void 0;
exports.encodeHistoricalCompatibilityDisposition = encodeHistoricalCompatibilityDisposition;
exports.loadHistoricalCompatibilityDispositionSync = loadHistoricalCompatibilityDispositionSync;
const node_crypto_1 = require("node:crypto");
const node_fs_1 = __importDefault(require("node:fs"));
const node_zlib_1 = require("node:zlib");
const mutation_preconditions_1 = require("../filesystem/mutation-preconditions");
const paths_1 = require("../paths");
const canonical_write_target_1 = require("../paths/canonical-write-target");
exports.HISTORICAL_COMPATIBILITY_DISPOSITION_SCHEMA_VERSION = 1;
exports.HISTORICAL_COMPATIBILITY_DISPOSITION_PROTOCOL_VERSION = 1;
exports.HISTORICAL_COMPATIBILITY_DISPOSITION_PATH = ".day-shift/state/historical-planning-compatibility-disposition.json";
const MAX_DISPOSITION_BYTES = 2 * 1024 * 1024;
const MAX_DECOMPRESSED_BYTES = 8 * 1024 * 1024;
exports.HISTORICAL_COMPATIBILITY_DISPOSITION_CODES = Object.freeze([
    "lifecycle-incomplete-summary-completion-audit",
    "lifecycle-invalid-completed-task-state",
    "lifecycle-missing-readiness-authorization",
    "lifecycle-stale-review-repair-authorization",
    "lifecycle-unsupported-lifecycle-token",
    "policy-missing-evidence-profile",
    "policy-unsupported-lifecycle-token"
]);
function encodeHistoricalCompatibilityDisposition(options) {
    const createdAt = new Date(options.createdAt);
    if (Number.isNaN(createdAt.getTime())) {
        throw new TypeError("Historical compatibility disposition requires a valid creation timestamp.");
    }
    if (options.createdBy.trim() === "" || options.reason.trim() === "") {
        throw new TypeError("Historical compatibility disposition requires actor and reason evidence.");
    }
    const entries = normalizeEntries(options.entries);
    const payload = {
        schemaVersion: exports.HISTORICAL_COMPATIBILITY_DISPOSITION_SCHEMA_VERSION,
        entries
    };
    const payloadText = JSON.stringify(payload);
    const payloadDigest = `sha256:${sha256(payloadText)}`;
    const compressed = (0, node_zlib_1.brotliCompressSync)(Buffer.from(payloadText, "utf8"), {
        params: {
            [node_zlib_1.constants.BROTLI_PARAM_MODE]: node_zlib_1.constants.BROTLI_MODE_TEXT,
            [node_zlib_1.constants.BROTLI_PARAM_QUALITY]: node_zlib_1.constants.BROTLI_MAX_QUALITY
        }
    });
    const dispositionId = `historical-compatibility-disposition-${sha256(JSON.stringify({
        payloadDigest,
        createdAt: createdAt.toISOString(),
        createdBy: options.createdBy,
        reason: options.reason
    })).slice(0, 24)}`;
    const document = {
        schemaVersion: exports.HISTORICAL_COMPATIBILITY_DISPOSITION_SCHEMA_VERSION,
        protocolVersion: exports.HISTORICAL_COMPATIBILITY_DISPOSITION_PROTOCOL_VERSION,
        dispositionKind: "historical-planning-compatibility",
        dispositionId,
        createdAt: createdAt.toISOString(),
        createdBy: options.createdBy,
        reason: options.reason,
        scope: ".day-shift/planning/governed",
        effect: "aggregate-compatibility-only",
        taskLifecycleAuthority: "none",
        entryCount: entries.length,
        uniquePathCount: new Set(entries.map((entry) => entry.path)).size,
        gapCounts: Object.fromEntries(Object.entries(options.gapCounts)
            .sort(([left], [right]) => left.localeCompare(right))),
        payloadEncoding: "brotli-base64",
        payloadDigest,
        payloadBase64: compressed.toString("base64")
    };
    return { document, contents: `${JSON.stringify(document, null, 2)}\n` };
}
function loadHistoricalCompatibilityDispositionSync(repositoryRoot, options = {}) {
    if (options.enabled === false)
        return absentMatcher();
    const target = (0, canonical_write_target_1.resolveCanonicalWriteTargetSync)(repositoryRoot, exports.HISTORICAL_COMPATIBILITY_DISPOSITION_PATH);
    const stats = node_fs_1.default.lstatSync(target.runtimePath, { throwIfNoEntry: false });
    if (stats === undefined)
        return absentMatcher();
    if (!stats.isFile() || stats.isSymbolicLink() || stats.size <= 0 || stats.size > MAX_DISPOSITION_BYTES) {
        return invalidMatcher("Disposition storage must be a bounded regular repository-local file.");
    }
    try {
        const document = parseDocument(JSON.parse(node_fs_1.default.readFileSync(target.runtimePath, "utf8")));
        const compressed = Buffer.from(document.payloadBase64, "base64");
        const payloadBytes = document.payloadEncoding === "brotli-base64"
            ? (0, node_zlib_1.brotliDecompressSync)(compressed, { maxOutputLength: MAX_DECOMPRESSED_BYTES })
            : (0, node_zlib_1.gunzipSync)(compressed, { maxOutputLength: MAX_DECOMPRESSED_BYTES });
        const payloadText = payloadBytes.toString("utf8");
        if (`sha256:${sha256(payloadText)}` !== document.payloadDigest) {
            return invalidMatcher("Disposition payload digest does not match its exact entry bytes.");
        }
        const payload = parsePayload(JSON.parse(payloadText));
        if (payload.entries.length !== document.entryCount) {
            return invalidMatcher("Disposition entry count does not match its decoded payload.");
        }
        if (new Set(payload.entries.map((entry) => entry.path)).size !== document.uniquePathCount) {
            return invalidMatcher("Disposition unique-path count does not match its decoded payload.");
        }
        return activeMatcher(repositoryRoot, document, payload.entries);
    }
    catch (error) {
        return invalidMatcher(error instanceof Error ? error.message : "Disposition parsing failed.");
    }
}
function activeMatcher(repositoryRoot, document, entries) {
    const byIdentity = new Map(entries.map((entry) => [identity(entry.code, entry.path), entry]));
    const revisionCache = new Map();
    const matchedEntries = new Set();
    const matchedGaps = new Set();
    const currentRevision = (repositoryPath) => {
        if (revisionCache.has(repositoryPath))
            return revisionCache.get(repositoryPath) ?? null;
        let revision = null;
        try {
            const target = (0, canonical_write_target_1.resolveCanonicalWriteTargetSync)(repositoryRoot, repositoryPath);
            const stats = node_fs_1.default.lstatSync(target.runtimePath, { throwIfNoEntry: false });
            if (stats?.isFile() === true && !stats.isSymbolicLink()) {
                revision = (0, mutation_preconditions_1.computeContentRevisionSync)(target.runtimePath);
            }
        }
        catch {
            revision = null;
        }
        revisionCache.set(repositoryPath, revision);
        return revision;
    };
    return {
        matches(code, paths) {
            if (!isSupportedCode(code) || paths.length === 0)
                return false;
            const matched = paths.every((repositoryPath) => {
                const key = identity(code, repositoryPath);
                const entry = byIdentity.get(key);
                if (entry === undefined || currentRevision(repositoryPath) !== entry.contentRevision) {
                    return false;
                }
                return true;
            });
            if (!matched)
                return false;
            matchedGaps.add(`${code}\0${[...paths].sort().join("\0")}`);
            for (const repositoryPath of paths)
                matchedEntries.add(identity(code, repositoryPath));
            return true;
        },
        summary() {
            let staleRevisionEntryCount = 0;
            for (const entry of entries) {
                if (currentRevision(entry.path) !== entry.contentRevision)
                    staleRevisionEntryCount += 1;
            }
            return {
                path: exports.HISTORICAL_COMPATIBILITY_DISPOSITION_PATH,
                status: "active",
                dispositionId: document.dispositionId,
                entryCount: entries.length,
                uniquePathCount: document.uniquePathCount,
                matchedGapCount: matchedGaps.size,
                matchedEntryCount: matchedEntries.size,
                staleRevisionEntryCount,
                unmatchedEntryCount: entries.length - matchedEntries.size,
                taskLifecycleAuthority: "none",
                invalidReason: null
            };
        }
    };
}
function absentMatcher() {
    return inertMatcher("absent", null);
}
function invalidMatcher(reason) {
    return inertMatcher("invalid", reason);
}
function inertMatcher(status, invalidReason) {
    return {
        matches: () => false,
        summary: () => ({
            path: exports.HISTORICAL_COMPATIBILITY_DISPOSITION_PATH,
            status,
            dispositionId: null,
            entryCount: 0,
            uniquePathCount: 0,
            matchedGapCount: 0,
            matchedEntryCount: 0,
            staleRevisionEntryCount: 0,
            unmatchedEntryCount: 0,
            taskLifecycleAuthority: "none",
            invalidReason
        })
    };
}
function parseDocument(value) {
    if (value === null || typeof value !== "object" || Array.isArray(value)) {
        throw new TypeError("Historical compatibility disposition must be a JSON object.");
    }
    const document = value;
    if (document.schemaVersion !== exports.HISTORICAL_COMPATIBILITY_DISPOSITION_SCHEMA_VERSION
        || document.protocolVersion !== exports.HISTORICAL_COMPATIBILITY_DISPOSITION_PROTOCOL_VERSION
        || document.dispositionKind !== "historical-planning-compatibility"
        || typeof document.dispositionId !== "string"
        || !/^historical-compatibility-disposition-[a-f0-9]{24}$/u.test(document.dispositionId)
        || typeof document.createdAt !== "string"
        || Number.isNaN(new Date(document.createdAt).getTime())
        || typeof document.createdBy !== "string"
        || document.createdBy.trim() === ""
        || typeof document.reason !== "string"
        || document.reason.trim() === ""
        || document.scope !== ".day-shift/planning/governed"
        || document.effect !== "aggregate-compatibility-only"
        || document.taskLifecycleAuthority !== "none"
        || !Number.isInteger(document.entryCount)
        || (document.entryCount ?? -1) < 0
        || !Number.isInteger(document.uniquePathCount)
        || (document.uniquePathCount ?? -1) < 0
        || document.gapCounts === null
        || typeof document.gapCounts !== "object"
        || Array.isArray(document.gapCounts)
        || (document.payloadEncoding !== "gzip-base64"
            && document.payloadEncoding !== "brotli-base64")
        || typeof document.payloadDigest !== "string"
        || !/^sha256:[a-f0-9]{64}$/u.test(document.payloadDigest)
        || typeof document.payloadBase64 !== "string"
        || !/^[A-Za-z0-9+/]+={0,2}$/u.test(document.payloadBase64)) {
        throw new TypeError("Historical compatibility disposition metadata is malformed or unsupported.");
    }
    return document;
}
function parsePayload(value) {
    if (value === null || typeof value !== "object" || Array.isArray(value)) {
        throw new TypeError("Historical compatibility disposition payload must be a JSON object.");
    }
    const payload = value;
    if (payload.schemaVersion !== 1 || !Array.isArray(payload.entries)) {
        throw new TypeError("Historical compatibility disposition payload is malformed.");
    }
    return { schemaVersion: 1, entries: normalizeEntries(payload.entries) };
}
function normalizeEntries(entries) {
    const normalized = entries.map((entry) => {
        if (entry === null
            || typeof entry !== "object"
            || !isSupportedCode(entry.code)
            || typeof entry.path !== "string"
            || typeof entry.contentRevision !== "string"
            || !/^[a-f0-9]{64}$/u.test(entry.contentRevision)) {
            throw new TypeError("Historical compatibility disposition entry is malformed or unsupported.");
        }
        const repositoryPath = (0, paths_1.parseRepositoryRelativePath)(entry.path).normalizedPath;
        if (!repositoryPath.startsWith(".day-shift/planning/governed/")
            || (!repositoryPath.endsWith("/task-definition.md")
                && !repositoryPath.endsWith("/implementation-summary.md"))) {
            throw new TypeError("Historical compatibility dispositions are limited to Governed task pairs.");
        }
        return { code: entry.code, path: repositoryPath, contentRevision: entry.contentRevision };
    }).sort((left, right) => left.code.localeCompare(right.code)
        || left.path.localeCompare(right.path)
        || left.contentRevision.localeCompare(right.contentRevision));
    const identities = new Set();
    for (const entry of normalized) {
        const key = identity(entry.code, entry.path);
        if (identities.has(key)) {
            throw new TypeError(`Historical compatibility disposition duplicates '${entry.code}' at '${entry.path}'.`);
        }
        identities.add(key);
    }
    return normalized;
}
function isSupportedCode(code) {
    return exports.HISTORICAL_COMPATIBILITY_DISPOSITION_CODES.includes(code);
}
function identity(code, repositoryPath) {
    return `${code}\0${repositoryPath}`;
}
function sha256(value) {
    return (0, node_crypto_1.createHash)("sha256").update(value).digest("hex");
}
