import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useEffect, useId, useRef, useState } from "react";
import { Eye, Loader2 } from "lucide-react";
import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover";
import { MarkdownDisplay, } from "./MarkdownDisplay";
import { SimpleIconButton } from "../editor/ui/SimpleIconButton";
import { cn } from "../lib/utils";
/**
 * Returns true when a file should be treated as Markdown, based on its
 * extension or content type. Content-type parameters (e.g.
 * `text/markdown; charset=utf-8`) are stripped before comparison.
 */
export function isMarkdownFileName(fileName, contentType) {
    const ext = fileName.toLowerCase().split(".").pop();
    if (ext === "md" || ext === "markdown")
        return true;
    const ct = contentType?.split(";")[0]?.trim().toLowerCase();
    return ct === "text/markdown" || ct === "text/x-markdown";
}
const UNSET = Symbol("unset");
/**
 * Lazily resolves async markdown content the first time `open` becomes true.
 *
 * - Caches a successful result so reopening does not refetch.
 * - Does NOT cache errors, so the next open retries.
 * - Guards against stale completions: a resolve/reject after unmount, or after
 *   `cacheKey` changes, never updates state.
 *
 * `loadSource` is read through a ref so an inline `() => fetch(...)` (new
 * identity every render) does not retrigger the fetch; pair an inline loader
 * with a stable `cacheKey` (e.g. an id) when the underlying content can change.
 */
export function useLazyMarkdownContent(open, loadSource, cacheKey) {
    const [state, setState] = useState({ status: "idle" });
    const loadSourceRef = useRef(loadSource);
    loadSourceRef.current = loadSource;
    const mountedRef = useRef(true);
    const reqIdRef = useRef(0);
    const loadedKeyRef = useRef(UNSET);
    const keyId = cacheKey ?? "default";
    useEffect(() => {
        mountedRef.current = true;
        return () => {
            mountedRef.current = false;
        };
    }, []);
    // Reset cached content when the key changes (and invalidate any in-flight).
    useEffect(() => {
        loadedKeyRef.current = UNSET;
        reqIdRef.current += 1;
        setState({ status: "idle" });
    }, [keyId]);
    useEffect(() => {
        if (!open)
            return;
        const loader = loadSourceRef.current;
        if (!loader)
            return;
        if (loadedKeyRef.current === keyId)
            return; // cached success → no refetch
        const reqId = (reqIdRef.current += 1);
        setState({ status: "loading" });
        loader().then((text) => {
            if (!mountedRef.current || reqIdRef.current !== reqId)
                return;
            loadedKeyRef.current = keyId;
            setState({ status: "loaded", text });
        }, (err) => {
            if (!mountedRef.current || reqIdRef.current !== reqId)
                return;
            const message = err instanceof Error && err.message
                ? err.message
                : "Failed to load content.";
            setState({ status: "error", message });
        });
    }, [open, keyId]);
    return state;
}
export function MarkdownViewerPopover({ source, loadSource, cacheKey, title, children, label, triggerIcon, disabled, widthClassName = "w-[min(calc(100vw-2rem),28rem)]", maxHeightClassName = "max-h-[70vh]", contentClassName = "text-xs", components, emptyState = "No content to display.", align = "start", side, sideOffset = 6, container, enableIframeClickDetection = false, open, onOpenChange, className, }) {
    const controlled = open !== undefined;
    const [internalOpen, setInternalOpen] = useState(false);
    const effectiveOpen = controlled ? open : internalOpen;
    const handleOpenChange = (next) => {
        if (!controlled)
            setInternalOpen(next);
        onOpenChange?.(next);
    };
    const lazy = useLazyMarkdownContent(effectiveOpen, loadSource, cacheKey);
    const headerId = useId();
    const resolvedLabel = label ?? "View markdown";
    let body;
    if (loadSource && lazy.status === "error") {
        body = (_jsx("div", { className: "text-feedback-red py-2 text-xs", children: lazy.message }));
    }
    else if (loadSource && lazy.status !== "loaded") {
        body = (_jsxs("div", { className: "text-neutral-grey-50 flex items-center gap-2 py-4 text-xs", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin", strokeWidth: 1.5 }), "Loading\u2026"] }));
    }
    else {
        const text = loadSource
            ? lazy.status === "loaded"
                ? lazy.text
                : ""
            : (source ?? "");
        body =
            text.trim().length > 0 ? (_jsx(MarkdownDisplay, { source: text, className: contentClassName, components: components })) : (_jsx("div", { className: "text-neutral-grey-50 py-2 text-xs", children: emptyState }));
    }
    return (_jsxs(Popover, { open: effectiveOpen, onOpenChange: handleOpenChange, enableIframeClickDetection: enableIframeClickDetection, children: [_jsx(PopoverTrigger, { asChild: true, children: children ?? (_jsx(SimpleIconButton, { label: resolvedLabel, title: resolvedLabel, icon: triggerIcon ?? _jsx(Eye, { className: "h-3.5 w-3.5", strokeWidth: 1.5 }), disabled: disabled, showTooltip: false, onClick: () => { } })) }), _jsxs(PopoverContent, { align: align, side: side, sideOffset: sideOffset, container: container, className: cn("p-0", widthClassName, className), "aria-labelledby": title ? headerId : undefined, "aria-label": title ? undefined : resolvedLabel, children: [title ? (_jsx("div", { id: headerId, className: "bg-popover border-border-default text-neutral-grey-100 sticky top-0 z-10 truncate border-b px-4 py-2 text-xs font-medium", children: title })) : null, _jsx("div", { className: cn("overflow-x-hidden overflow-y-auto px-4 py-3", maxHeightClassName), children: body })] })] }));
}
//# sourceMappingURL=MarkdownViewerPopover.js.map