"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CANONICAL_WORKFLOW_MACHINE_STATE_END = exports.CANONICAL_WORKFLOW_MACHINE_STATE_START = void 0;
exports.renderCanonicalWorkflowMachineStateSection = renderCanonicalWorkflowMachineStateSection;
exports.parseCanonicalWorkflowMachineState = parseCanonicalWorkflowMachineState;
exports.hasCanonicalWorkflowMachineState = hasCanonicalWorkflowMachineState;
exports.extractRawCanonicalWorkflowMachineState = extractRawCanonicalWorkflowMachineState;
const state_1 = require("../../workflows/state");
exports.CANONICAL_WORKFLOW_MACHINE_STATE_START = "<!-- alpacaloop:workflow-state -->";
exports.CANONICAL_WORKFLOW_MACHINE_STATE_END = "<!-- /alpacaloop:workflow-state -->";
function renderCanonicalWorkflowMachineStateSection(workflowState, flavor, labels, sectionsBeforeMachineState = []) {
    const lines = [
        ...renderOptionalSections(sectionsBeforeMachineState),
        ...renderCanonicalWorkflowMachineStateLines(workflowState, labels),
    ];
    if (flavor !== "gfm-collapsible") {
        return lines.join("\n");
    }
    return [
        "<details>",
        `<summary>${labels.openDebugMetadataSummary}</summary>`,
        "",
        ...lines,
        "",
        "</details>",
    ].join("\n");
}
function renderOptionalSections(sections) {
    return sections.flatMap((section) => [section, ""]);
}
function parseCanonicalWorkflowMachineState(markdown) {
    const extraction = extractRawCanonicalWorkflowMachineState(markdown);
    if (extraction.kind !== "found") {
        return null;
    }
    return (0, state_1.coercePersistedWorkflowStateValue)(extraction.value);
}
function hasCanonicalWorkflowMachineState(markdown) {
    return (countMachineStateMarkerLines(markdown, exports.CANONICAL_WORKFLOW_MACHINE_STATE_START) > 0);
}
function extractRawCanonicalWorkflowMachineState(markdown) {
    const block = extractCanonicalWorkflowMachineStateBlock(markdown);
    if (block.kind !== "found") {
        return block;
    }
    const payload = extractCanonicalWorkflowMachineStatePayload(block.markdown);
    if (payload === null) {
        return {
            kind: "malformed",
            summary: "Canonical workflow machine state JSON fence is malformed.",
        };
    }
    try {
        return {
            kind: "found",
            payload,
            value: JSON.parse(payload),
        };
    }
    catch {
        return {
            kind: "malformed",
            summary: "Canonical workflow machine state JSON is malformed.",
        };
    }
}
function renderCanonicalWorkflowMachineStateLines(workflowState, labels) {
    const normalizedWorkflowState = normalizePersistedWorkflowStateForMachineState(workflowState);
    const json = JSON.stringify(normalizedWorkflowState, null, 2);
    const fence = selectJsonFence(json);
    return [
        `#### ${labels.machineStateHeading}`,
        "",
        exports.CANONICAL_WORKFLOW_MACHINE_STATE_START,
        `${fence}json`,
        json,
        fence,
        exports.CANONICAL_WORKFLOW_MACHINE_STATE_END,
    ];
}
function extractCanonicalWorkflowMachineStatePayload(markdown) {
    const fencedBlock = markdown.trim();
    const match = fencedBlock.match(/^(`{3,}|~{3,})[ \t]*json[ \t]*\n([\s\S]*)\n\1[ \t]*$/u);
    return match?.[2] ?? null;
}
function extractCanonicalWorkflowMachineStateBlock(markdown) {
    const lines = markdown.split("\n");
    const startIndexes = findMarkerLineIndexes(lines, exports.CANONICAL_WORKFLOW_MACHINE_STATE_START);
    const endIndexes = findMarkerLineIndexes(lines, exports.CANONICAL_WORKFLOW_MACHINE_STATE_END);
    if (startIndexes.length === 0) {
        return {
            kind: "missing",
            summary: "Canonical workflow machine state markers are missing.",
        };
    }
    if (startIndexes.length > 1 || endIndexes.length > 1) {
        return {
            kind: "duplicate",
            summary: "Canonical workflow machine state markers are duplicated.",
        };
    }
    if (startIndexes.length !== 1 || endIndexes.length !== 1) {
        return {
            kind: "malformed",
            summary: "Canonical workflow machine state must have one start marker and one end marker.",
        };
    }
    const startIndex = startIndexes[0];
    const endIndex = endIndexes[0];
    if (startIndex === undefined ||
        endIndex === undefined ||
        endIndex <= startIndex + 1) {
        return {
            kind: "malformed",
            summary: "Canonical workflow machine state markers are out of order.",
        };
    }
    return {
        kind: "found",
        markdown: lines.slice(startIndex + 1, endIndex).join("\n"),
    };
}
function normalizePersistedWorkflowStateForMachineState(workflowState) {
    const jsonCompatibleWorkflowState = JSON.parse(JSON.stringify(workflowState));
    return sortJsonObjectKeys(jsonCompatibleWorkflowState);
}
function sortJsonObjectKeys(value) {
    if (Array.isArray(value)) {
        return value.map((entry) => sortJsonObjectKeys(entry));
    }
    if (!(0, state_1.isRecord)(value)) {
        return value;
    }
    return Object.fromEntries(Object.entries(value)
        .sort(([leftKey], [rightKey]) => compareMachineStateObjectKeys(leftKey, rightKey))
        .flatMap(([key, entry]) => {
        const normalized = sortJsonObjectKeys(entry);
        return normalized === undefined ? [] : [[key, normalized]];
    }));
}
function compareMachineStateObjectKeys(leftKey, rightKey) {
    if (leftKey === rightKey) {
        return 0;
    }
    if (leftKey === "version") {
        return -1;
    }
    if (rightKey === "version") {
        return 1;
    }
    return leftKey.localeCompare(rightKey);
}
function selectJsonFence(json) {
    let fenceLength = 3;
    for (const match of json.matchAll(/`+/gu)) {
        fenceLength = Math.max(fenceLength, match[0].length + 1);
    }
    return "`".repeat(fenceLength);
}
function findMarkerLineIndexes(lines, marker) {
    return lines.flatMap((line, index) => line.trim() === marker ? [index] : []);
}
function countMachineStateMarkerLines(markdown, marker) {
    return findMarkerLineIndexes(markdown.split("\n"), marker).length;
}
