"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.drawMermaidPng = exports.drawMermaid = void 0;
function _escapeNodeLabel(nodeLabel) {
    // Escapes the node label for Mermaid syntax.
    return nodeLabel.replace(/[^a-zA-Z-_0-9]/g, "_");
}
// Adjusts Mermaid edge to map conditional nodes to pure nodes.
function _adjustMermaidEdge(edge, nodes) {
    const sourceNodeLabel = nodes[edge.source] ?? edge.source;
    const targetNodeLabel = nodes[edge.target] ?? edge.target;
    return [sourceNodeLabel, targetNodeLabel];
}
function _generateMermaidGraphStyles(nodeColors) {
    let styles = "";
    for (const [className, color] of Object.entries(nodeColors)) {
        styles += `\tclassDef ${className}class fill:${color};\n`;
    }
    return styles;
}
/**
 * Draws a Mermaid graph using the provided graph data
 */
function drawMermaid(nodes, edges, config) {
    const { firstNodeLabel, lastNodeLabel, nodeColors, withStyles = true, curveStyle = "linear", wrapLabelNWords = 9, } = config ?? {};
    // Initialize Mermaid graph configuration
    let mermaidGraph = withStyles
        ? `%%{init: {'flowchart': {'curve': '${curveStyle}'}}}%%\ngraph TD;\n`
        : "graph TD;\n";
    if (withStyles) {
        // Node formatting templates
        const defaultClassLabel = "default";
        const formatDict = {
            [defaultClassLabel]: "{0}([{1}]):::otherclass",
        };
        if (firstNodeLabel !== undefined) {
            formatDict[firstNodeLabel] = "{0}[{0}]:::startclass";
        }
        if (lastNodeLabel !== undefined) {
            formatDict[lastNodeLabel] = "{0}[{0}]:::endclass";
        }
        // Add nodes to the graph
        for (const node of Object.values(nodes)) {
            const nodeLabel = formatDict[node] ?? formatDict[defaultClassLabel];
            const escapedNodeLabel = _escapeNodeLabel(node);
            const nodeParts = node.split(":");
            const nodeSplit = nodeParts[nodeParts.length - 1];
            mermaidGraph += `\t${nodeLabel
                .replace(/\{0\}/g, escapedNodeLabel)
                .replace(/\{1\}/g, nodeSplit)};\n`;
        }
    }
    let subgraph = "";
    // Add edges to the graph
    for (const edge of edges) {
        const sourcePrefix = edge.source.includes(":")
            ? edge.source.split(":")[0]
            : undefined;
        const targetPrefix = edge.target.includes(":")
            ? edge.target.split(":")[0]
            : undefined;
        // Exit subgraph if source or target is not in the same subgraph
        if (subgraph !== "" &&
            (subgraph !== sourcePrefix || subgraph !== targetPrefix)) {
            mermaidGraph += "\tend\n";
            subgraph = "";
        }
        // Enter subgraph if source and target are in the same subgraph
        if (subgraph === "" &&
            sourcePrefix !== undefined &&
            sourcePrefix === targetPrefix) {
            mermaidGraph = `\tsubgraph ${sourcePrefix}\n`;
            subgraph = sourcePrefix;
        }
        const [source, target] = _adjustMermaidEdge(edge, nodes);
        let edgeLabel = "";
        // Add BR every wrapLabelNWords words
        if (edge.data !== undefined) {
            let edgeData = edge.data;
            const words = edgeData.split(" ");
            // Group words into chunks of wrapLabelNWords size
            if (words.length > wrapLabelNWords) {
                edgeData = words
                    .reduce((acc, word, i) => {
                    if (i % wrapLabelNWords === 0)
                        acc.push("");
                    acc[acc.length - 1] += ` ${word}`;
                    return acc;
                }, [])
                    .join("<br>");
            }
            if (edge.conditional) {
                edgeLabel = ` -. ${edgeData} .-> `;
            }
            else {
                edgeLabel = ` -- ${edgeData} --> `;
            }
        }
        else {
            if (edge.conditional) {
                edgeLabel = ` -.-> `;
            }
            else {
                edgeLabel = ` --> `;
            }
        }
        mermaidGraph += `\t${_escapeNodeLabel(source)}${edgeLabel}${_escapeNodeLabel(target)};\n`;
    }
    if (subgraph !== "") {
        mermaidGraph += "end\n";
    }
    // Add custom styles for nodes
    if (withStyles && nodeColors !== undefined) {
        mermaidGraph += _generateMermaidGraphStyles(nodeColors);
    }
    return mermaidGraph;
}
exports.drawMermaid = drawMermaid;
/**
 * Renders Mermaid graph using the Mermaid.INK API.
 */
async function drawMermaidPng(mermaidSyntax, config) {
    let { backgroundColor = "white" } = config ?? {};
    // Use btoa for compatibility, assume ASCII
    const mermaidSyntaxEncoded = btoa(mermaidSyntax);
    // Check if the background color is a hexadecimal color code using regex
    if (backgroundColor !== undefined) {
        const hexColorPattern = /^#(?:[0-9a-fA-F]{3}){1,2}$/;
        if (!hexColorPattern.test(backgroundColor)) {
            backgroundColor = `!${backgroundColor}`;
        }
    }
    const imageUrl = `https://mermaid.ink/img/${mermaidSyntaxEncoded}?bgColor=${backgroundColor}`;
    const res = await fetch(imageUrl);
    if (!res.ok) {
        throw new Error([
            `Failed to render the graph using the Mermaid.INK API.`,
            `Status code: ${res.status}`,
            `Status text: ${res.statusText}`,
        ].join("\n"));
    }
    const content = await res.blob();
    return content;
}
exports.drawMermaidPng = drawMermaidPng;
