"use client";

import { useEffect, useMemo, useRef, useState } from "react";
import { cn } from "@/lib/utils";

const LEVEL_CLASS: Record<string, string> = {
  trace: "text-zinc-600",
  debug: "text-zinc-500",
  info: "text-sky-400",
  warn: "text-amber-400",
  warning: "text-amber-400",
  error: "text-red-400",
  fatal: "text-red-500",
};

function parseLine(line: string): { text: string; className: string } {
  const t = line.trim();
  if (t.startsWith("{")) {
    try {
      const o = JSON.parse(t) as Record<string, unknown>;
      if (o && typeof o === "object" && typeof o.lvl === "string") {
        return {
          text: typeof o.msg === "string" ? o.msg : t,
          className: LEVEL_CLASS[o.lvl.toLowerCase()] ?? "text-zinc-300",
        };
      }
    } catch {
      // 不是合法 JSON，原样显示
    }
  }
  return { text: line, className: "text-zinc-300" };
}

interface LogViewerProps {
  /** SSE 模式：订阅 /api/runs/[runId]/events。runId 变化时请通过 key 重挂载 */
  runId?: number;
  /** 静态模式：直接展示完整日志 */
  log?: string;
  live?: boolean;
  className?: string;
  emptyText?: string;
  /** SSE 收到 done 事件时回调 */
  onDone?: () => void;
}

export function LogViewer({
  runId,
  log,
  live,
  className,
  emptyText = "暂无日志",
  onDone,
}: LogViewerProps) {
  const isLive = live && runId != null;
  const [sseLines, setSseLines] = useState<string[]>([]);
  const boxRef = useRef<HTMLDivElement>(null);
  const stickRef = useRef(true);
  const onDoneRef = useRef(onDone);

  useEffect(() => {
    onDoneRef.current = onDone;
  }, [onDone]);

  useEffect(() => {
    if (!isLive) return;
    const es = new EventSource(`/api/runs/${runId}/events`);
    es.onmessage = (e) => {
      try {
        const d = JSON.parse(e.data) as { line?: unknown };
        setSseLines((prev) => [
          ...prev,
          typeof d.line === "string" ? d.line : String(e.data),
        ]);
      } catch {
        setSseLines((prev) => [...prev, String(e.data)]);
      }
    };
    es.addEventListener("done", () => {
      es.close();
      onDoneRef.current?.();
    });
    es.onerror = () => es.close();
    return () => es.close();
  }, [isLive, runId]);

  const lines = useMemo(() => {
    if (isLive) return sseLines;
    if (log == null || log === "") return [];
    return log.replace(/\n+$/, "").split("\n");
  }, [isLive, sseLines, log]);

  useEffect(() => {
    const el = boxRef.current;
    if (el && stickRef.current) el.scrollTop = el.scrollHeight;
  }, [lines]);

  return (
    <div
      ref={boxRef}
      onScroll={() => {
        const el = boxRef.current;
        if (!el) return;
        // 用户上翻时暂停自动滚动，回到底部附近时恢复
        stickRef.current =
          el.scrollHeight - el.scrollTop - el.clientHeight < 40;
      }}
      className={cn(
        "h-80 overflow-auto rounded-md border border-zinc-800 bg-zinc-950 p-3 font-mono text-xs leading-5",
        className
      )}
    >
      {lines.length === 0 ? (
        <div className="text-zinc-500">{emptyText}</div>
      ) : (
        lines.map((line, i) => {
          const { text, className: lineCls } = parseLine(line);
          return (
            <div
              key={i}
              className={cn("whitespace-pre-wrap break-all", lineCls)}
            >
              {text || " "}
            </div>
          );
        })
      )}
    </div>
  );
}
