"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.readBaselinePathMappings = readBaselinePathMappings;
exports.readPlanningValidationBaselineForRemap = readPlanningValidationBaselineForRemap;
exports.isRemappableBaselineFinding = isRemappableBaselineFinding;
exports.isRemappableBaselineInventoryFinding = isRemappableBaselineInventoryFinding;
exports.isRemappableBaselineGap = isRemappableBaselineGap;
exports.remapPlanningValidationBaseline = remapPlanningValidationBaseline;
exports.remapBaselinePlanningPath = remapBaselinePlanningPath;
exports.canonicalizeDerivedOverviewSuffix = canonicalizeDerivedOverviewSuffix;
exports.remapBaselineMessage = remapBaselineMessage;
exports.baselineFingerprint = baselineFingerprint;
const contracts_1 = require("./contracts");
const filesystem_1 = require("./filesystem");
const validation_1 = require("./validation");
function readBaselinePathMappings(value) {
    if (!Array.isArray(value)) {
        throw new contracts_1.RepositoryMigrationRehearsalError("cutover-stale", "Canonical migration record does not contain path-mapping evidence.");
    }
    const exact = new Map();
    const roots = new Map();
    for (const entry of value) {
        if (typeof entry !== "object"
            || entry === null
            || !("sourcePath" in entry)
            || !("destinationPath" in entry)
            || typeof entry.sourcePath !== "string"
            || typeof entry.destinationPath !== "string"
            || !/^\.day-shift\/planning\/\d{2}-[^/]+\//.test(entry.sourcePath)
            || !/^\.day-shift\/planning\/(?:basic|structured|governed)\//.test(entry.destinationPath)) {
            throw new contracts_1.RepositoryMigrationRehearsalError("cutover-stale", "Canonical migration record contains invalid planning path-mapping evidence.");
        }
        if (exact.has(entry.sourcePath)) {
            throw new contracts_1.RepositoryMigrationRehearsalError("cutover-stale", `Canonical migration record repeats source path '${entry.sourcePath}'.`);
        }
        const mapping = {
            sourcePath: entry.sourcePath,
            destinationPath: entry.destinationPath
        };
        exact.set(entry.sourcePath, mapping);
        const sourceRoot = entry.sourcePath.match(/^(\.day-shift\/planning\/\d{2}-[^/]+)(?:\/|$)/)?.[1];
        const destinationRoot = entry.destinationPath.match(/^(\.day-shift\/planning\/governed\/[^/]+\/slices\/[^/]+)(?:\/|$)/)?.[1] ?? entry.destinationPath.match(/^(\.day-shift\/planning\/structured\/[^/]+)(?:\/|$)/)?.[1] ?? entry.destinationPath.match(/^(\.day-shift\/planning\/basic)(?:\/|$)/)?.[1];
        if (sourceRoot === undefined || destinationRoot === undefined) {
            throw new contracts_1.RepositoryMigrationRehearsalError("cutover-stale", `Canonical migration mapping '${entry.sourcePath}' cannot establish a canonical root authority.`);
        }
        const existingRoot = roots.get(sourceRoot);
        if (existingRoot !== undefined
            && existingRoot.destinationRoot !== destinationRoot) {
            throw new contracts_1.RepositoryMigrationRehearsalError("cutover-stale", `Canonical migration mappings disagree about source root '${sourceRoot}'.`);
        }
        if (existingRoot === undefined) {
            roots.set(sourceRoot, {
                ...mapping,
                sourceRoot,
                destinationRoot
            });
        }
    }
    return { exact, roots };
}
function readPlanningValidationBaselineForRemap(text) {
    let value;
    try {
        value = JSON.parse(text);
    }
    catch {
        throw new contracts_1.RepositoryMigrationRehearsalError("cutover-blocked", "Forward-only planning validation baseline is not valid JSON.");
    }
    if (typeof value !== "object"
        || value === null
        || !("schema_version" in value)
        || value.schema_version !== 1
        || !("created_at" in value)
        || typeof value.created_at !== "string"
        || !("scope" in value)
        || value.scope !== "planning"
        || !("mode" in value)
        || value.mode !== "forward-only"
        || !("notes" in value)
        || !Array.isArray(value.notes)
        || !value.notes.every((entry) => typeof entry === "string")
        || !("findings" in value)
        || !Array.isArray(value.findings)
        || !value.findings.every(isRemappableBaselineFinding)
        || !("inventory_findings" in value)
        || !Array.isArray(value.inventory_findings)
        || !value.inventory_findings.every(isRemappableBaselineInventoryFinding)
        || !("gaps" in value)
        || !Array.isArray(value.gaps)
        || !value.gaps.every(isRemappableBaselineGap)) {
        throw new contracts_1.RepositoryMigrationRehearsalError("cutover-blocked", "Forward-only planning validation baseline does not match schema version 1.");
    }
    (0, validation_1.normalizeIsoTimestamp)(value.created_at);
    return value;
}
function isRemappableBaselineFinding(value) {
    return typeof value === "object"
        && value !== null
        && "fingerprint" in value
        && typeof value.fingerprint === "string"
        && "code" in value
        && typeof value.code === "string"
        && "severity" in value
        && typeof value.severity === "string"
        && "path" in value
        && (typeof value.path === "string" || value.path === null)
        && "message" in value
        && typeof value.message === "string";
}
function isRemappableBaselineInventoryFinding(value) {
    return typeof value === "object"
        && value !== null
        && "fingerprint" in value
        && typeof value.fingerprint === "string"
        && "message" in value
        && typeof value.message === "string";
}
function isRemappableBaselineGap(value) {
    return typeof value === "object"
        && value !== null
        && "fingerprint" in value
        && typeof value.fingerprint === "string"
        && "code" in value
        && typeof value.code === "string"
        && "path" in value
        && typeof value.path === "string"
        && "message" in value
        && typeof value.message === "string";
}
function remapPlanningValidationBaseline(baseline, resolver) {
    const evidence = [];
    const findings = baseline.findings.map((finding, index) => {
        const pathRemap = finding.path === null
            ? null
            : remapBaselinePlanningPath(finding.path, resolver);
        const messageRemap = remapBaselineMessage(finding.message, resolver, pathRemap === null
            ? []
            : [{ oldPath: finding.path, remap: pathRemap }]);
        const remappedFinding = {
            ...finding,
            path: pathRemap?.path ?? null,
            message: messageRemap.message
        };
        remappedFinding.fingerprint = baselineFingerprint({
            kind: "finding",
            code: remappedFinding.code,
            severity: remappedFinding.severity,
            path: remappedFinding.path,
            message: remappedFinding.message
        });
        if (remappedFinding.path !== finding.path
            || remappedFinding.message !== finding.message
            || remappedFinding.fingerprint !== finding.fingerprint) {
            evidence.push({
                lane: "finding",
                index,
                code: finding.code,
                oldPath: finding.path,
                newPath: remappedFinding.path,
                oldFingerprint: finding.fingerprint,
                newFingerprint: remappedFinding.fingerprint,
                oldMessageDigest: (0, filesystem_1.sha256)(finding.message),
                newMessageDigest: (0, filesystem_1.sha256)(remappedFinding.message),
                primaryPathRemap: pathRemap === null
                    ? null
                    : {
                        oldPath: finding.path,
                        newPath: pathRemap.path,
                        mappingKind: pathRemap.mappingKind,
                        authoritySourcePath: pathRemap.authoritySourcePath,
                        authorityDestinationPath: pathRemap.authorityDestinationPath
                    },
                pathRemaps: messageRemap.pathRemaps
            });
        }
        return remappedFinding;
    });
    const inventoryFindings = baseline.inventory_findings.map((finding, index) => {
        const messageRemap = remapBaselineMessage(finding.message, resolver);
        const remappedFinding = {
            ...finding,
            message: messageRemap.message
        };
        remappedFinding.fingerprint = baselineFingerprint({
            kind: "inventory-finding",
            message: remappedFinding.message
        });
        if (remappedFinding.message !== finding.message
            || remappedFinding.fingerprint !== finding.fingerprint) {
            evidence.push({
                lane: "inventory-finding",
                index,
                code: null,
                oldPath: null,
                newPath: null,
                oldFingerprint: finding.fingerprint,
                newFingerprint: remappedFinding.fingerprint,
                oldMessageDigest: (0, filesystem_1.sha256)(finding.message),
                newMessageDigest: (0, filesystem_1.sha256)(remappedFinding.message),
                primaryPathRemap: null,
                pathRemaps: messageRemap.pathRemaps
            });
        }
        return remappedFinding;
    });
    const gaps = baseline.gaps.map((gap, index) => {
        const pathRemap = remapBaselinePlanningPath(gap.path, resolver);
        const messageRemap = remapBaselineMessage(gap.message, resolver, [{ oldPath: gap.path, remap: pathRemap }]);
        const remappedGap = {
            ...gap,
            path: pathRemap.path,
            message: messageRemap.message
        };
        remappedGap.fingerprint = baselineFingerprint({
            kind: "planning-gap",
            code: remappedGap.code,
            path: remappedGap.path,
            message: remappedGap.message
        });
        if (remappedGap.path !== gap.path
            || remappedGap.message !== gap.message
            || remappedGap.fingerprint !== gap.fingerprint) {
            evidence.push({
                lane: "gap",
                index,
                code: gap.code,
                oldPath: gap.path,
                newPath: remappedGap.path,
                oldFingerprint: gap.fingerprint,
                newFingerprint: remappedGap.fingerprint,
                oldMessageDigest: (0, filesystem_1.sha256)(gap.message),
                newMessageDigest: (0, filesystem_1.sha256)(remappedGap.message),
                primaryPathRemap: {
                    oldPath: gap.path,
                    newPath: pathRemap.path,
                    mappingKind: pathRemap.mappingKind,
                    authoritySourcePath: pathRemap.authoritySourcePath,
                    authorityDestinationPath: pathRemap.authorityDestinationPath
                },
                pathRemaps: messageRemap.pathRemaps
            });
        }
        return remappedGap;
    });
    return {
        baseline: {
            ...baseline,
            findings,
            inventory_findings: inventoryFindings,
            gaps
        },
        evidence
    };
}
function remapBaselinePlanningPath(inputPath, resolver) {
    const isRelative = !inputPath.startsWith(".day-shift/planning/");
    const fullPath = isRelative ? `.day-shift/planning/${inputPath}` : inputPath;
    const exact = resolver.exact.get(fullPath);
    if (exact !== undefined) {
        return {
            path: isRelative
                ? exact.destinationPath.slice(".day-shift/planning/".length)
                : exact.destinationPath,
            mappingKind: "exact-artifact",
            authoritySourcePath: exact.sourcePath,
            authorityDestinationPath: exact.destinationPath
        };
    }
    const sourceRoot = fullPath.match(/^(\.day-shift\/planning\/\d{2}-[^/]+)(?:\/|$)/)?.[1];
    const rootMapping = sourceRoot === undefined
        ? undefined
        : resolver.roots.get(sourceRoot);
    if (sourceRoot === undefined || rootMapping === undefined) {
        throw new contracts_1.RepositoryMigrationRehearsalError("cutover-stale", `Baseline path '${inputPath}' is not covered by the durable migration path authority.`);
    }
    const suffix = canonicalizeDerivedOverviewSuffix(fullPath.slice(sourceRoot.length));
    const destinationPath = `${rootMapping.destinationRoot}${suffix}`;
    return {
        path: isRelative
            ? destinationPath.slice(".day-shift/planning/".length)
            : destinationPath,
        mappingKind: "durable-root-prefix",
        authoritySourcePath: rootMapping.sourcePath,
        authorityDestinationPath: rootMapping.destinationPath
    };
}
function canonicalizeDerivedOverviewSuffix(suffix) {
    if (suffix === "/overview.md")
        return "/slice-overview.md";
    if (/\/phases\/[^/]+\/overview\.md$/.test(suffix)) {
        return suffix.replace(/\/overview\.md$/, "/phase-overview.md");
    }
    if (/\/milestones\/[^/]+\/overview\.md$/.test(suffix)) {
        return suffix.replace(/\/overview\.md$/, "/milestone-overview.md");
    }
    return suffix;
}
function remapBaselineMessage(message, resolver, explicit = []) {
    let remappedMessage = message;
    const evidence = new Map();
    for (const entry of explicit) {
        if (entry.oldPath !== entry.remap.path && remappedMessage.includes(entry.oldPath)) {
            remappedMessage = remappedMessage.split(entry.oldPath).join(entry.remap.path);
            evidence.set(`${entry.oldPath}\0${entry.remap.path}`, {
                oldPath: entry.oldPath,
                newPath: entry.remap.path,
                mappingKind: entry.remap.mappingKind,
                authoritySourcePath: entry.remap.authoritySourcePath,
                authorityDestinationPath: entry.remap.authorityDestinationPath
            });
        }
    }
    remappedMessage = remappedMessage.replace(/\.day-shift\/planning\/[A-Za-z0-9_./-]+(?:\.md|\.json|\.toml|\.gitkeep)/g, (oldPath) => {
        if (!/^\.day-shift\/planning\/\d{2}-[^/]+\//.test(oldPath)) {
            return oldPath;
        }
        const remap = remapBaselinePlanningPath(oldPath, resolver);
        evidence.set(`${oldPath}\0${remap.path}`, {
            oldPath,
            newPath: remap.path,
            mappingKind: remap.mappingKind,
            authoritySourcePath: remap.authoritySourcePath,
            authorityDestinationPath: remap.authorityDestinationPath
        });
        return remap.path;
    });
    return {
        message: remappedMessage,
        pathRemaps: [...evidence.values()].sort((left, right) => left.oldPath.localeCompare(right.oldPath))
    };
}
function baselineFingerprint(value) {
    return Buffer.from(JSON.stringify(value)).toString("base64url");
}
