"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PROMPT_DRIFT_STATES = exports.PROMPT_MATERIALIZATION_OWNERS = exports.LATEST_PROMPT_DELIVERY_MODES = exports.PROMPT_MATERIALIZATION_STATE_PATH = void 0;
exports.emptyPromptMaterializationState = emptyPromptMaterializationState;
exports.hashPromptContent = hashPromptContent;
exports.hashPromptEvidence = hashPromptEvidence;
exports.readPromptMaterializationStateSync = readPromptMaterializationStateSync;
exports.serializePromptMaterializationState = serializePromptMaterializationState;
exports.withLatestPromptDeliveryEvidence = withLatestPromptDeliveryEvidence;
exports.createPromptMaterializationRecord = createPromptMaterializationRecord;
exports.withPromptMaterializationRecord = withPromptMaterializationRecord;
exports.findPromptMaterializationRecord = findPromptMaterializationRecord;
exports.classifyPromptDrift = classifyPromptDrift;
exports.createPromptMaterializationMutation = createPromptMaterializationMutation;
exports.applyPromptMaterializationMutationSync = applyPromptMaterializationMutationSync;
exports.resolvePromptMaterializationStateAbsolutePath = resolvePromptMaterializationStateAbsolutePath;
const node_crypto_1 = require("node:crypto");
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");
exports.PROMPT_MATERIALIZATION_STATE_PATH = ".day-shift/state/prompt-materializations.json";
exports.LATEST_PROMPT_DELIVERY_MODES = [
    "materialized",
    "disabled",
    "legacy-not-materialized"
];
exports.PROMPT_MATERIALIZATION_OWNERS = [
    "prompt init",
    "prompt edit",
    "prompt reconcile apply"
];
exports.PROMPT_DRIFT_STATES = [
    "unknown-provenance",
    "safe-stale-override",
    "current-customization",
    "customized-and-stale"
];
function emptyPromptMaterializationState() {
    return {
        schemaVersion: 1,
        latestDelivery: null,
        prompts: []
    };
}
function hashPromptContent(contents) {
    return (0, node_crypto_1.createHash)("sha256").update(typeof contents === "string" ? Buffer.from(contents, "utf8") : contents).digest("hex");
}
function hashPromptEvidence(value) {
    return hashPromptContent(JSON.stringify(value));
}
function readPromptMaterializationStateSync(repositoryRoot) {
    const statePath = resolvePromptMaterializationStateAbsolutePath(repositoryRoot);
    if (!node_fs_1.default.existsSync(statePath)) {
        return { status: "missing", state: emptyPromptMaterializationState(), error: null };
    }
    let parsed;
    try {
        parsed = JSON.parse(node_fs_1.default.readFileSync(statePath, "utf8"));
    }
    catch (error) {
        return {
            status: "malformed",
            state: null,
            error: `Prompt materialization state is not valid JSON: ${errorMessage(error)}`
        };
    }
    const validationError = validatePromptMaterializationState(parsed);
    if (validationError !== null) {
        return { status: "malformed", state: null, error: validationError };
    }
    return {
        status: "valid",
        state: normalizePromptMaterializationState(parsed),
        error: null
    };
}
function serializePromptMaterializationState(state) {
    const validationError = validatePromptMaterializationState(state);
    if (validationError !== null) {
        throw new TypeError(validationError);
    }
    return `${JSON.stringify(normalizePromptMaterializationState(state), null, 2)}\n`;
}
function withLatestPromptDeliveryEvidence(state, latestDelivery) {
    return normalizePromptMaterializationState({
        ...state,
        latestDelivery
    });
}
function createPromptMaterializationRecord(options) {
    return {
        promptPath: canonicalRepositoryRelativePath(options.promptPath),
        baseSha256: hashPromptContent(options.promptContents),
        upstreamSha256: hashPromptContent(options.upstreamContents),
        upstreamPath: canonicalRepositoryRelativePath(options.upstreamPath),
        upstreamLayer: options.upstreamLayer,
        writeOwner: options.writeOwner
    };
}
function withPromptMaterializationRecord(state, record) {
    const normalizedRecord = normalizePromptMaterializationRecord(record);
    const prompts = state.prompts.filter((candidate) => candidate.promptPath !== normalizedRecord.promptPath);
    prompts.push(normalizedRecord);
    return normalizePromptMaterializationState({ ...state, prompts });
}
function findPromptMaterializationRecord(state, promptPath) {
    const canonicalPath = canonicalRepositoryRelativePath(promptPath);
    return state.prompts.find((record) => record.promptPath === canonicalPath) ?? null;
}
function classifyPromptDrift(options) {
    const evidenceValid = options.evidenceValid !== false
        && isSha256OrNull(options.baseSha256)
        && isSha256OrNull(options.userSha256)
        && isSha256OrNull(options.upstreamSha256);
    const complete = evidenceValid
        && options.baseSha256 !== null
        && options.userSha256 !== null
        && options.upstreamSha256 !== null;
    const needsReplacement = isSha256(options.userSha256)
        && isSha256(options.upstreamSha256)
        && options.userSha256 !== options.upstreamSha256;
    if (!complete) {
        return {
            state: "unknown-provenance",
            needsReplacement,
            ...options,
            reason: "Base, user, upstream, and lifecycle evidence must all be present and well formed."
        };
    }
    if (options.userSha256 === options.baseSha256) {
        return {
            state: "safe-stale-override",
            needsReplacement,
            ...options,
            reason: needsReplacement
                ? "User content still equals its recorded base and can safely adopt the current upstream."
                : "User, base, and upstream content are already aligned; replacement is a no-op."
        };
    }
    if (options.upstreamSha256 === options.baseSha256
        || options.userSha256 === options.upstreamSha256) {
        return {
            state: "current-customization",
            needsReplacement,
            ...options,
            reason: options.userSha256 === options.upstreamSha256
                ? "User content already equals the current upstream while differing from its recorded base."
                : "User content differs from its base while upstream remains at that base."
        };
    }
    return {
        state: "customized-and-stale",
        needsReplacement,
        ...options,
        reason: "User and upstream content both differ from the recorded base and from each other."
    };
}
function createPromptMaterializationMutation(options) {
    const stateRead = readPromptMaterializationStateSync(options.repositoryRoot);
    if (stateRead.status === "malformed") {
        throw new Error(stateRead.error);
    }
    const record = createPromptMaterializationRecord(options);
    const state = withPromptMaterializationRecord(stateRead.state, record);
    const promptAbsolutePath = (0, paths_1.resolveRepositoryRelativePath)(options.repositoryRoot, record.promptPath);
    const stateAbsolutePath = resolvePromptMaterializationStateAbsolutePath(options.repositoryRoot);
    const promptEvidence = (0, multi_file_mutation_1.inspectMutationPathSync)(promptAbsolutePath);
    const stateEvidence = (0, multi_file_mutation_1.inspectMutationPathSync)(stateAbsolutePath);
    const serializedState = serializePromptMaterializationState(state);
    const mutationPlan = (0, multi_file_mutation_1.createMultiFileMutationPlan)([
        {
            path: promptAbsolutePath,
            contents: options.promptContents,
            preconditionToken: promptEvidence.preconditionToken
        },
        {
            path: stateAbsolutePath,
            contents: serializedState,
            preconditionToken: stateEvidence.preconditionToken
        }
    ]);
    return {
        state,
        record,
        mutationPlan,
        previewHash: hashPromptEvidence({
            schemaVersion: 1,
            promptPath: record.promptPath,
            promptBeforeSha256: promptEvidence.contentHash,
            promptIntendedSha256: record.baseSha256,
            statePath: exports.PROMPT_MATERIALIZATION_STATE_PATH,
            stateBeforeSha256: stateEvidence.contentHash,
            stateIntendedSha256: hashPromptContent(serializedState)
        })
    };
}
function applyPromptMaterializationMutationSync(options) {
    const stateAbsolutePath = resolvePromptMaterializationStateAbsolutePath(options.repositoryRoot);
    for (const entry of options.mutation.mutationPlan.entries) {
        node_fs_1.default.mkdirSync(node_path_1.default.dirname(entry.resolvedPath), { recursive: true });
    }
    return (0, recoverable_mutation_1.executeRecoverableMutationPlanSync)(stateAbsolutePath, options.mutation.mutationPlan);
}
function resolvePromptMaterializationStateAbsolutePath(repositoryRoot) {
    return (0, paths_1.resolveRepositoryRelativePath)(repositoryRoot, exports.PROMPT_MATERIALIZATION_STATE_PATH);
}
function validatePromptMaterializationState(value) {
    if (!isObject(value) || value.schemaVersion !== 1) {
        return "Prompt materialization state must be an object with schemaVersion 1.";
    }
    if (value.latestDelivery !== null) {
        const deliveryError = validateLatestDelivery(value.latestDelivery);
        if (deliveryError !== null)
            return deliveryError;
    }
    if (!Array.isArray(value.prompts)) {
        return "Prompt materialization state prompts must be an array.";
    }
    const promptPaths = new Set();
    for (const record of value.prompts) {
        const recordError = validatePromptMaterializationRecord(record);
        if (recordError !== null)
            return recordError;
        const promptPath = record.promptPath;
        if (promptPaths.has(promptPath)) {
            return `Prompt materialization state contains duplicate prompt path '${promptPath}'.`;
        }
        promptPaths.add(promptPath);
    }
    return null;
}
function validateLatestDelivery(value) {
    if (!isObject(value) || !exports.LATEST_PROMPT_DELIVERY_MODES.includes(value.mode)) {
        return "Latest prompt delivery evidence has an unsupported mode.";
    }
    if (!isSha256(value.inventorySha256) || !Array.isArray(value.prompts)) {
        return "Latest prompt delivery evidence requires a SHA-256 inventory and prompt array.";
    }
    if (value.mode !== "materialized" && value.prompts.length > 0) {
        return "Disabled or legacy-not-materialized delivery evidence cannot claim delivered prompts.";
    }
    const paths = new Set();
    for (const evidence of value.prompts) {
        if (!isObject(evidence) || !isCanonicalRepositoryRelativePath(evidence.path) || !isSha256(evidence.sha256)) {
            return "Latest delivered prompt evidence requires canonical repository-relative paths and SHA-256 hashes.";
        }
        if (paths.has(evidence.path)) {
            return `Latest prompt delivery evidence contains duplicate path '${evidence.path}'.`;
        }
        paths.add(evidence.path);
    }
    return null;
}
function validatePromptMaterializationRecord(value) {
    if (!isObject(value))
        return "Prompt materialization records must be objects.";
    if (!isCanonicalRepositoryRelativePath(value.promptPath) || !isCanonicalRepositoryRelativePath(value.upstreamPath)) {
        return "Prompt materialization records require canonical repository-relative prompt and upstream paths.";
    }
    if (!isSha256(value.baseSha256) || !isSha256(value.upstreamSha256)) {
        return "Prompt materialization records require base and upstream SHA-256 hashes.";
    }
    if (value.upstreamLayer !== "latest" && value.upstreamLayer !== "packaged-default") {
        return "Prompt materialization records require a supported upstream layer.";
    }
    if (!exports.PROMPT_MATERIALIZATION_OWNERS.includes(value.writeOwner)) {
        return "Prompt materialization records require a supported write owner.";
    }
    return null;
}
function normalizePromptMaterializationState(state) {
    const latestDelivery = state.latestDelivery === null ? null : {
        ...state.latestDelivery,
        prompts: [...state.latestDelivery.prompts]
            .map((entry) => ({ ...entry }))
            .sort((left, right) => left.path.localeCompare(right.path))
    };
    const prompts = [...state.prompts]
        .map(normalizePromptMaterializationRecord)
        .sort((left, right) => left.promptPath.localeCompare(right.promptPath));
    return { schemaVersion: 1, latestDelivery, prompts };
}
function normalizePromptMaterializationRecord(record) {
    return {
        ...record,
        promptPath: canonicalRepositoryRelativePath(record.promptPath),
        upstreamPath: canonicalRepositoryRelativePath(record.upstreamPath)
    };
}
function canonicalRepositoryRelativePath(value) {
    return (0, paths_1.parseRepositoryRelativePath)(value).normalizedPath;
}
function isCanonicalRepositoryRelativePath(value) {
    if (typeof value !== "string")
        return false;
    try {
        return canonicalRepositoryRelativePath(value) === value;
    }
    catch {
        return false;
    }
}
function isSha256(value) {
    return typeof value === "string" && /^[a-f0-9]{64}$/.test(value);
}
function isSha256OrNull(value) {
    return value === null || isSha256(value);
}
function isObject(value) {
    return value !== null && typeof value === "object" && !Array.isArray(value);
}
function errorMessage(error) {
    return error instanceof Error ? error.message : String(error);
}
