'use client';
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
/**
 * OperationLogsDrawer — slide-over panel that renders the full log history
 * for one long-running operation (cloud deploy or git-remote creation).
 *
 * Discoverability: the drawer opens from a small info-icon button tucked
 * next to the Deploy / Publish buttons. Never shown by default — only
 * when there's something to see.
 *
 * Polling: while the drawer is open and the underlying operation is
 * active (isRunning=true), we refetch every 1.5s so new entries stream in.
 * Once the operation finishes, polling stops and the view becomes static.
 *
 * No raw stdout dumps — each entry is a one-liner with an optional
 * "detail" (multi-line stderr, stack trace) rendered in a collapsible
 * block so the default view stays sleek.
 */
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { AlertTriangle, Bug, CircleCheck, Copy, Info, Loader2, TriangleAlert } from 'lucide-react';
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, } from '../../ui/sheet.js';
import { cn } from '../../../lib/utils.js';
const LEVEL_ICON = {
    Debug: { icon: Bug, className: 'text-muted-foreground' },
    Info: { icon: Info, className: 'text-sky-500' },
    Warn: { icon: TriangleAlert, className: 'text-amber-500' },
    Error: { icon: AlertTriangle, className: 'text-destructive' },
};
function formatTime(iso) {
    try {
        const d = new Date(iso);
        return d.toLocaleTimeString(undefined, {
            hour: '2-digit',
            minute: '2-digit',
            second: '2-digit',
        });
    }
    catch {
        return iso;
    }
}
export function OperationLogsDrawer({ open, onOpenChange, kind, operationId, title, subtitle, isRunning, }) {
    const [entries, setEntries] = useState([]);
    const [loading, setLoading] = useState(false);
    const [error, setError] = useState(null);
    const [showDebug, setShowDebug] = useState(false);
    const bodyRef = useRef(null);
    const refresh = useCallback(async () => {
        setLoading(true);
        setError(null);
        try {
            const res = await fetch(`/api/operations/${encodeURIComponent(kind)}/${encodeURIComponent(operationId)}/logs`);
            if (!res.ok) {
                const text = await res.text();
                setError(`HTTP ${res.status}: ${text.slice(0, 200)}`);
                return;
            }
            const body = (await res.json());
            setEntries(body.entries ?? []);
        }
        catch (err) {
            setError(err instanceof Error ? err.message : 'Failed to load logs');
        }
        finally {
            setLoading(false);
        }
    }, [kind, operationId]);
    // Fetch on open + poll while running.
    useEffect(() => {
        if (!open)
            return;
        void refresh();
        if (!isRunning)
            return;
        const timer = setInterval(() => {
            void refresh();
        }, 1500);
        return () => clearInterval(timer);
    }, [open, isRunning, refresh]);
    // Auto-scroll to bottom while running so new entries are visible without
    // the user needing to chase them. Skip when the user is paused (not
    // running) so they can read historical entries without getting yanked.
    useEffect(() => {
        if (!isRunning || !open)
            return;
        const el = bodyRef.current;
        if (el)
            el.scrollTop = el.scrollHeight;
    }, [entries, isRunning, open]);
    const visible = useMemo(() => (showDebug ? entries : entries.filter((e) => e.level !== 'Debug')), [entries, showDebug]);
    const copyAll = useCallback(async () => {
        const text = entries
            .map((e) => {
            const base = `[${formatTime(e.createdAt)}] ${e.level.toUpperCase()} — ${e.message}`;
            return e.detail ? `${base}\n${e.detail}` : base;
        })
            .join('\n');
        try {
            await navigator.clipboard.writeText(text);
        }
        catch {
            // Ignore — secure context may be unavailable in some dev envs.
        }
    }, [entries]);
    return (_jsx(Sheet, { open: open, onOpenChange: onOpenChange, children: _jsxs(SheetContent, { side: "right", className: "flex w-full flex-col gap-0 p-0 sm:max-w-lg lg:max-w-2xl", children: [_jsxs(SheetHeader, { className: "border-b", children: [_jsxs(SheetTitle, { className: "flex items-center gap-2", children: [_jsx(DrawerHeaderIcon, { isRunning: isRunning, entries: entries }), title] }), subtitle ? _jsx(SheetDescription, { children: subtitle }) : null, _jsxs("div", { className: "text-muted-foreground flex items-center gap-3 text-[11px]", children: [_jsxs("span", { children: [entries.length, " ", entries.length === 1 ? 'entry' : 'entries'] }), _jsx("button", { type: "button", className: cn('cursor-pointer rounded px-1.5 py-0.5 text-[11px]', showDebug ? 'bg-muted text-foreground' : 'hover:bg-muted hover:text-foreground'), onClick: () => setShowDebug((v) => !v), children: showDebug ? 'Hide debug' : 'Show debug' }), _jsxs("button", { type: "button", className: "hover:bg-muted hover:text-foreground inline-flex cursor-pointer items-center gap-1 rounded px-1.5 py-0.5 text-[11px]", onClick: () => void copyAll(), disabled: entries.length === 0, children: [_jsx(Copy, { className: "size-3" }), "Copy all"] })] })] }), _jsxs("div", { ref: bodyRef, className: "flex-1 overflow-y-auto p-4", children: [loading && entries.length === 0 ? (_jsxs("div", { className: "text-muted-foreground flex items-center gap-2 text-xs", children: [_jsx(Loader2, { className: "size-3 animate-spin" }), " Loading logs\u2026"] })) : null, error ? (_jsx("div", { className: "text-destructive bg-destructive/10 mb-3 rounded-md border p-2 text-xs", children: error })) : null, visible.length === 0 && !loading && !error ? (_jsxs("div", { className: "text-muted-foreground text-xs", children: ["No log entries yet.", isRunning ? ' The operation just started — entries will appear here.' : ''] })) : null, _jsx("ol", { className: "flex flex-col gap-2", children: visible.map((entry) => {
                                const { icon: Icon, className: levelClass } = LEVEL_ICON[entry.level];
                                return (_jsx("li", { className: "border-border/60 hover:bg-muted/30 group rounded-md border p-2 transition-colors", children: _jsxs("div", { className: "flex items-start gap-2", children: [_jsx(Icon, { className: cn('mt-0.5 size-3.5 shrink-0', levelClass) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex items-baseline gap-2", children: [_jsx("span", { className: "text-muted-foreground shrink-0 font-mono text-[10px]", children: formatTime(entry.createdAt) }), _jsx("span", { className: "min-w-0 flex-1 text-xs leading-snug break-words", children: entry.message })] }), entry.detail ? (_jsxs("details", { className: "mt-1", children: [_jsx("summary", { className: "text-muted-foreground hover:text-foreground cursor-pointer text-[10px]", children: "Details" }), _jsx("pre", { className: "bg-muted/50 mt-1 overflow-x-auto rounded px-2 py-1 font-mono text-[10px] whitespace-pre-wrap", children: entry.detail })] })) : null] })] }) }, entry.id));
                            }) })] })] }) }));
}
/**
 * Header icon for the OperationLogsDrawer. The current outcome is what
 * the user cares about — NOT whether any old entry from a stale prior
 * run was an Error. Earlier code did `entries.some(e => e.level ===
 * 'Error')` which permanently poisoned the icon to a destructive
 * triangle the moment a single failure was logged, even if the next
 * deploy succeeded cleanly. Look at the LATEST entry instead and let
 * its level drive the icon. Running state still wins over everything.
 */
function DrawerHeaderIcon({ isRunning, entries, }) {
    if (isRunning) {
        return _jsx(Loader2, { className: "text-primary size-4 animate-spin" });
    }
    // Walk backwards to find the latest meaningful entry. Debug entries
    // don't change the headline status — they're noise.
    for (let i = entries.length - 1; i >= 0; i--) {
        const level = entries[i].level;
        if (level === 'Debug')
            continue;
        if (level === 'Error') {
            return _jsx(AlertTriangle, { className: "text-destructive size-4" });
        }
        if (level === 'Warn') {
            return _jsx(AlertTriangle, { className: "size-4 text-amber-500" });
        }
        // Info — operation finished cleanly
        return _jsx(CircleCheck, { className: "size-4 text-emerald-500" });
    }
    // No entries at all — neutral idle icon
    return _jsx(CircleCheck, { className: "text-muted-foreground size-4" });
}
