"use client";

import { useCallback, useEffect, useMemo, useRef, useState, type RefObject } from "react";
import { createPortal } from "react-dom";
import { ArrowRight, Bot, Database, LoaderCircle } from "lucide-react";
import { errorMessage, getActivityDetail } from "@/lib/api";
import type { CanvasItem, InspectorDetailProjection } from "@/lib/types";

type RetrievalItem = Extract<CanvasItem, { itemType: "retrieval-direct" | "retrieval-agent" }>;

interface Options {
  anchorRef: RefObject<HTMLElement | null>;
  buildId?: string;
  item: RetrievalItem;
  onOpenDetail: () => void;
}

const detailCache = new Map<string, InspectorDetailProjection>();
const OPEN_DELAY = 300;
const CLOSE_DELAY = 160;
const PREVIEW_WIDTH = 380;

export function useRetrievalHoverPreview({ anchorRef, buildId, item, onOpenDetail }: Options) {
  const previewRef = useRef<HTMLDivElement>(null);
  const openTimer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
  const closeTimer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
  const [open, setOpen] = useState(false);
  const [position, setPosition] = useState({ left: 0, top: 0 });

  const clearTimers = useCallback(() => {
    if (openTimer.current) clearTimeout(openTimer.current);
    if (closeTimer.current) clearTimeout(closeTimer.current);
  }, []);
  const place = useCallback(() => {
    const anchor = anchorRef.current;
    if (!anchor) return;
    const rect = anchor.getBoundingClientRect();
    const gap = 12;
    const left = rect.right + PREVIEW_WIDTH + gap <= window.innerWidth
      ? rect.right + gap
      : Math.max(gap, rect.left - PREVIEW_WIDTH - gap);
    const estimatedHeight = Math.min(460, Math.max(260, window.innerHeight * .58));
    const top = Math.min(Math.max(gap, rect.top), Math.max(gap, window.innerHeight - estimatedHeight - gap));
    setPosition({ left, top });
  }, [anchorRef]);
  const showSoon = useCallback(() => {
    if (closeTimer.current) clearTimeout(closeTimer.current);
    if (open || openTimer.current) return;
    openTimer.current = setTimeout(() => {
      openTimer.current = undefined;
      place();
      setOpen(true);
    }, OPEN_DELAY);
  }, [open, place]);
  const showNow = useCallback(() => {
    clearTimers();
    place();
    setOpen(true);
  }, [clearTimers, place]);
  const hideSoon = useCallback(() => {
    if (openTimer.current) { clearTimeout(openTimer.current); openTimer.current = undefined; }
    if (closeTimer.current) clearTimeout(closeTimer.current);
    closeTimer.current = setTimeout(() => setOpen(false), CLOSE_DELAY);
  }, []);
  const keepOpen = useCallback(() => {
    if (closeTimer.current) clearTimeout(closeTimer.current);
  }, []);

  useEffect(() => clearTimers, [clearTimers]);
  useEffect(() => {
    if (!open) return;
    const update = () => place();
    window.addEventListener("resize", update);
    window.addEventListener("scroll", update, true);
    return () => { window.removeEventListener("resize", update); window.removeEventListener("scroll", update, true); };
  }, [open, place]);

  const preview = open && typeof document !== "undefined" ? createPortal(
    <RetrievalPreviewPanel
      ref={previewRef}
      buildId={buildId}
      item={item}
      position={position}
      onPointerEnter={keepOpen}
      onPointerLeave={hideSoon}
      onOpenDetail={onOpenDetail}
    />,
    document.body,
  ) : null;

  return {
    preview,
    anchorProps: {
      onPointerEnter: showSoon,
      onPointerLeave: hideSoon,
      onFocusCapture: showNow,
      onBlurCapture: (event: React.FocusEvent<HTMLElement>) => {
        const next = event.relatedTarget as Node | null;
        if (next && (anchorRef.current?.contains(next) || previewRef.current?.contains(next))) return;
        hideSoon();
      },
    },
  };
}

const RetrievalPreviewPanel = function RetrievalPreviewPanel({
  buildId,
  item,
  position,
  onPointerEnter,
  onPointerLeave,
  onOpenDetail,
  ref,
}: {
  buildId?: string;
  item: RetrievalItem;
  position: { left: number; top: number };
  onPointerEnter: () => void;
  onPointerLeave: () => void;
  onOpenDetail: () => void;
  ref: RefObject<HTMLDivElement | null>;
}) {
  const isAgent = item.itemType === "retrieval-agent";
  return <div
    ref={ref}
    className="retrievalHoverPreview nodrag nopan"
    role="dialog"
    aria-label={isAgent ? `${item.run.businessLabel}取数预览` : "工具取数预览"}
    style={{ left: position.left, top: position.top }}
    onPointerEnter={onPointerEnter}
    onPointerLeave={onPointerLeave}
  >
    {isAgent ? <AgentPreview item={item} /> : <DirectToolPreview buildId={buildId} item={item} />}
    <button type="button" className="retrievalPreviewAction" onClick={onOpenDetail}>查看完整详情<ArrowRight size={14} /></button>
  </div>;
};

function AgentPreview({ item }: { item: Extract<RetrievalItem, { itemType: "retrieval-agent" }> }) {
  const run = item.run;
  const hits = run.attempts.filter((attempt) => attempt.status === "hit");
  const returnedCount = hits.reduce((sum, attempt) => sum + (attempt.resultCount || 0), 0);
  const screening = cleanPreview(run.screening.preview || "");
  return <>
    <header className="retrievalPreviewHeader"><span className="retrievalPreviewIcon is-agent"><Bot size={16} /></span><div><small>Agent 取数</small><h3>{run.businessLabel}</h3></div><strong>{run.querySummary.hit} 次命中</strong></header>
    <p className="retrievalPreviewSummary">{run.querySummary.total} 次查询{returnedCount ? `，共返回 ${returnedCount} 条结果` : ""}{run.querySummary.empty ? `，${run.querySummary.empty} 次无结果` : ""}{run.querySummary.failure ? `，${run.querySummary.failure} 次失败` : ""}。</p>
    {hits.length ? <section><h4>命中了什么</h4><ul>{hits.slice(0, 3).map((attempt) => <li key={attempt.id}><span>{attempt.queryLabel}</span><em>{attempt.resultCount ?? "?"} 条</em></li>)}</ul>{hits.length > 3 ? <small className="retrievalPreviewMore">另有 {hits.length - 3} 次命中</small> : null}</section> : null}
    <section><h4>初筛得到</h4><p className="retrievalPreviewText">{screening || (run.screening.state === "running" ? "正在整理查询结果。" : "本次运行没有保存初筛结论。")}</p></section>
  </>;
}

function DirectToolPreview({ buildId, item }: { buildId?: string; item: Extract<RetrievalItem, { itemType: "retrieval-direct" }> }) {
  const calls = item.group.calls;
  const [details, setDetails] = useState<Array<{ callId: string; detail?: InspectorDetailProjection; error?: string }>>([]);
  const [loading, setLoading] = useState(Boolean(buildId && calls.length));
  useEffect(() => {
    let active = true;
    if (!buildId || !calls.length) { setLoading(false); return; }
    setLoading(true);
    void Promise.all(calls.slice(0, 4).map(async (call) => {
      const key = `${buildId}:${call.detailRef}`;
      const cached = detailCache.get(key);
      if (cached) return { callId: call.id, detail: cached };
      try {
        const detail = await getActivityDetail(buildId, call.detailRef);
        detailCache.set(key, detail);
        return { callId: call.id, detail };
      } catch (error) {
        return { callId: call.id, error: errorMessage(error) };
      }
    })).then((result) => { if (active) { setDetails(result); setLoading(false); } });
    return () => { active = false; };
  }, [buildId, calls]);

  const summaries = useMemo(() => calls.slice(0, 4).map((call) => {
    const detail = details.find((entry) => entry.callId === call.id);
    return {
      call,
      outcome: detail?.detail?.outcome?.message,
      items: (detail?.detail?.items || []).slice(0, 3),
      error: detail?.error,
    };
  }), [calls, details]);

  return <>
    <header className="retrievalPreviewHeader"><span className="retrievalPreviewIcon is-tool"><Database size={16} /></span><div><small>工具取数</small><h3>{item.group.sources.map((source) => source.businessLabel).join("、") || "读取内容"}</h3></div><strong>{item.group.successCount}/{item.group.callCount} 完成</strong></header>
    {loading ? <div className="retrievalPreviewLoading"><LoaderCircle size={16} />正在读取真实返回内容…</div> : null}
    {!loading ? <div className="retrievalPreviewCalls">{summaries.map(({ call, outcome, items, error }) => <section key={call.id}>
      <h4>{call.businessLabel}</h4>
      {error ? <p className="retrievalPreviewError">完整返回暂时不可用：{error}</p> : <>
        <p className="retrievalPreviewSummary">{outcome || call.resultSummary}</p>
        {items.length ? <ul>{items.map((result) => <li key={result.id}><span>{result.title}</span>{result.subtitle ? <small>{cleanPreview(result.subtitle)}</small> : null}</li>)}</ul> : null}
      </>}
    </section>)}</div> : null}
    {calls.length > 4 ? <small className="retrievalPreviewMore">完整详情中还有 {calls.length - 4} 次工具调用</small> : null}
  </>;
}

function cleanPreview(value: string) {
  if (!value || value.startsWith("functioncall:")) return "";
  const clean = value
    .replace(/```[\s\S]*?```/g, " ")
    .replace(/^#{1,6}\s+/gm, "")
    .replace(/^[-*+]\s+/gm, "")
    .replace(/\|/g, " ")
    .replace(/[*_`>#]/g, "")
    .replace(/\s+/g, " ")
    .trim();
  return clean.length > 360 ? `${clean.slice(0, 360).trim()}…` : clean;
}
