"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createVscodeIgnoreIntegrationPlan = createVscodeIgnoreIntegrationPlan;
exports.applyVscodeIgnoreIntegrationPlan = applyVscodeIgnoreIntegrationPlan;
const node_crypto_1 = require("node:crypto");
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
const recoverable_mutation_1 = require("../filesystem/recoverable-mutation");
const multi_file_mutation_1 = require("../filesystem/multi-file-mutation");
const PROTECTED_PREFIXES = [".day-shift", "src", "test", "tests", "tests_v2", "docs", "documentation"];
function normalizeSupportedPattern(rule) {
    const pattern = rule.pattern.trim().replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\//, "");
    if (!pattern || pattern.startsWith("!") || pattern.includes(".."))
        return null;
    const normalized = pattern.replace(/\/$/, "");
    if (!normalized || /[*?{}[\]]/.test(normalized))
        return null;
    const firstSegment = normalized.split("/")[0];
    if (PROTECTED_PREFIXES.includes(firstSegment))
        return null;
    return normalized;
}
function stableObject(keys) {
    return Object.freeze(Object.fromEntries(keys.map((key) => [key, true])));
}
function createVscodeIgnoreIntegrationPlan(options) {
    const content = options.settingsContent;
    const exists = content !== undefined;
    const bytes = content === undefined
        ? Buffer.alloc(0)
        : Buffer.isBuffer(content) ? content : Buffer.from(content, "utf8");
    if (exists) {
        let parsed;
        try {
            parsed = JSON.parse(bytes.toString("utf8"));
        }
        catch {
            throw new Error("Malformed VS Code workspace settings: expected valid JSON object content.");
        }
        if (parsed === null || Array.isArray(parsed) || typeof parsed !== "object") {
            throw new Error("Malformed VS Code workspace settings: expected a JSON object.");
        }
    }
    const supported = new Set();
    const assumptions = new Set();
    for (const source of options.policy.sources) {
        for (const rule of source.rules) {
            const normalized = normalizeSupportedPattern(rule);
            if (normalized && rule.decision === "exclude")
                supported.add(normalized);
            else if (normalized)
                supported.delete(normalized);
            else if (rule.decision === "exclude")
                assumptions.add(`Unsupported exclusion was not translated: ${rule.pattern}`);
            else
                assumptions.add(`Include rule remains canonical-policy evidence and was not translated: ${rule.pattern}`);
        }
    }
    const paths = [...supported].sort((left, right) => left.localeCompare(right));
    const globKeys = paths.map((entry) => `**/${entry}/**`);
    const mutations = [
        { category: "search", field: "search.exclude", value: stableObject(globKeys) },
        { category: "filesystem-watcher", field: "files.watcherExclude", value: stableObject(globKeys) },
        { category: "language-service", field: "typescript.preferences.exclude", value: Object.freeze(globKeys) }
    ];
    return {
        target: "vscode",
        policy: { id: options.policy.id, version: options.policy.version },
        files: [".vscode/settings.json"],
        mutations,
        assumptions: [...assumptions].sort((left, right) => left.localeCompare(right)),
        precondition: {
            path: ".vscode/settings.json",
            exists,
            sha256: (0, node_crypto_1.createHash)("sha256").update(bytes).digest("hex"),
            byteLength: bytes.length
        }
    };
}
function applyVscodeIgnoreIntegrationPlan(options) {
    if (options.plan.target !== "vscode")
        throw new Error(`Unsupported ignore integration target: ${options.plan.target}.`);
    if (options.plan.policy.id !== options.policy.id || options.plan.policy.version !== options.policy.version) {
        throw new Error("Ignore integration preview policy identity is stale.");
    }
    const settingsPath = node_path_1.default.join(node_path_1.default.resolve(options.workspaceRoot), ".vscode", "settings.json");
    const currentContent = node_fs_1.default.existsSync(settingsPath) ? node_fs_1.default.readFileSync(settingsPath) : undefined;
    const currentPlan = createVscodeIgnoreIntegrationPlan({ policy: options.policy, settingsContent: currentContent });
    if (currentPlan.precondition.exists !== options.plan.precondition.exists
        || currentPlan.precondition.sha256 !== options.plan.precondition.sha256
        || currentPlan.precondition.byteLength !== options.plan.precondition.byteLength) {
        throw new Error("Ignore integration preview content identity is stale.");
    }
    const parsed = currentContent === undefined ? {} : JSON.parse(currentContent.toString("utf8"));
    const merged = { ...parsed };
    for (const mutation of options.plan.mutations) {
        const existing = merged[mutation.field];
        merged[mutation.field] = Array.isArray(mutation.value)
            ? [...mutation.value]
            : {
                ...(existing !== null && typeof existing === "object" && !Array.isArray(existing) ? existing : {}),
                ...mutation.value
            };
    }
    const intendedContent = `${JSON.stringify(merged, null, 2)}\n`;
    const inspected = (0, multi_file_mutation_1.inspectMutationPathSync)(settingsPath);
    const mutationPlan = (0, multi_file_mutation_1.createMultiFileMutationPlan)([{
            path: settingsPath,
            contents: intendedContent,
            preconditionToken: inspected.preconditionToken
        }]);
    options.beforeExecute?.();
    node_fs_1.default.mkdirSync(node_path_1.default.dirname(settingsPath), { recursive: true });
    const mutation = (0, recoverable_mutation_1.executeRecoverableMutationPlanSync)(settingsPath, mutationPlan, {
        ...(options.atomicWrite === undefined ? {} : { atomicWrite: options.atomicWrite })
    });
    const afterContent = node_fs_1.default.readFileSync(settingsPath);
    const afterPlan = createVscodeIgnoreIntegrationPlan({ policy: options.policy, settingsContent: afterContent });
    return {
        target: "vscode",
        changed: (mutation.receipt?.applied.length ?? 0) > 0,
        files: [".vscode/settings.json"],
        fields: options.plan.mutations.map(({ field }) => field),
        before: options.plan.precondition,
        after: afterPlan.precondition,
        mutation,
        rollback: {
            path: ".vscode/settings.json",
            existed: currentContent !== undefined,
            restorationContent: currentContent?.toString("utf8") ?? null
        }
    };
}
