"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseSpecIndexEntry = parseSpecIndexEntry;
exports.serializeSpecIndexEntry = serializeSpecIndexEntry;
const types_1 = require("./types");
const source_1 = require("./source");
const routing_1 = require("./routing");
const traceability_1 = require("./traceability");
const source_transition_1 = require("./source-transition");
const REQUIRED_ENTRY_FIELDS = new Set([
    ...types_1.SPEC_INDEX_ENTRY_REQUIRED_FIELDS,
    ...types_1.SPEC_INDEX_ENTRY_ROUTED_REQUIRED_FIELDS
]);
const OPTIONAL_ENTRY_FIELDS = new Set(types_1.SPEC_INDEX_ENTRY_OPTIONAL_FIELDS);
function parseSpecIndexEntry(input) {
    const entry = readRecord(input, "Spec index entry");
    const status = readRequiredString(entry, "status");
    const planningOptional = status.trim().toLowerCase() === "draft";
    const depth = planningOptional
        ? readOptionalString(entry, "depth")
        : readRequiredString(entry, "depth");
    const destination = planningOptional
        ? readOptionalString(entry, "destination")
        : readRequiredString(entry, "destination");
    const parsedSource = (0, source_1.parseSpecRegistrySourceReference)(entry.source);
    const parsedRouting = (0, routing_1.parseSpecRegistryRouting)({
        ...(destination === undefined ? {} : { destination }),
        spec_path: readRequiredString(entry, "spec_path"),
        linked_slice: entry.linked_slice,
        implementation_order: entry.implementation_order
    });
    const optionalMetadata = {};
    const additionalMetadata = {};
    for (const key of Object.keys(entry)) {
        if (REQUIRED_ENTRY_FIELDS.has(key)) {
            continue;
        }
        if (OPTIONAL_ENTRY_FIELDS.has(key)) {
            assignOptionalMetadata(optionalMetadata, key, entry[key]);
            continue;
        }
        additionalMetadata[key] = entry[key];
    }
    const parsedEntry = {
        id: readRequiredString(entry, "id"),
        title: readRequiredString(entry, "title"),
        status,
        spec_path: parsedRouting.spec_path,
        source: parsedSource,
        optional: optionalMetadata,
        additionalMetadata
    };
    if (depth !== undefined) {
        parsedEntry.depth = depth;
    }
    if (parsedRouting.destination !== undefined) {
        parsedEntry.destination = parsedRouting.destination;
    }
    return parsedEntry;
}
function serializeSpecIndexEntry(entry) {
    const serialized = {
        id: entry.id,
        title: entry.title,
        status: entry.status,
        spec_path: entry.spec_path,
        source: (0, source_1.serializeSpecRegistrySourceReference)(entry.source)
    };
    if (entry.depth !== undefined) {
        serialized.depth = entry.depth;
    }
    if (entry.destination !== undefined) {
        serialized.destination = entry.destination;
    }
    for (const key of types_1.SPEC_INDEX_ENTRY_OPTIONAL_FIELDS) {
        const value = entry.optional[key];
        if (value !== undefined) {
            serialized[key] = serializeOptionalMetadataField(key, value);
        }
    }
    for (const [key, value] of Object.entries(entry.additionalMetadata)) {
        if (!(key in serialized)) {
            serialized[key] = value;
        }
    }
    return serialized;
}
function assignOptionalMetadata(metadata, key, value) {
    switch (key) {
        case "implementation_order":
            metadata.implementation_order = value;
            break;
        case "linked_slice":
            metadata.linked_slice = (0, traceability_1.parseLinkedSliceReference)(value);
            break;
        case "source_history":
            metadata.source_history = (0, source_transition_1.parseSpecRegistrySourceHistory)(value);
            break;
        case "registered_at":
            metadata.registered_at = (0, traceability_1.parseRegistryTimestampField)(value, "registered_at");
            break;
        case "updated_at":
            metadata.updated_at = (0, traceability_1.parseRegistryTimestampField)(value, "updated_at");
            break;
        case "archived_at":
            metadata.archived_at = (0, traceability_1.parseRegistryTimestampField)(value, "archived_at");
            break;
    }
}
function serializeOptionalMetadataField(key, value) {
    switch (key) {
        case "implementation_order":
            return value;
        case "linked_slice":
            return (0, traceability_1.serializeLinkedSliceReference)((0, traceability_1.parseLinkedSliceReference)(value));
        case "source_history":
            return (0, source_transition_1.serializeSpecRegistrySourceHistory)(value);
        case "registered_at":
            return (0, traceability_1.parseRegistryTimestampField)(value, "registered_at");
        case "updated_at":
            return (0, traceability_1.parseRegistryTimestampField)(value, "updated_at");
        case "archived_at":
            return (0, traceability_1.parseRegistryTimestampField)(value, "archived_at");
    }
}
function readRequiredString(record, field, label = "Spec index entry") {
    const value = record[field];
    if (typeof value !== "string" || value.trim() === "") {
        throw new TypeError(`${label} field '${field}' must be a non-empty string.`);
    }
    return value;
}
function readOptionalString(record, field, label = "Spec index entry") {
    if (record[field] === undefined) {
        return undefined;
    }
    return readRequiredString(record, field, label);
}
function readRecord(input, label) {
    if (typeof input !== "object" || input === null || Array.isArray(input)) {
        throw new TypeError(`${label} must be an object.`);
    }
    return input;
}
