import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { useEffect, useState } from "react";
import { CheckCircle2, XCircle } from "lucide-react";
import { useEditContext, useOperationsContext } from "../client/editContext";
import { normalizeOpId } from "../client/waitForEditOperationTerminal";
import { Popover, PopoverContent, PopoverTrigger, } from "../../components/ui/popover";
import { Tooltip, TooltipContent, TooltipTrigger, } from "../../components/ui/tooltip";
import { Spinner } from "../ui/Spinner";
const MAX_RECENT_RESULTS = 5;
function operationLabel(op) {
    const fromType = op.type?.replace(/-/g, " ").trim();
    return (op.title?.trim() ||
        (fromType ? fromType.charAt(0).toUpperCase() + fromType.slice(1) : "") ||
        "Operation");
}
/** Slim progress bar; pulses at full width while progress is still unknown. */
function OperationProgressBar({ progress }) {
    const pct = typeof progress === "number"
        ? Math.min(Math.round(progress), 100)
        : undefined;
    return (_jsxs("div", { className: "bg-neutral-grey-10 relative h-1 w-full overflow-hidden rounded-full", children: [pct !== undefined && (_jsx("div", { className: "bg-feedback-blue h-full transition-all duration-300", style: { width: `${pct}%` } })), _jsx("div", { className: "bg-feedback-blue absolute top-0 left-0 h-full animate-pulse opacity-50", style: {
                    width: pct !== undefined ? `${Math.max(pct, 2)}%` : "100%",
                    animationDuration: "1.5s",
                } })] }));
}
function ExecutingOperationRow({ op }) {
    const progress = typeof op.progress === "number"
        ? Math.min(Math.round(op.progress), 100)
        : undefined;
    return (_jsxs("div", { className: "px-3 py-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Spinner, { size: "sm" }), _jsx("span", { className: "min-w-0 flex-1 truncate text-xs font-medium", children: operationLabel(op) })] }), _jsx("div", { className: "mt-1.5 w-full", children: _jsx(OperationProgressBar, { progress: progress }) }), _jsxs("div", { className: "text-neutral-grey-50 mt-0.5 flex justify-between text-[10px]", children: [_jsx("span", { className: "mr-2 flex-1 truncate", children: op.statusMessage || "Processing..." }), progress !== undefined && (_jsxs("span", { className: "shrink-0", children: [progress, "%"] }))] })] }));
}
/**
 * Menubar pill that is visible while long-running operations (publish, import,
 * copy, ...) execute in the background. Shows live progress for each running
 * operation in a popover, plus the most recent completions and failures of
 * this browser session.
 */
export function OperationsIndicator() {
    const editContext = useEditContext();
    const operationsContext = useOperationsContext();
    const [open, setOpen] = useState(false);
    const [recentResults, setRecentResults] = useState([]);
    const addSocketMessageListener = editContext?.addSocketMessageListener;
    useEffect(() => {
        if (!addSocketMessageListener)
            return;
        return addSocketMessageListener((message) => {
            if (message.type !== "operation:completed" &&
                message.type !== "operation:failed") {
                return;
            }
            const payload = message.payload ?? {};
            const id = payload.operationId || payload.id;
            if (!id)
                return;
            const key = normalizeOpId(id);
            setRecentResults((prev) => [
                {
                    id: key,
                    title: operationLabel(payload),
                    failed: message.type === "operation:failed",
                    errorMessage: payload.errorMessage,
                },
                ...prev.filter((x) => x.id !== key),
            ].slice(0, MAX_RECENT_RESULTS));
        });
    }, [addSocketMessageListener]);
    const executing = (operationsContext?.executingEditOperations ?? []).filter((op) => op.executionStatus === "executing");
    // Nothing running and the popover is closed: take no menubar space. While
    // the popover is open the trigger must stay mounted, even at zero ops.
    if (executing.length === 0 && !open)
        return null;
    const single = executing.length === 1 ? executing[0] : undefined;
    // Aggregate for the pill bar: average of the known progress values
    const knownProgress = executing
        .map((op) => op.progress)
        .filter((p) => typeof p === "number");
    const pillProgress = knownProgress.length
        ? knownProgress.reduce((sum, p) => sum + p, 0) / knownProgress.length
        : undefined;
    return (_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(PopoverTrigger, { asChild: true, children: _jsx("button", { type: "button", className: "hover:bg-neutral-grey-5 flex h-8 w-40 cursor-pointer flex-col justify-center gap-1 rounded-md px-2", "data-testid": "operations-indicator", "aria-label": "Operations in progress", children: executing.length > 0 ? (_jsxs(_Fragment, { children: [_jsx(OperationProgressBar, { progress: pillProgress }), _jsx("span", { className: "w-full truncate text-left text-xs leading-none", children: single
                                                ? operationLabel(single)
                                                : `${executing.length} operations` })] })) : (_jsxs("span", { className: "flex items-center gap-1.5 text-xs", children: [_jsx(CheckCircle2, { strokeWidth: 1, className: "text-feedback-green h-3.5 w-3.5" }), "Operations"] })) }) }) }), _jsx(TooltipContent, { children: executing.length > 0
                            ? "Operations in progress"
                            : "Recent operations" })] }), _jsxs(PopoverContent, { align: "end", className: "w-80 p-0", "data-testid": "operations-indicator-popover", children: [executing.length > 0 ? (_jsx("div", { className: "divide-border-default divide-y", children: executing.map((op) => (_jsx(ExecutingOperationRow, { op: op }, op.id))) })) : (_jsx("div", { className: "text-neutral-grey-50 px-3 py-2 text-xs", children: "No operations running" })), recentResults.length > 0 && (_jsxs("div", { className: "border-border-default border-t", children: [_jsx("div", { className: "text-neutral-grey-50 px-3 pt-2 text-[10px] font-medium tracking-wide uppercase", children: "Recent" }), recentResults.map((result) => (_jsxs("div", { className: "flex items-start gap-2 px-3 py-1.5 last:pb-2", children: [result.failed ? (_jsx(XCircle, { strokeWidth: 1, className: "text-feedback-red mt-0.5 h-3.5 w-3.5 shrink-0" })) : (_jsx(CheckCircle2, { strokeWidth: 1, className: "text-feedback-green mt-0.5 h-3.5 w-3.5 shrink-0" })), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "truncate text-xs", children: result.title }), result.failed && result.errorMessage && (_jsx("div", { className: "text-feedback-red truncate text-[10px]", title: result.errorMessage, children: result.errorMessage }))] })] }, result.id)))] }))] })] }));
}
//# sourceMappingURL=OperationsIndicator.js.map