"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PROMPT_UPGRADE_MODES = void 0;
exports.createPromptUpgradePlan = createPromptUpgradePlan;
exports.applyPromptUpgradePlanSync = applyPromptUpgradePlanSync;
exports.verifyLatestPromptDelivery = verifyLatestPromptDelivery;
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
const multi_file_mutation_1 = require("../filesystem/multi-file-mutation");
const recoverable_mutation_1 = require("../filesystem/recoverable-mutation");
const paths_1 = require("../paths");
const default_assets_1 = require("../workspace-init/default-assets");
const prompt_materialization_1 = require("./prompt-materialization");
exports.PROMPT_UPGRADE_MODES = [
    "materialize",
    "disabled",
    "legacy-not-materialized"
];
function createPromptUpgradePlan(options) {
    assertPromptUpgradeMode(options.mode);
    const latestRoot = (0, paths_1.parseRepositoryRelativePath)(options.config.prompt_latest_root).normalizedPath;
    const userRoot = (0, paths_1.parseRepositoryRelativePath)(options.config.prompt_user_root).normalizedPath;
    if (repositoryRelativeRootsOverlap(latestRoot, userRoot)) {
        throw new Error("Prompt latest and user roots must not overlap before workspace upgrade can run.");
    }
    const stateRead = (0, prompt_materialization_1.readPromptMaterializationStateSync)(options.repositoryRoot);
    if (stateRead.status === "malformed") {
        throw new Error(stateRead.error);
    }
    const packagedAssets = (0, default_assets_1.getDefaultPackagedPromptMarkdownAssets)().map((asset) => {
        const destinationPath = node_path_1.default.posix.join(latestRoot, asset.promptSuffix);
        const destinationAbsolutePath = (0, paths_1.resolveRepositoryRelativePath)(options.repositoryRoot, destinationPath);
        const current = (0, multi_file_mutation_1.inspectMutationPathSync)(destinationAbsolutePath);
        const sha256 = (0, prompt_materialization_1.hashPromptContent)(asset.contents);
        return {
            sourcePath: asset.path,
            destinationPath,
            destinationAbsolutePath,
            contents: asset.contents,
            sha256,
            currentSha256: current.contentHash,
            preconditionToken: current.preconditionToken,
            writeNeeded: current.contentHash !== sha256
        };
    }).sort((left, right) => left.destinationPath.localeCompare(right.destinationPath));
    const inventorySha256 = (0, prompt_materialization_1.hashPromptEvidence)(packagedAssets.map((asset) => [
        asset.sourcePath,
        asset.destinationPath,
        asset.sha256
    ]));
    if (options.mode !== "materialize") {
        const existingLatestMarkdown = discoverMarkdownPaths(options.repositoryRoot, latestRoot);
        if (existingLatestMarkdown.length > 0) {
            throw new Error(`Cannot record latest prompt delivery mode '${options.mode}' while latest Markdown exists: ${existingLatestMarkdown.join(", ")}`);
        }
    }
    const latestDelivery = {
        mode: options.mode === "materialize" ? "materialized" : options.mode,
        inventorySha256,
        prompts: options.mode === "materialize"
            ? packagedAssets.map((asset) => ({ path: asset.destinationPath, sha256: asset.sha256 }))
            : []
    };
    const state = (0, prompt_materialization_1.withLatestPromptDeliveryEvidence)(stateRead.state, latestDelivery);
    const stateAbsolutePath = (0, prompt_materialization_1.resolvePromptMaterializationStateAbsolutePath)(options.repositoryRoot);
    const stateEvidence = (0, multi_file_mutation_1.inspectMutationPathSync)(stateAbsolutePath);
    const mutationInputs = [
        ...(options.mode === "materialize" ? packagedAssets.map((asset) => ({
            path: asset.destinationAbsolutePath,
            contents: asset.contents,
            preconditionToken: asset.preconditionToken
        })) : []),
        {
            path: stateAbsolutePath,
            contents: (0, prompt_materialization_1.serializePromptMaterializationState)(state),
            preconditionToken: stateEvidence.preconditionToken
        }
    ];
    const mutationPlan = (0, multi_file_mutation_1.createMultiFileMutationPlan)(mutationInputs);
    const previewHash = (0, prompt_materialization_1.hashPromptEvidence)({
        schemaVersion: 1,
        mode: options.mode,
        inventorySha256,
        assets: packagedAssets.map((asset) => ({
            sourcePath: asset.sourcePath,
            destinationPath: asset.destinationPath,
            sourceSha256: asset.sha256,
            currentSha256: asset.currentSha256
        })),
        state: {
            path: ".day-shift/state/prompt-materializations.json",
            currentSha256: stateEvidence.contentHash,
            intendedSha256: (0, prompt_materialization_1.hashPromptContent)((0, prompt_materialization_1.serializePromptMaterializationState)(state))
        }
    });
    return {
        mode: options.mode,
        inventorySha256,
        previewHash,
        writeApplied: false,
        userLayerWriteCount: 0,
        latestDelivery,
        assets: options.mode === "materialize"
            ? packagedAssets.map((asset) => ({
                sourcePath: asset.sourcePath,
                destinationPath: asset.destinationPath,
                sha256: asset.sha256,
                currentSha256: asset.currentSha256,
                writeNeeded: asset.writeNeeded
            }))
            : [],
        mutationPlan
    };
}
function applyPromptUpgradePlanSync(options) {
    if (!/^[a-f0-9]{64}$/.test(options.previewHash)) {
        throw new TypeError("Prompt upgrade preview hash must be 64 lowercase hexadecimal characters.");
    }
    const plan = createPromptUpgradePlan(options);
    if (plan.previewHash !== options.previewHash) {
        throw new Error("Prompt upgrade preview is stale; rerun preview before apply.");
    }
    for (const entry of plan.mutationPlan.entries) {
        node_fs_1.default.mkdirSync(node_path_1.default.dirname(entry.resolvedPath), { recursive: true });
    }
    const stateAbsolutePath = (0, prompt_materialization_1.resolvePromptMaterializationStateAbsolutePath)(options.repositoryRoot);
    const mutation = (0, recoverable_mutation_1.executeRecoverableMutationPlanSync)(stateAbsolutePath, plan.mutationPlan);
    const updatedPaths = mutation.receipt?.applied.map((entry) => toRepositoryRelativePath(options.repositoryRoot, entry.resolvedPath)) ?? [];
    return {
        plan,
        mutation,
        writeApplied: updatedPaths.length > 0,
        updatedPaths,
        userLayerWriteCount: 0
    };
}
function verifyLatestPromptDelivery(options) {
    const evidence = options.state.latestDelivery;
    if (evidence === null) {
        return { complete: false, reason: "Latest prompt delivery evidence is missing." };
    }
    const latestRoot = (0, paths_1.parseRepositoryRelativePath)(options.config.prompt_latest_root).normalizedPath;
    if (evidence.mode === "disabled" || evidence.mode === "legacy-not-materialized") {
        if (evidence.prompts.length > 0) {
            return { complete: false, reason: "Absent latest delivery evidence cannot contain delivered prompt records." };
        }
        const unexpected = discoverMarkdownPaths(options.repositoryRoot, latestRoot);
        return unexpected.length === 0
            ? { complete: true, reason: null }
            : { complete: false, reason: `Latest Markdown exists despite '${evidence.mode}' delivery evidence: ${unexpected.join(", ")}` };
    }
    const expected = (0, default_assets_1.getDefaultPackagedPromptMarkdownAssets)().map((asset) => ({
        sourcePath: asset.path,
        path: node_path_1.default.posix.join(latestRoot, asset.promptSuffix),
        sha256: (0, prompt_materialization_1.hashPromptContent)(asset.contents)
    })).sort((left, right) => left.path.localeCompare(right.path));
    const expectedInventorySha256 = (0, prompt_materialization_1.hashPromptEvidence)(expected.map((asset) => [
        asset.sourcePath,
        asset.path,
        asset.sha256
    ]));
    if (evidence.inventorySha256 !== expectedInventorySha256) {
        return { complete: false, reason: "Latest delivery inventory does not match the current packaged prompt inventory." };
    }
    if (evidence.prompts.length !== expected.length
        || evidence.prompts.some((prompt, index) => prompt.path !== expected[index]?.path || prompt.sha256 !== expected[index]?.sha256)) {
        return { complete: false, reason: "Latest delivery prompt evidence is incomplete or does not match the packaged inventory." };
    }
    for (const prompt of expected) {
        const absolutePath = (0, paths_1.resolveRepositoryRelativePath)(options.repositoryRoot, prompt.path);
        if (!node_fs_1.default.existsSync(absolutePath) || !node_fs_1.default.statSync(absolutePath).isFile()) {
            return { complete: false, reason: `Delivered latest prompt is missing: ${prompt.path}` };
        }
        if ((0, prompt_materialization_1.hashPromptContent)(node_fs_1.default.readFileSync(absolutePath)) !== prompt.sha256) {
            return { complete: false, reason: `Delivered latest prompt hash is stale: ${prompt.path}` };
        }
    }
    return { complete: true, reason: null };
}
function discoverMarkdownPaths(repositoryRoot, relativeRoot) {
    const absoluteRoot = (0, paths_1.resolveRepositoryRelativePath)(repositoryRoot, relativeRoot);
    if (!node_fs_1.default.existsSync(absoluteRoot))
        return [];
    if (!node_fs_1.default.statSync(absoluteRoot).isDirectory()) {
        throw new Error(`Configured prompt latest root is not a directory: ${relativeRoot}`);
    }
    const paths = [];
    collectMarkdownPaths(absoluteRoot, repositoryRoot, paths);
    return paths.sort((left, right) => left.localeCompare(right));
}
function collectMarkdownPaths(directory, repositoryRoot, output) {
    for (const entry of node_fs_1.default.readdirSync(directory, { withFileTypes: true })) {
        const absolutePath = node_path_1.default.join(directory, entry.name);
        if (entry.isDirectory()) {
            collectMarkdownPaths(absolutePath, repositoryRoot, output);
        }
        else if (entry.isFile() && entry.name.endsWith(".md")) {
            output.push(toRepositoryRelativePath(repositoryRoot, absolutePath));
        }
    }
}
function toRepositoryRelativePath(repositoryRoot, absolutePath) {
    const relativePath = node_path_1.default.relative(node_path_1.default.resolve(repositoryRoot), absolutePath).replace(/\\/g, "/");
    return (0, paths_1.parseRepositoryRelativePath)(relativePath).normalizedPath;
}
function assertPromptUpgradeMode(mode) {
    if (!exports.PROMPT_UPGRADE_MODES.includes(mode)) {
        throw new TypeError(`Unsupported prompt upgrade mode '${mode}'.`);
    }
}
function repositoryRelativeRootsOverlap(left, right) {
    return left === right || left.startsWith(`${right}/`) || right.startsWith(`${left}/`);
}
