"use client";

import * as Dialog from "@radix-ui/react-dialog";
import { useEffect, useState, useCallback, useRef } from "react";
import { useRouter } from "next/navigation";
import {
  MessageSquare, X, Send, Plus, MessageSquareText, Lock,
  Wrench, ArrowUpRight, ChevronDown, ChevronRight,
} from "lucide-react";
import Link from "next/link";
import { createClient, isSupabaseConfigured } from "@/lib/supabase/client";
import type { User } from "@supabase/supabase-js";
import { MarkdownBody } from "./markdown-body";
import type { Artifact } from "@/lib/chat/agui";
import Image from "next/image";

interface ChatBubbleProps {
  context: { doc_id?: string; page_id?: string };
}

interface SessionRow {
  id: string;
  title: string | null;
  context_doc_id: string | null;
  context_page_id: string | null;
  updated_at: string;
  message_count: number;
}

interface ToolBlock {
  id: string;
  name: string;
  args: Record<string, unknown>;
  result?: unknown;
  durationMs?: number;
  state: "running" | "done";
}

interface NavOffer {
  target: string;
  label: string;
}

interface Msg {
  id?: string;
  role: "user" | "assistant" | "tool" | "system";
  content: string;
  tools?: ToolBlock[];
  navs?: NavOffer[];
  artifacts?: Artifact[];
  streaming?: boolean;
}

export function ChatBubble({ context }: ChatBubbleProps) {
  const router = useRouter();
  const [open, setOpen] = useState(false);
  const [user, setUser] = useState<User | null>(null);
  const [authReady, setAuthReady] = useState(false);
  const [sessions, setSessions] = useState<SessionRow[]>([]);
  const [activeId, setActiveId] = useState<string | null>(null);
  const [messages, setMessages] = useState<Msg[]>([]);
  const [input, setInput] = useState("");
  const [sending, setSending] = useState(false);
  const [view, setView] = useState<"list" | "chat">("chat");
  const scrollRef = useRef<HTMLDivElement>(null);

  // Auth
  useEffect(() => {
    if (!isSupabaseConfigured()) { setAuthReady(true); return; }
    const supabase = createClient();
    supabase.auth.getUser().then(({ data }) => { setUser(data.user); setAuthReady(true); });
    const { data: sub } = supabase.auth.onAuthStateChange((_e, s) => setUser(s?.user ?? null));
    return () => sub.subscription.unsubscribe();
  }, []);

  const loadSessions = useCallback(async () => {
    if (!user) return;
    const r = await fetch("/api/sessions");
    if (!r.ok) return;
    const d = (await r.json()) as { sessions: SessionRow[] };
    setSessions(d.sessions);
  }, [user]);

  // Hydrate a session's full transcript from the server. Maps persisted
  // tool_calls + citations back into the inline `tools` / `artifacts` shape
  // the renderer uses, so reopening an old session shows the same cards as
  // when it was streamed live.
  const loadMessages = useCallback(async (sessionId: string) => {
    const r = await fetch(`/api/sessions/${sessionId}`);
    if (!r.ok) {
      // Stale local activeId (e.g. session deleted server-side) — clear so
      // the next interaction creates a fresh one.
      if (r.status === 404) {
        setActiveId(null);
        try { localStorage.removeItem("chat:lastSessionId"); } catch { /* ignore */ }
      }
      return;
    }
    const d = (await r.json()) as {
      messages: Array<{
        id?: string;
        role: Msg["role"];
        content: string;
        tool_calls?: ToolBlock[] | null;
        citations?: Artifact[] | null;
      }>;
    };
    const hydrated: Msg[] = (d.messages ?? []).map((m) => ({
      id: m.id,
      role: m.role,
      content: m.content || "",
      tools: Array.isArray(m.tool_calls) ? m.tool_calls : undefined,
      artifacts: Array.isArray(m.citations) ? m.citations : undefined,
    }));
    setMessages(hydrated);
  }, []);

  const newSession = useCallback(async () => {
    const r = await fetch("/api/sessions", {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({
        context_doc_id: context.doc_id ?? null,
        context_page_id: context.page_id ?? null,
      }),
    });
    if (!r.ok) return null;
    const d = (await r.json()) as { session: SessionRow };
    setActiveId(d.session.id);
    try { localStorage.setItem("chat:lastSessionId", d.session.id); } catch { /* ignore */ }
    setMessages([{
      role: "assistant",
      content: context.page_id
        ? `Investigando **${context.page_id}**. Pergunte qualquer coisa.`
        : context.doc_id
        ? `Investigando documento **${context.doc_id}**. O que quer descobrir?`
        : "Bem-vindo. Pergunte sobre qualquer documento, entidade ou caso.",
    }]);
    setView("chat");
    return d.session;
  }, [context]);

  // Wrap setActiveId so we also persist + hydrate on switch.
  const selectSession = useCallback((id: string) => {
    setActiveId(id);
    try { localStorage.setItem("chat:lastSessionId", id); } catch { /* ignore */ }
    loadMessages(id);
    setView("chat");
  }, [loadMessages]);

  // On open: list sessions, then either resume the persisted activeId
  // (if it still belongs to the user) or pick the most recent one.
  // Only spawn a brand-new session when the user has zero sessions yet.
  useEffect(() => {
    if (!open || !user) return;
    let cancelled = false;

    (async () => {
      const r = await fetch("/api/sessions");
      if (!r.ok) return;
      const d = (await r.json()) as { sessions: SessionRow[] };
      if (cancelled) return;
      setSessions(d.sessions);

      // Resume the most-recently-active session this user had in this browser
      let resumeId: string | null = null;
      try { resumeId = localStorage.getItem("chat:lastSessionId"); } catch { /* ignore */ }
      const knownIds = new Set(d.sessions.map((s) => s.id));
      const stillValid = resumeId && knownIds.has(resumeId) ? resumeId : null;

      if (stillValid) {
        if (activeId !== stillValid) {
          setActiveId(stillValid);
          loadMessages(stillValid);
        }
      } else if (d.sessions.length > 0) {
        // No persisted choice but the user has sessions — open the latest.
        const latest = d.sessions[0].id;
        setActiveId(latest);
        loadMessages(latest);
        try { localStorage.setItem("chat:lastSessionId", latest); } catch { /* ignore */ }
      } else if (!activeId) {
        // First-ever session for this user — create it.
        await newSession();
      }
    })();

    return () => { cancelled = true; };
  }, [open, user, activeId, loadMessages, newSession]);

  // Auto-scroll on new content
  useEffect(() => {
    if (scrollRef.current) {
      scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
    }
  }, [messages]);

  async function send() {
    if (!input.trim() || sending || !activeId) return;
    const sent = input;
    setInput("");
    setSending(true);

    // Push user msg + placeholder assistant msg
    setMessages((m) => [
      ...m,
      { role: "user", content: sent },
      { role: "assistant", content: "", tools: [], navs: [], streaming: true },
    ]);

    try {
      const r = await fetch(`/api/sessions/${activeId}/messages`, {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({ content: sent }),
      });
      if (!r.ok || !r.body) {
        const err = await r.json().catch(() => ({ error: r.statusText }));
        throw new Error(err.error || `HTTP ${r.status}`);
      }
      await consumeSSE(r.body);
      loadSessions();
    } catch (e) {
      setMessages((m) => {
        const copy = [...m];
        const last = copy[copy.length - 1];
        if (last && last.role === "assistant") {
          last.content = `⚠ ${e instanceof Error ? e.message : String(e)}`;
          last.streaming = false;
        }
        return copy;
      });
    } finally {
      setSending(false);
    }
  }

  /**
   * Read the SSE stream and apply each event to the last assistant message.
   */
  async function consumeSSE(body: ReadableStream<Uint8Array>) {
    const reader = body.getReader();
    const decoder = new TextDecoder();
    let buf = "";

    const apply = (mutator: (msg: Msg) => void) => {
      setMessages((curr) => {
        const copy = [...curr];
        const last = copy[copy.length - 1];
        if (last && last.role === "assistant") mutator(last);
        return copy;
      });
    };

    for (;;) {
      const { value, done } = await reader.read();
      if (done) break;
      buf += decoder.decode(value, { stream: true });
      let idx: number;
      while ((idx = buf.indexOf("\n\n")) !== -1) {
        const block = buf.slice(0, idx);
        buf = buf.slice(idx + 2);
        let evName: string | null = null;
        let evData: string | null = null;
        for (const line of block.split("\n")) {
          if (line.startsWith("event: ")) evName = line.slice(7).trim();
          else if (line.startsWith("data: ")) evData = line.slice(6);
        }
        if (!evName || evData == null) continue;
        let payload: Record<string, unknown> = {};
        try { payload = JSON.parse(evData); } catch { continue; }

        if (evName === "text_delta") {
          const d = String(payload.delta ?? "");
          apply((m) => { m.content += d; });
        } else if (evName === "tool_start") {
          const block: ToolBlock = {
            id: String(payload.id),
            name: String(payload.name),
            args: (payload.args as Record<string, unknown>) ?? {},
            state: "running",
          };
          apply((m) => { (m.tools ??= []).push(block); });
        } else if (evName === "tool_result") {
          const id = String(payload.id);
          apply((m) => {
            const t = m.tools?.find((x) => x.id === id);
            if (t) {
              t.result = payload.result;
              t.durationMs = typeof payload.durationMs === "number" ? payload.durationMs : undefined;
              t.state = "done";
            }
          });
        } else if (evName === "navigate") {
          const offer: NavOffer = { target: String(payload.target), label: String(payload.label) };
          apply((m) => { (m.navs ??= []).push(offer); });
        } else if (evName === "artifact") {
          const a = payload.artifact as Artifact | undefined;
          if (a && typeof a === "object" && a.kind) {
            apply((m) => { (m.artifacts ??= []).push(a); });
          }
        } else if (evName === "done") {
          apply((m) => { m.streaming = false; });
        } else if (evName === "error") {
          const msg = String(payload.message ?? "(unknown error)");
          apply((m) => { m.content += `\n\n⚠ ${msg}`; m.streaming = false; });
        }
      }
    }
  }

  return (
    <>
      <button
        onClick={() => setOpen(true)}
        className="fixed bottom-5 right-5 z-30 bg-[#00ff9c] text-black hover:bg-[#00d4a8]
                   rounded-full w-14 h-14 flex items-center justify-center
                   shadow-[0_0_20px_rgba(0,255,156,0.4)] transition-all"
        aria-label="Open chat"
      >
        <MessageSquare size={22} />
      </button>

      <Dialog.Root open={open} onOpenChange={setOpen}>
        <Dialog.Portal>
          <Dialog.Overlay className="fixed inset-0 bg-black/40 backdrop-blur-sm z-40" />
          <Dialog.Content
            className="fixed bottom-5 right-5 z-50 w-[480px] max-w-[92vw] h-[78vh] max-h-[720px]
                       bg-[#0a121e] border border-[rgba(0,255,156,0.32)] rounded
                       flex flex-col text-[#c8d4e6] font-sans"
          >
            <header className="flex items-center justify-between p-3 border-b border-[rgba(0,255,156,0.32)]">
              <div className="flex items-center gap-2">
                <button
                  onClick={() => setView(view === "list" ? "chat" : "list")}
                  className="p-1 hover:bg-[rgba(0,255,156,0.08)] rounded"
                  disabled={!user}
                >
                  <MessageSquareText size={16} className={user ? "text-[#7fdbff]" : "text-[#5a6678]"} />
                </button>
                <div>
                  <Dialog.Title className="font-mono text-sm text-[#00ff9c]">🕵 Sherlock</Dialog.Title>
                  <div className="font-mono text-[9px] text-[#5a6678] tracking-widest uppercase">
                    {context.page_id || context.doc_id || "browsing"}
                  </div>
                </div>
              </div>
              <Dialog.Close asChild>
                <button className="p-1 hover:bg-[rgba(0,255,156,0.08)] rounded">
                  <X size={16} />
                </button>
              </Dialog.Close>
            </header>

            {!authReady ? (
              <div className="flex-1 flex items-center justify-center text-[#5a6678] text-sm">Loading…</div>
            ) : !isSupabaseConfigured() ? (
              <div className="flex-1 flex flex-col items-center justify-center p-6 text-center">
                <Lock size={28} className="text-[#5a6678] mb-3" />
                <p className="text-sm text-[#8896aa]">Auth not configured.</p>
              </div>
            ) : !user ? (
              <div className="flex-1 flex flex-col items-center justify-center p-6 text-center">
                <Lock size={28} className="text-[#00ff9c] mb-3" />
                <h3 className="font-mono text-[#00ff9c] mb-1">Sign in to chat</h3>
                <p className="text-sm text-[#8896aa] mb-4">Sherlock&apos;s history is tied to your account.</p>
                <Link
                  href="/auth/signin"
                  onClick={() => setOpen(false)}
                  className="px-4 py-2 bg-[#00ff9c] text-black font-mono uppercase tracking-widest text-xs hover:bg-[#00d4a8] rounded"
                >
                  Sign in
                </Link>
              </div>
            ) : view === "list" ? (
              <div className="flex-1 overflow-y-auto">
                <div className="p-3 border-b border-[rgba(0,255,156,0.12)]">
                  <button
                    onClick={() => { setActiveId(null); newSession(); }}
                    className="w-full px-3 py-2 bg-[rgba(0,255,156,0.08)] hover:bg-[rgba(0,255,156,0.16)]
                               text-[#00ff9c] font-mono text-xs uppercase tracking-widest rounded
                               flex items-center justify-center gap-2"
                  >
                    <Plus size={14} /> new investigation
                  </button>
                </div>
                <ul>
                  {sessions.map((s) => (
                    <li key={s.id} className="group flex items-stretch border-b border-[rgba(0,255,156,0.08)]">
                      <button
                        onClick={() => selectSession(s.id)}
                        className={`flex-1 text-left px-3 py-2
                                   hover:bg-[rgba(0,255,156,0.04)] ${s.id === activeId ? "bg-[rgba(0,255,156,0.08)]" : ""}`}
                      >
                        <div className="font-mono text-xs text-[#7fdbff] truncate">
                          {s.title || s.context_page_id || s.context_doc_id || "untitled"}
                        </div>
                        <div className="font-mono text-[9px] text-[#5a6678]">
                          {s.message_count} msgs · {new Date(s.updated_at).toLocaleDateString()}
                        </div>
                      </button>
                      <button
                        onClick={async () => {
                          const next = window.prompt("Renomear conversa para:", s.title ?? "");
                          if (next === null) return;
                          const r = await fetch(`/api/sessions/${s.id}`, {
                            method: "PATCH",
                            headers: { "content-type": "application/json" },
                            body: JSON.stringify({ title: next.trim() || null }),
                          });
                          if (r.ok) loadSessions();
                        }}
                        title="renomear"
                        className="opacity-0 group-hover:opacity-100 px-2 text-[#5a6678] hover:text-[#7fdbff]"
                      >✎</button>
                      <button
                        onClick={async () => {
                          if (!window.confirm("Arquivar esta conversa?")) return;
                          const r = await fetch(`/api/sessions/${s.id}`, { method: "DELETE" });
                          if (!r.ok) return;
                          if (s.id === activeId) {
                            setActiveId(null);
                            setMessages([]);
                            try { localStorage.removeItem("chat:lastSessionId"); } catch { /* ignore */ }
                          }
                          loadSessions();
                        }}
                        title="arquivar"
                        className="opacity-0 group-hover:opacity-100 px-2 text-[#5a6678] hover:text-[#ff6b6b]"
                      >🗑</button>
                    </li>
                  ))}
                  {sessions.length === 0 && (
                    <li className="px-3 py-6 text-center font-mono text-xs text-[#5a6678]">
                      sem conversas ainda — crie uma nova
                    </li>
                  )}
                </ul>
              </div>
            ) : (
              <>
                <div ref={scrollRef} className="flex-1 overflow-y-auto p-3 space-y-3 text-sm">
                  {messages.map((m, i) => (
                    <MessageBubble key={m.id ?? i} msg={m} onNavigate={(t) => { setOpen(false); router.push(t); }} />
                  ))}
                  {sending && messages[messages.length - 1]?.streaming === false ? null : null}
                </div>

                <div className="p-3 border-t border-[rgba(0,255,156,0.32)] flex gap-2">
                  <input
                    value={input}
                    onChange={(e) => setInput(e.target.value)}
                    onKeyDown={(e) => e.key === "Enter" && send()}
                    placeholder="Ask Sherlock…"
                    className="flex-1 bg-[#060a13] border border-[rgba(0,255,156,0.32)] rounded
                               px-3 py-2 text-sm focus:outline-none focus:border-[#00ff9c]"
                  />
                  <button
                    onClick={send}
                    disabled={sending || !input.trim()}
                    className="px-3 py-2 bg-[#00ff9c] text-black rounded
                               hover:bg-[#00d4a8] disabled:opacity-40 disabled:cursor-not-allowed"
                  >
                    <Send size={16} />
                  </button>
                </div>
              </>
            )}
          </Dialog.Content>
        </Dialog.Portal>
      </Dialog.Root>
    </>
  );
}

function MessageBubble({ msg, onNavigate }: { msg: Msg; onNavigate: (t: string) => void }) {
  const isUser = msg.role === "user";
  return (
    <div
      className={
        isUser
          ? "ml-8 px-3 py-2 bg-[rgba(127,219,255,0.08)] border-l-2 border-[#7fdbff] rounded whitespace-pre-wrap"
          : "mr-8 px-3 py-2 bg-[rgba(0,255,156,0.05)] border-l-2 border-[#00ff9c] rounded"
      }
    >
      {msg.tools && msg.tools.length > 0 && (
        <div className="space-y-1 mb-2">
          {msg.tools.map((t) => <ToolTrace key={t.id} t={t} />)}
        </div>
      )}
      {msg.role === "user" ? (
        <div className="whitespace-pre-wrap">{msg.content}</div>
      ) : msg.streaming && (!msg.content || msg.content.length < 8) ? (
        <div className="whitespace-pre-wrap">{msg.content}<span className="text-[#5a6678]"> ▍</span></div>
      ) : (
        <div className="chat-markdown">
          <MarkdownBody>{msg.content}</MarkdownBody>
          {msg.streaming && <span className="text-[#5a6678]"> ▍</span>}
        </div>
      )}
      {msg.artifacts && msg.artifacts.length > 0 && (
        <div className="mt-2 grid grid-cols-1 sm:grid-cols-2 gap-2">
          {msg.artifacts.map((a, i) => (
            <ArtifactCard key={i} a={a} onNavigate={onNavigate} />
          ))}
        </div>
      )}
      {msg.navs && msg.navs.length > 0 && (
        <div className="mt-2 flex flex-wrap gap-1">
          {msg.navs.map((n, i) => (
            <button
              key={i}
              onClick={() => onNavigate(n.target)}
              className="text-[10px] font-mono uppercase tracking-widest
                         bg-[rgba(0,255,156,0.12)] hover:bg-[rgba(0,255,156,0.24)]
                         border border-[#00ff9c] text-[#00ff9c] rounded
                         px-2 py-1 flex items-center gap-1"
            >
              <ArrowUpRight size={11} /> {n.label}
            </button>
          ))}
        </div>
      )}
    </div>
  );
}

function ArtifactCard({ a, onNavigate }: { a: Artifact; onNavigate: (t: string) => void }) {
  if (a.kind === "citation") {
    const href = `/d/${a.doc_id}#${a.chunk_id}`;
    const cls = (a.classification ?? "").split("//")[0].trim();
    return (
      <button
        onClick={() => onNavigate(href)}
        className="text-left p-2 border border-[rgba(127,219,255,0.20)] hover:border-[#00ff9c]
                   bg-[#060a13] rounded transition"
      >
        <div className="flex items-center gap-1.5 mb-1 font-mono text-[9px] uppercase tracking-widest">
          {cls && <span className="text-[#ff8a4d]">{cls}</span>}
          <span className="text-[#7fdbff]">{a.chunk_id}</span>
          <span className="text-[#5a6678]">· p{a.page} · {a.type}</span>
          {typeof a.score === "number" && (
            <span className="ml-auto text-[#00ff9c]">{a.score.toFixed(3)}</span>
          )}
        </div>
        {a.snippet && (
          <div className="text-[12px] text-[#c8d4e6] leading-snug line-clamp-3">{a.snippet}</div>
        )}
        <div className="font-mono text-[9px] text-[#5a6678] mt-1 truncate">↳ {a.doc_id}</div>
      </button>
    );
  }
  if (a.kind === "crop_image") {
    const href = a.chunk_id ? `/d/${a.doc_id}#${a.chunk_id}` : `/d/${a.doc_id}/p${String(a.page).padStart(3, "0")}`;
    return (
      <button
        onClick={() => onNavigate(href)}
        className="block p-1 border border-[rgba(0,255,156,0.20)] hover:border-[#00ff9c]
                   bg-[#060a13] rounded transition overflow-hidden"
      >
        <Image
          src={a.src}
          alt={a.alt_pt || a.alt_en || a.chunk_id || "crop"}
          width={320}
          height={200}
          sizes="(max-width:640px) 90vw, 280px"
          className="block w-full h-auto rounded-sm"
        />
        <div className="font-mono text-[9px] text-[#5a6678] mt-1 px-1 truncate">
          {a.doc_id} · p{a.page}{a.chunk_id ? ` · ${a.chunk_id}` : ""}
        </div>
      </button>
    );
  }
  if (a.kind === "entity_card") {
    const folder: Record<string, string> = {
      person: "people", organization: "organizations", location: "locations",
      event: "events", uap_object: "uap-objects", vehicle: "vehicles",
      operation: "operations", concept: "concepts",
    };
    const href = `/e/${folder[a.entity_class]}/${a.entity_id}`;
    return (
      <button
        onClick={() => onNavigate(href)}
        className="text-left p-2 border border-[rgba(167,139,250,0.30)] hover:border-[#a78bfa]
                   bg-[#060a13] rounded transition"
      >
        <div className="font-mono text-[9px] uppercase tracking-widest text-[#a78bfa] mb-1">
          {a.entity_class}
        </div>
        <div className="text-[13px] text-[#c8d4e6] font-bold leading-snug">{a.canonical_name}</div>
        <div className="font-mono text-[9px] text-[#5a6678] mt-1">
          {a.total_mentions ?? 0} menções · {a.documents_count ?? 0} docs
        </div>
      </button>
    );
  }
  if (a.kind === "navigation_offer") {
    return (
      <button
        onClick={() => onNavigate(a.target)}
        className="text-[10px] font-mono uppercase tracking-widest
                   bg-[rgba(0,255,156,0.12)] hover:bg-[rgba(0,255,156,0.24)]
                   border border-[#00ff9c] text-[#00ff9c] rounded
                   px-2 py-1 flex items-center gap-1"
      >
        <ArrowUpRight size={11} /> {a.label_pt || a.label_en}
      </button>
    );
  }
  // evidence_card / hypothesis_card / case_card — render as placeholder until
  // those routes exist in Fase 2.
  return null;
}

interface ChunkHit {
  chunk_id?: string;
  doc_id?: string;
  page?: number;
  type?: string;
  bbox?: { x: number; y: number; w: number; h: number } | null;
  classification?: string | null;
  snippet?: string;
  score?: number;
  href?: string;
}

/** Render hybrid_search / list_anomalies results as citation cards with bbox crops. */
function ChunkHitCard({ hit }: { hit: ChunkHit }) {
  if (!hit.doc_id || !hit.chunk_id) return null;
  const cropUrl = hit.bbox
    ? `/api/crop?doc=${encodeURIComponent(hit.doc_id)}&page=${hit.page}` +
      `&x=${hit.bbox.x}&y=${hit.bbox.y}&w=${hit.bbox.w}&h=${hit.bbox.h}&w_px=240`
    : null;
  return (
    <a
      href={hit.href ?? `/d/${hit.doc_id}/p${String(hit.page).padStart(3, "0")}#${hit.chunk_id}`}
      target="_blank"
      rel="noopener"
      className="block my-1.5 p-2 bg-[#0a121e] border border-[rgba(127,219,255,0.20)] hover:border-[#00ff9c] rounded text-[11px] flex gap-2"
    >
      {cropUrl && (
        <img
          src={cropUrl}
          alt=""
          width={80}
          height={50}
          className="block w-20 h-12 object-cover bg-black rounded flex-shrink-0"
          loading="lazy"
        />
      )}
      <div className="min-w-0 flex-1">
        <div className="flex items-center gap-2 text-[10px] font-mono mb-0.5">
          <span className="text-[#7fdbff]">{hit.chunk_id}</span>
          <span className="text-[#5a6678]">p{hit.page}</span>
          <span className="text-[#5a6678]">{hit.type}</span>
          {hit.classification && <span className="text-[#ff6b6b]">{hit.classification}</span>}
          {hit.score !== undefined && (
            <span className="text-[#8896aa] ml-auto">{hit.score.toFixed(2)}</span>
          )}
        </div>
        <div className="text-[#c8d4e6] line-clamp-2">{hit.snippet}</div>
        <div className="text-[10px] font-mono text-[#5a6678] truncate mt-0.5">{hit.doc_id}</div>
      </div>
    </a>
  );
}

function ToolTrace({ t }: { t: ToolBlock }) {
  const [open, setOpen] = useState(false);
  // Rich render for retrieval tools — show citation cards inline
  const richRender = (() => {
    if (!t.result || typeof t.result !== "object") return null;
    // request_investigation: render a detective status card with a link to /jobs/[id]
    if (t.name === "request_investigation") {
      const r = t.result as {
        job_id?: string; kind?: string; status?: string; status_url?: string;
        eta_seconds?: number; detective?: string; error?: string;
      };
      if (r.error || !r.job_id) {
        return (
          <div className="mt-1 ml-3 p-2 rounded border border-[#ff3344]/40 bg-[rgba(255,51,68,0.06)] text-[11px] font-mono text-[#ff6ec7]">
            ⚠ investigation_unavailable: {r.error || "unknown"}
          </div>
        );
      }
      const detective = r.detective ?? (
        r.kind === "hypothesis_tournament" ? "holmes" :
        r.kind === "contradiction_scan" ? "dupin" : "locard"
      );
      const tone =
        detective === "holmes" ? { text: "text-[#7fdbff]", border: "border-[#7fdbff]", label: "Holmes" } :
        detective === "dupin"  ? { text: "text-[#ff8a4d]", border: "border-[#ff8a4d]", label: "Dupin"  } :
                                  { text: "text-[#06d6a0]", border: "border-[#06d6a0]", label: "Locard" };
      return (
        <div className={`mt-1 ml-3 p-3 rounded border ${tone.border} bg-[#060a13]`}>
          <div className="flex items-baseline justify-between mb-1">
            <div className={`font-mono text-[11px] font-bold ${tone.text}`}>
              🔎 {tone.label} · {r.kind}
            </div>
            <span className="text-[10px] text-[#5a6678] font-mono uppercase">{r.status}</span>
          </div>
          <div className="text-[10px] text-[#9aa6b8] font-mono">
            job <span className="text-[#e7ecf3]">{r.job_id.slice(0, 8)}…</span>
            {r.eta_seconds ? <span className="ml-2">· ETA ~{r.eta_seconds}s</span> : null}
          </div>
          {r.status_url && (
            <Link
              href={r.status_url}
              target="_blank"
              className={`mt-2 inline-flex items-center gap-1 text-[11px] font-mono ${tone.text} hover:underline`}
            >
              acompanhar a investigação <ArrowUpRight size={11} />
            </Link>
          )}
        </div>
      );
    }
    const r = t.result as { hits?: ChunkHit[]; anomalies?: ChunkHit[]; chunks?: ChunkHit[] };
    const items = r.hits ?? r.anomalies ?? r.chunks ?? null;
    if (!items || items.length === 0) return null;
    return (
      <div className="mt-1 ml-3 space-y-1">
        {items.slice(0, 5).map((hit, i) => (
          <ChunkHitCard key={hit.chunk_id ?? i} hit={hit} />
        ))}
        {items.length > 5 && (
          <div className="text-[10px] text-[#5a6678] font-mono">…and {items.length - 5} more</div>
        )}
      </div>
    );
  })();

  return (
    <div className="text-[11px] font-mono">
      <button
        onClick={() => setOpen(!open)}
        className="flex items-center gap-1 text-[#7fdbff] hover:text-[#00ff9c]"
      >
        {open ? <ChevronDown size={11} /> : <ChevronRight size={11} />}
        <Wrench size={10} />
        <span className="font-semibold">{t.name}</span>
        {t.state === "running" ? (
          <span className="text-[#5a6678] animate-pulse">·running…</span>
        ) : (
          <span className="text-[#5a6678]">·done{t.durationMs ? ` (${t.durationMs}ms)` : ""}</span>
        )}
      </button>
      {richRender}
      {open && (
        <pre className="mt-1 ml-3 p-2 bg-[#060a13] border border-[rgba(0,255,156,0.12)] rounded
                        text-[10px] text-[#8896aa] overflow-x-auto max-h-32">
{JSON.stringify({ args: t.args, result: t.result }, null, 2).slice(0, 1500)}
        </pre>
      )}
    </div>
  );
}
