"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TRACE_LINK_DISPOSITION_STORAGE_PATH = exports.TRACE_LINK_DISPOSITION_STORAGE_SCHEMA_VERSION = void 0;
exports.resolveTraceLinkDispositionStoragePath = resolveTraceLinkDispositionStoragePath;
exports.createEmptyTraceLinkDispositionStorageDocument = createEmptyTraceLinkDispositionStorageDocument;
exports.parseTraceLinkDispositionStorageDocument = parseTraceLinkDispositionStorageDocument;
exports.normalizeTraceLinkDispositionStorageDocument = normalizeTraceLinkDispositionStorageDocument;
exports.serializeTraceLinkDispositionStorageDocument = serializeTraceLinkDispositionStorageDocument;
const node_path_1 = __importDefault(require("node:path"));
const model_1 = require("./model");
exports.TRACE_LINK_DISPOSITION_STORAGE_SCHEMA_VERSION = 1;
exports.TRACE_LINK_DISPOSITION_STORAGE_PATH = ".day-shift/state/trace-link-dispositions.json";
function resolveTraceLinkDispositionStoragePath(repositoryRoot) {
    return node_path_1.default.join(repositoryRoot, exports.TRACE_LINK_DISPOSITION_STORAGE_PATH);
}
function createEmptyTraceLinkDispositionStorageDocument() {
    return {
        schemaVersion: exports.TRACE_LINK_DISPOSITION_STORAGE_SCHEMA_VERSION,
        dispositions: []
    };
}
function parseTraceLinkDispositionStorageDocument(rawContent) {
    let parsed;
    try {
        parsed = JSON.parse(rawContent);
    }
    catch (error) {
        throw new SyntaxError(`Trace-link disposition storage is malformed JSON: ${formatErrorMessage(error)}`);
    }
    return normalizeTraceLinkDispositionStorageDocument(parsed);
}
function normalizeTraceLinkDispositionStorageDocument(value) {
    if (!isRecord(value)) {
        throw new RangeError("Trace-link disposition storage root must be a JSON object.");
    }
    if (value.schemaVersion !== exports.TRACE_LINK_DISPOSITION_STORAGE_SCHEMA_VERSION) {
        throw new RangeError(`Unsupported trace-link disposition storage schemaVersion '${String(value.schemaVersion)}'.`);
    }
    if (!Array.isArray(value.dispositions)) {
        throw new RangeError("Trace-link disposition storage 'dispositions' must be an array.");
    }
    const dispositions = value.dispositions.map((record, index) => normalizeStoredTraceLinkDisposition(record, index));
    (0, model_1.validateTraceLinkDispositionCollection)(dispositions);
    return {
        schemaVersion: exports.TRACE_LINK_DISPOSITION_STORAGE_SCHEMA_VERSION,
        dispositions
    };
}
function serializeTraceLinkDispositionStorageDocument(document) {
    const normalized = normalizeTraceLinkDispositionStorageDocument(document);
    return `${JSON.stringify(normalized, null, 2)}\n`;
}
function normalizeStoredTraceLinkDisposition(value, index) {
    if (!isRecord(value)) {
        throw new RangeError(`Trace-link disposition at index ${index} must be a JSON object.`);
    }
    const status = normalizeStatus(value.status, index);
    const target = normalizeStoredTarget(value.target, `dispositions[${index}].target`);
    const base = {
        id: requireString(value.id, `dispositions[${index}].id`),
        status,
        target,
        discovery: normalizeOptionalDiscovery(value.discovery, index),
        reason: normalizeOptionalString(value.reason, `dispositions[${index}].reason`),
        createdAt: requireString(value.createdAt, `dispositions[${index}].createdAt`),
        updatedAt: requireString(value.updatedAt, `dispositions[${index}].updatedAt`),
        applyEvidence: normalizeOptionalApplyEvidence(value.applyEvidence, index)
    };
    const expectedId = (0, model_1.createTraceLinkDispositionId)(target.normalizedPath);
    if (base.id !== expectedId) {
        throw new RangeError(`Trace-link disposition at index ${index} has id '${base.id}' but expected '${expectedId}'.`);
    }
    if (status === "migrated") {
        if (!("replacementTarget" in value)) {
            throw new RangeError(`Migrated trace-link disposition at index ${index} requires replacementTarget.`);
        }
        return {
            ...base,
            status: "migrated",
            replacementTarget: normalizeStoredTarget(value.replacementTarget, `dispositions[${index}].replacementTarget`)
        };
    }
    if ("replacementTarget" in value) {
        throw new RangeError(`Trace-link disposition at index ${index} may only include replacementTarget when status is migrated.`);
    }
    return status === "retired"
        ? { ...base, status: "retired" }
        : { ...base, status: "deleted" };
}
function normalizeStoredTarget(value, fieldPath) {
    if (!isRecord(value)) {
        throw new RangeError(`Trace-link disposition storage '${fieldPath}' must be a JSON object.`);
    }
    const normalizedPath = requireString(value.normalizedPath, `${fieldPath}.normalizedPath`);
    const normalized = (0, model_1.normalizeTraceLinkDispositionTarget)(normalizedPath);
    const originalInput = normalizeOptionalString(value.originalInput, `${fieldPath}.originalInput`) ?? normalizedPath;
    return {
        originalInput,
        normalizedPath: normalized.normalizedPath
    };
}
function normalizeStatus(value, index) {
    if (typeof value !== "string" || !model_1.TRACE_LINK_DISPOSITION_STATUSES.includes(value)) {
        throw new RangeError(`Trace-link disposition at index ${index} has unsupported status '${String(value)}'.`);
    }
    return value;
}
function normalizeOptionalDiscovery(value, index) {
    if (value === undefined) {
        return undefined;
    }
    if (!isRecord(value)) {
        throw new RangeError(`Trace-link disposition at index ${index} discovery must be a JSON object.`);
    }
    const discovery = {};
    const sourceArtifactPath = normalizeOptionalString(value.sourceArtifactPath, `dispositions[${index}].discovery.sourceArtifactPath`);
    const traceField = normalizeOptionalString(value.traceField, `dispositions[${index}].discovery.traceField`);
    const scope = normalizeOptionalString(value.scope, `dispositions[${index}].discovery.scope`);
    if (sourceArtifactPath !== undefined) {
        discovery.sourceArtifactPath = (0, model_1.normalizeTraceLinkDispositionTarget)(sourceArtifactPath).normalizedPath;
    }
    if (traceField !== undefined) {
        discovery.traceField = traceField;
    }
    if (scope !== undefined) {
        discovery.scope = scope;
    }
    return discovery;
}
function normalizeOptionalApplyEvidence(value, index) {
    if (value === undefined) {
        return undefined;
    }
    if (!isRecord(value)) {
        throw new RangeError(`Trace-link disposition at index ${index} applyEvidence must be a JSON object.`);
    }
    return {
        lastAppliedAt: requireString(value.lastAppliedAt, `dispositions[${index}].applyEvidence.lastAppliedAt`),
        dispositionRevision: requireString(value.dispositionRevision, `dispositions[${index}].applyEvidence.dispositionRevision`),
        changedPaths: normalizeStringArray(value.changedPaths, `dispositions[${index}].applyEvidence.changedPaths`),
        skippedPaths: normalizeStringArray(value.skippedPaths, `dispositions[${index}].applyEvidence.skippedPaths`),
        conflictPaths: normalizeStringArray(value.conflictPaths, `dispositions[${index}].applyEvidence.conflictPaths`),
        unresolvedPaths: normalizeStringArray(value.unresolvedPaths, `dispositions[${index}].applyEvidence.unresolvedPaths`)
    };
}
function normalizeStringArray(value, fieldPath) {
    if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
        throw new RangeError(`Trace-link disposition storage '${fieldPath}' must be an array of strings.`);
    }
    return value.map((item) => (0, model_1.normalizeTraceLinkDispositionTarget)(item).normalizedPath);
}
function requireString(value, fieldPath) {
    if (typeof value !== "string" || value.trim() === "") {
        throw new RangeError(`Trace-link disposition storage '${fieldPath}' must be a non-empty string.`);
    }
    return value;
}
function normalizeOptionalString(value, fieldPath) {
    if (value === undefined) {
        return undefined;
    }
    if (typeof value !== "string") {
        throw new RangeError(`Trace-link disposition storage '${fieldPath}' must be a string when present.`);
    }
    const trimmed = value.trim();
    return trimmed === "" ? undefined : value;
}
function isRecord(value) {
    return typeof value === "object" && value !== null && !Array.isArray(value);
}
function formatErrorMessage(error) {
    return error instanceof Error ? error.message : String(error);
}
