"use client";
import { __awaiter } from '../../node_modules/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.46.2_tslib@2.8.1_typescript@5.9.3/node_modules/tslib/tslib.es6.mjs';
import { jsxs, jsx } from 'react/jsx-runtime';
import { globalLucideIcons } from '@windrun-huaiin/base-ui/components/server';
import { cn } from '@windrun-huaiin/lib/utils';
import { useTheme } from 'next-themes';
import { useId, useState, useRef, useEffect, useCallback } from 'react';
import { themeIconColor, themeSvgIconColor } from '@windrun-huaiin/base-ui/lib';

function sanitizeFilename(name) {
    return name
        .replace(/[\/:*?"<>|]/g, '_')
        .replace(/\s+/g, '_')
        .slice(0, 120);
}
function Mermaid({ chart, title, watermarkEnabled, watermarkText, enablePreview = true }) {
    const id = useId();
    const [svg, setSvg] = useState('');
    const { resolvedTheme } = useTheme();
    const [open, setOpen] = useState(false);
    // zoom & pan states for preview dialog
    const [scale, setScale] = useState(1);
    const [translate, setTranslate] = useState({ x: 0, y: 0 });
    const isPanningRef = useRef(false);
    const startPointRef = useRef({ x: 0, y: 0 });
    const startTranslateRef = useRef({ x: 0, y: 0 });
    const activePointersRef = useRef(new Map());
    const pinchStartDistanceRef = useRef(0);
    const pinchStartScaleRef = useRef(1);
    useEffect(() => {
        let isMounted = true;
        void renderChart();
        function renderChart() {
            return __awaiter(this, void 0, void 0, function* () {
                const mermaidConfig = {
                    startOnLoad: false,
                    securityLevel: 'loose',
                    fontFamily: 'inherit',
                    themeCSS: 'margin: 1.5rem auto 0;',
                    theme: resolvedTheme === 'dark' ? 'dark' : 'default',
                };
                const { default: mermaid } = yield import('../../node_modules/.pnpm/mermaid@11.12.1/node_modules/mermaid/dist/mermaid.core.mjs');
                try {
                    mermaid.initialize(mermaidConfig);
                    const { svg } = yield mermaid.render(id.replaceAll(':', ''), chart.replaceAll('\\n', '\n'));
                    let svgWithWatermark = svg;
                    if (watermarkEnabled && watermarkText) {
                        svgWithWatermark = addWatermarkToSvg(svg, watermarkText, themeSvgIconColor);
                    }
                    if (isMounted)
                        setSvg(svgWithWatermark);
                }
                catch (error) {
                    console.error('Error while rendering mermaid', error);
                }
            });
        }
        return () => {
            isMounted = false;
            setSvg('');
        };
    }, [chart, id, resolvedTheme, watermarkEnabled, watermarkText]);
    // helpers for preview zoom
    const clamp = (v, min, max) => Math.min(Math.max(v, min), max);
    const resetTransform = useCallback(() => {
        setScale(4); // 400%
        setTranslate({ x: 0, y: 0 });
    }, []);
    const zoomBy = useCallback((delta) => {
        // zoom by center: keep the zoom center at the center of the canvas, without introducing displacement
        setScale((prev) => clamp(prev + delta, 0.25, 10));
    }, []);
    const onWheel = useCallback((e) => {
        // Cmd/Ctrl + wheel zoom (around the center point), otherwise up and down panning
        if (e.metaKey || e.ctrlKey) {
            e.preventDefault();
            e.stopPropagation();
            const delta = e.deltaY > 0 ? -0.1 : 0.1;
            setScale((prev) => clamp(prev + delta, 0.25, 10));
        }
        else {
            // two-finger pan on touchpad: support both horizontal (deltaX) and vertical (deltaY)
            e.preventDefault();
            e.stopPropagation();
            setTranslate((prev) => ({ x: prev.x - e.deltaX, y: prev.y - e.deltaY }));
        }
    }, []);
    const onPointerDown = useCallback((e) => {
        activePointersRef.current.set(e.pointerId, { x: e.clientX, y: e.clientY });
        if (activePointersRef.current.size === 2) {
            const [first, second] = Array.from(activePointersRef.current.values());
            pinchStartDistanceRef.current = Math.hypot(second.x - first.x, second.y - first.y);
            pinchStartScaleRef.current = scale;
            isPanningRef.current = false;
        }
        else {
            isPanningRef.current = true;
            startPointRef.current = { x: e.clientX, y: e.clientY };
            startTranslateRef.current = Object.assign({}, translate);
        }
        e.currentTarget.setPointerCapture(e.pointerId);
    }, [scale, translate]);
    const onPointerMove = useCallback((e) => {
        if (!activePointersRef.current.has(e.pointerId))
            return;
        activePointersRef.current.set(e.pointerId, { x: e.clientX, y: e.clientY });
        if (activePointersRef.current.size === 2) {
            const [first, second] = Array.from(activePointersRef.current.values());
            const distance = Math.hypot(second.x - first.x, second.y - first.y);
            if (pinchStartDistanceRef.current > 0) {
                setScale(clamp((distance / pinchStartDistanceRef.current) * pinchStartScaleRef.current, 0.25, 10));
            }
            return;
        }
        if (!isPanningRef.current)
            return;
        const dx = e.clientX - startPointRef.current.x;
        const dy = e.clientY - startPointRef.current.y;
        setTranslate({ x: startTranslateRef.current.x + dx, y: startTranslateRef.current.y + dy });
    }, []);
    const onPointerUp = useCallback((e) => {
        activePointersRef.current.delete(e.pointerId);
        isPanningRef.current = false;
        if (activePointersRef.current.size === 1) {
            const remaining = Array.from(activePointersRef.current.values())[0];
            startPointRef.current = remaining;
            startTranslateRef.current = Object.assign({}, translate);
            isPanningRef.current = true;
        }
        if (e.currentTarget.hasPointerCapture(e.pointerId)) {
            e.currentTarget.releasePointerCapture(e.pointerId);
        }
    }, [translate]);
    const onPointerCancel = useCallback((e) => {
        activePointersRef.current.delete(e.pointerId);
        isPanningRef.current = false;
        if (e.currentTarget.hasPointerCapture(e.pointerId)) {
            e.currentTarget.releasePointerCapture(e.pointerId);
        }
    }, []);
    const handleDownload = useCallback(() => {
        if (!svg)
            return;
        const fileName = `${sanitizeFilename(title !== null && title !== void 0 ? title : 'mermaid')}.svg`;
        const blob = new Blob([svg], { type: 'image/svg+xml;charset=utf-8' });
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = fileName;
        document.body.appendChild(a);
        a.click();
        a.remove();
        URL.revokeObjectURL(url);
    }, [svg, title]);
    // prevent browser-level zoom (touchpad pinch/shortcut) from taking effect when the dialog is open
    useEffect(() => {
        if (!open)
            return;
        // 初次打开时，默认放大到 400%
        resetTransform();
        const onGlobalWheel = (ev) => {
            if (ev.ctrlKey || ev.metaKey) {
                ev.preventDefault();
            }
        };
        const onKeyDown = (ev) => {
            if (!(ev.ctrlKey || ev.metaKey))
                return;
            const k = ev.key;
            if (k === '=' || k === '+') {
                ev.preventDefault();
                setScale((prev) => clamp(prev + 0.2, 0.25, 10));
            }
            else if (k === '-') {
                ev.preventDefault();
                setScale((prev) => clamp(prev - 0.2, 0.25, 10));
            }
            else if (k === '0') {
                ev.preventDefault();
                resetTransform();
            }
        };
        window.addEventListener('wheel', onGlobalWheel, { passive: false, capture: true });
        window.addEventListener('keydown', onKeyDown, { capture: true });
        return () => {
            window.removeEventListener('wheel', onGlobalWheel, true);
            window.removeEventListener('keydown', onKeyDown, true);
        };
    }, [open, resetTransform]);
    // Lock background scroll when dialog is open
    useEffect(() => {
        if (!open)
            return;
        const previousPosition = document.body.style.position;
        const previousTop = document.body.style.top;
        const previousLeft = document.body.style.left;
        const previousRight = document.body.style.right;
        const previousWidth = document.body.style.width;
        const scrollY = window.scrollY;
        document.body.style.position = 'fixed';
        document.body.style.top = `-${scrollY}px`;
        document.body.style.left = '0';
        document.body.style.right = '0';
        document.body.style.width = '100%';
        return () => {
            document.body.style.position = previousPosition;
            document.body.style.top = previousTop;
            document.body.style.left = previousLeft;
            document.body.style.right = previousRight;
            document.body.style.width = previousWidth;
            window.scrollTo(0, scrollY);
        };
    }, [open]);
    return (jsxs("div", { children: [jsxs("div", { className: enablePreview ? 'group relative cursor-zoom-in' : undefined, onClick: () => enablePreview && svg && setOpen(true), children: [jsx("div", { dangerouslySetInnerHTML: { __html: svg } }), enablePreview && svg && (jsx("div", { className: "pointer-events-none absolute right-2 top-2 hidden rounded bg-black/50 px-2 py-0.5 text-[12px] text-white group-hover:block", children: "Preview Chart" }))] }), title && (jsxs("div", { className: cn("mt-2 flex items-center justify-center text-center text-[13px] font-italic", themeIconColor), children: [jsx(globalLucideIcons.Mmd, { className: 'mr-1 h-4 w-4' }), jsx("span", { children: title })] })), enablePreview && open && (jsxs("div", { role: "dialog", "aria-modal": "true", "aria-label": typeof title === 'string' ? title : 'Mermaid Preview', className: "fixed inset-0 z-9999 flex items-center justify-center", children: [jsx("div", { className: "absolute inset-0 bg-black/60", onClick: () => { setOpen(false); resetTransform(); }, onWheel: (e) => { e.preventDefault(); e.stopPropagation(); }, onTouchMove: (e) => { e.preventDefault(); e.stopPropagation(); } }), jsxs("div", { className: "relative z-1 max-w-[95vw] w-[95vw] h-[88vh] p-0 bg-white dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-700 rounded-md shadow-2xl overflow-hidden", children: [jsxs("div", { className: "flex items-center justify-between gap-3 px-3 py-2 border-b border-neutral-200 dark:border-neutral-700", children: [jsxs("div", { className: cn("min-w-0 flex items-center gap-2 text-sm", themeIconColor), children: [jsx(globalLucideIcons.Mmd, { className: "h-4 w-4" }), jsx("span", { className: "truncate max-w-[50vw]", children: title !== null && title !== void 0 ? title : 'Mermaid Preview' })] }), jsxs("div", { className: "flex shrink-0 items-center gap-0.5", children: [jsx("button", { "aria-label": "Zoom out", className: "hidden h-6 w-6 items-center justify-center rounded border border-neutral-300 text-[13px] transition-colors hover:bg-neutral-100 active:bg-neutral-200 hover:border-neutral-400 active:border-neutral-500 dark:border-neutral-600 dark:hover:bg-neutral-700 dark:active:bg-neutral-600 dark:hover:border-neutral-500 dark:active:border-neutral-400 sm:flex", onClick: () => zoomBy(-0.5), children: "\uFF0D" }), jsxs("span", { className: "mx-0.5 w-12 text-center text-[12px] select-none", children: [Math.round(scale * 100), "%"] }), jsx("button", { "aria-label": "Zoom in", className: "hidden h-6 w-6 items-center justify-center rounded border border-neutral-300 text-[13px] transition-colors hover:bg-neutral-100 active:bg-neutral-200 hover:border-neutral-400 active:border-neutral-500 dark:border-neutral-600 dark:hover:bg-neutral-700 dark:active:bg-neutral-600 dark:hover:border-neutral-500 dark:active:border-neutral-400 sm:flex", onClick: () => zoomBy(0.5), children: "\uFF0B" }), jsx("div", { className: "mx-1 hidden h-4 w-px bg-neutral-300 dark:bg-neutral-700 sm:block" }), jsx("button", { "aria-label": "Zoom 100%", className: "hidden h-6 min-w-8 items-center justify-center rounded border border-neutral-300 px-1.5 text-[12px] transition-colors hover:bg-neutral-100 active:bg-neutral-200 hover:border-neutral-400 active:border-neutral-500 dark:border-neutral-600 dark:hover:bg-neutral-700 dark:active:bg-neutral-600 dark:hover:border-neutral-500 dark:active:border-neutral-400 sm:inline-flex", onClick: () => setScale(1), children: "X1" }), jsx("button", { "aria-label": "Zoom 200%", className: "ml-1 hidden h-6 min-w-8 items-center justify-center rounded border border-neutral-300 px-1.5 text-[12px] transition-colors hover:bg-neutral-100 active:bg-neutral-200 hover:border-neutral-400 active:border-neutral-500 dark:border-neutral-600 dark:hover:bg-neutral-700 dark:active:bg-neutral-600 dark:hover:border-neutral-500 dark:active:border-neutral-400 sm:inline-flex", onClick: () => setScale(2), children: "X2" }), jsx("button", { "aria-label": "Zoom 300%", className: "ml-1 hidden h-6 min-w-8 items-center justify-center rounded border border-neutral-300 px-1.5 text-[12px] transition-colors hover:bg-neutral-100 active:bg-neutral-200 hover:border-neutral-400 active:border-neutral-500 dark:border-neutral-600 dark:hover:bg-neutral-700 dark:active:bg-neutral-600 dark:hover:border-neutral-500 dark:active:border-neutral-400 sm:inline-flex", onClick: () => setScale(3), children: "X3" }), jsx("button", { "aria-label": "Zoom 1000%", className: "ml-1 hidden h-6 min-w-10 items-center justify-center rounded border border-neutral-300 px-1.5 text-[12px] transition-colors hover:bg-neutral-100 active:bg-neutral-200 hover:border-neutral-400 active:border-neutral-500 dark:border-neutral-600 dark:hover:bg-neutral-700 dark:active:bg-neutral-600 dark:hover:border-neutral-500 dark:active:border-neutral-400 sm:inline-flex", onClick: () => setScale(10), children: "X10" }), jsx("button", { "aria-label": "Reset", className: cn("ml-1 flex h-6 w-6 items-center justify-center rounded transition-colors hover:bg-neutral-100 active:bg-neutral-200 dark:hover:bg-neutral-700 dark:active:bg-neutral-600", themeIconColor), onClick: resetTransform, children: jsx(globalLucideIcons.RefreshCcw, { className: "h-3.5 w-3.5" }) }), jsx("button", { "aria-label": "Download SVG", className: cn("ml-1 flex h-6 w-6 items-center justify-center rounded transition-colors hover:bg-neutral-100 active:bg-neutral-200 dark:hover:bg-neutral-700 dark:active:bg-neutral-600", themeIconColor), onClick: handleDownload, children: jsx(globalLucideIcons.Download, { className: "h-3.5 w-3.5" }) }), jsx("button", { "aria-label": "Close", className: cn("ml-1 flex h-6 w-6 items-center justify-center rounded transition-colors hover:bg-neutral-100 active:bg-neutral-200 dark:hover:bg-neutral-700 dark:active:bg-neutral-600", themeIconColor), onClick: () => { setOpen(false); resetTransform(); }, children: jsx(globalLucideIcons.X, { className: "h-3.5 w-3.5" }) })] })] }), jsxs("div", { className: "relative h-[calc(88vh-40px)] w-full overflow-hidden bg-white dark:bg-neutral-900 overscroll-contain touch-none", onWheel: onWheel, onPointerDown: onPointerDown, onPointerMove: onPointerMove, onPointerUp: onPointerUp, onPointerCancel: onPointerCancel, children: [jsx("div", { className: "absolute left-1/2 top-1/2", style: { transform: `translate(-50%, -50%) translate(${translate.x}px, ${translate.y}px)` }, children: jsx("div", { style: { transform: `scale(${scale})`, transformOrigin: '50% 50%' }, dangerouslySetInnerHTML: { __html: svg } }) }), jsxs("div", { className: "absolute inset-x-3 bottom-3 rounded-md bg-white/92 px-3 py-2 shadow-sm backdrop-blur sm:hidden dark:bg-neutral-900/92", children: [jsxs("label", { className: "mb-1 flex items-center justify-between text-[11px] text-neutral-600 dark:text-neutral-300", children: [jsx("span", { children: "Zoom" }), jsxs("span", { children: [Math.round(scale * 100), "%"] })] }), jsx("input", { "aria-label": "Zoom slider", className: "block w-full", type: "range", min: "25", max: "1000", step: "5", value: Math.round(scale * 100), style: { accentColor: themeSvgIconColor }, onChange: (e) => setScale(clamp(Number(e.target.value) / 100, 0.25, 10)) })] }), jsx("div", { className: "pointer-events-none absolute bottom-2 right-3 hidden rounded bg-black/40 px-2 py-1 text-xs text-white sm:block", children: "Drag to pan, click button to zoom-out or zoom-in" }), jsx("div", { className: "pointer-events-none absolute left-3 top-3 rounded bg-black/40 px-2 py-1 text-[11px] text-white sm:hidden", children: "Drag to pan, pinch to zoom-out or zoom-in" })] })] })] }))] }));
}
function addWatermarkToSvg(svg, watermark, watermarkColor) {
    const watermarkText = `
    <text
      x="100%"
      y="98%"
      text-anchor="end"
      font-size="12"
      font-style="italic"
      fill="${watermarkColor}"
      opacity="0.40"
      class="pointer-events-none"
      dx="-8"
      dy="-4"
    >${watermark}</text>
  `;
    return svg.replace('</svg>', `${watermarkText}</svg>`);
}

export { Mermaid };
