"use client";

import { useCallback, useEffect, useRef, useState } from "react";
import { AlertTriangle } from "lucide-react";
import { ReactFlowProvider } from "@xyflow/react";
import { errorMessage, getActivityDetail, getDecisionPrompt, getInspectorView, getRoundDetail } from "@/lib/api";
import type { CanvasItem, ExecutionViewV8, InspectorWorkbenchProjection, OpenTarget, PromptProjection, RetrievalActivity, StoryNode } from "@/lib/types";
import { branchFlow, branchSummaryItem, finalResultItem, multipathDecisionItem, multipathReviewItem, queryAttemptItem, retrievalActivityItem, retrievalAgentItem, retrievalDirectItem, retrievalStageItem, roundSummaryItem, storyItem } from "@/lib/layout";
import { useExecutionView } from "@/hooks/useExecutionView";
import { TopBar } from "@/components/workbench/TopBar";
import { FlowCanvas } from "@/components/workbench/FlowCanvas";
import { MobileTimeline } from "@/components/workbench/MobileTimeline";
import { Inspector } from "@/components/inspector/Inspector";
import { ScriptArtifactViewer } from "@/components/artifact/ScriptArtifactViewer";
import { PromptDrawer } from "@/components/decision/PromptDrawer";
import { expandedStateFor, isFullyExpanded } from "@/lib/expansion";

interface ArtifactTarget { snapshotRef: string }

export function ExecutionWorkbench() {
  const state = useExecutionView();
  const [expandedRounds, setExpandedRounds] = useState<Set<number>>(new Set());
  const [expandedBranches, setExpandedBranches] = useState<Set<string>>(new Set());
  const [expandedRetrievalAgents, setExpandedRetrievalAgents] = useState<Set<string>>(new Set());
  const [selected, setSelected] = useState<CanvasItem | null>(null);
  const [inspectorHistory, setInspectorHistory] = useState<CanvasItem[]>([]);
  const [artifactTarget, setArtifactTarget] = useState<ArtifactTarget | null>(null);
  const [promptRef, setPromptRef] = useState<string>();
  const [prompt, setPrompt] = useState<PromptProjection>();
  const [promptLoading, setPromptLoading] = useState(false);
  const [promptError, setPromptError] = useState<string>();
  const [inspectorMode, setInspectorMode] = useState<"business" | "lineage">("business");
  const [rawDetail, setRawDetail] = useState<unknown>();
  const [rawLoading, setRawLoading] = useState(false);
  const [detailError, setDetailError] = useState<string>();
  const [inspectorView, setInspectorView] = useState<InspectorWorkbenchProjection>();
  const [inspectorLoading, setInspectorLoading] = useState(false);
  const [inspectorError, setInspectorError] = useState<string>();
  const [mobileCanvas, setMobileCanvas] = useState(false);
  const [focusRound, setFocusRound] = useState<{ roundIndex: number; token: number }>();
  const selectedDetailRef = selected ? inspectorDetailRefFor(selected) : undefined;
  const inspectorTrigger = useRef<HTMLElement | null>(null);
  const inspectorTriggerKey = useRef<string | null>(null);
  const pendingFocusRestore = useRef<{ key: string | null; element: HTMLElement | null } | null>(null);
  const promptTrigger = useRef<HTMLElement | null>(null);
  const promptTriggerKey = useRef<string | null>(null);
  const pendingPromptFocusRestore = useRef<{ key: string | null; element: HTMLElement | null } | null>(null);
  const expansionPreference = useRef<"expanded" | "custom">("expanded");
  const detailCache = useRef(new Map<string, unknown>());
  const inspectorCache = useRef(new Map<string, InspectorWorkbenchProjection>());

  useEffect(() => {
    if (!state.view) return;
    expansionPreference.current = "expanded";
    const expanded = expandedStateFor(state.view);
    setExpandedRounds(expanded.rounds);
    setExpandedBranches(expanded.branches);
    setExpandedRetrievalAgents(expanded.retrievalAgents);
    setSelected(null);
    setInspectorHistory([]);
    setInspectorMode("business");
    detailCache.current.clear();
    inspectorCache.current.clear();
    setRawDetail(undefined);
    setDetailError(undefined);
    setInspectorView(undefined);
    setInspectorError(undefined);
    setArtifactTarget(null);
    setPromptRef(undefined);
  }, [state.view?.header.id]);

  useEffect(() => {
    if (!state.view || expansionPreference.current !== "expanded") return;
    const expanded = expandedStateFor(state.view);
    setExpandedRounds(expanded.rounds);
    setExpandedBranches(expanded.branches);
    setExpandedRetrievalAgents(expanded.retrievalAgents);
  }, [state.view?.capturedAt]);

  useEffect(() => {
    if (inspectorMode !== "business" || !selected || !state.view) return;
    const detailRef = selectedDetailRef;
    if (!detailRef) {
      setRawDetail(selected);
      setRawLoading(false);
      setDetailError(undefined);
      return;
    }
    const cacheKey = `${state.view.header.id}:${detailRef}:${state.view.capturedAt}`;
    const cached = detailCache.current.get(cacheKey);
    if (cached !== undefined && !isTransientDetail(cached)) {
      setRawDetail(cached);
      setRawLoading(false);
      setDetailError(undefined);
      return;
    }
    const controller = new AbortController();
    setRawDetail(undefined);
    setRawLoading(true);
    setDetailError(undefined);
    const request = selected.itemType === "round-summary"
      ? getRoundDetail(String(state.view.header.id), selected.roundIndex, controller.signal)
      : selected.itemType === "story" && selected.roundIndex && detailRef === `round:${selected.roundIndex}`
        ? getRoundDetail(String(state.view.header.id), selected.roundIndex, controller.signal)
        : getActivityDetail(String(state.view.header.id), detailRef, controller.signal);
    void request
      .then((detail) => {
        if (!isTransientDetail(detail)) detailCache.current.set(cacheKey, detail);
        setRawDetail(detail);
      })
      .catch((error) => {
        if (!controller.signal.aborted) {
          setRawDetail(undefined);
          setDetailError(errorMessage(error));
        }
      })
      .finally(() => {
        if (!controller.signal.aborted) setRawLoading(false);
      });
    return () => controller.abort();
  }, [inspectorMode, selected?.id, selectedDetailRef, state.view?.header.id, state.view?.capturedAt]);

  useEffect(() => {
    if (!selected || !state.view) return;
    const updated = findItem(state.view, selected.id);
    if (updated) setSelected(updated);
  }, [state.view?.capturedAt]);

  useEffect(() => {
    if (inspectorMode !== "lineage") return;
    if (!selected || !state.view) return;
    const detailRef = selectedDetailRef;
    if (!detailRef) {
      setInspectorView(undefined);
      setInspectorLoading(false);
      setInspectorError("当前卡片没有可用的详情引用。");
      return;
    }
    const cacheKey = `${state.view.header.id}:${detailRef}:${state.view.capturedAt}`;
    const cached = inspectorCache.current.get(cacheKey);
    if (cached) {
      setInspectorView(cached);
      setInspectorLoading(false);
      setInspectorError(undefined);
      return;
    }
    const controller = new AbortController();
    setInspectorView(undefined);
    setInspectorLoading(true);
    setInspectorError(undefined);
    void getInspectorView(String(state.view.header.id), detailRef, controller.signal)
      .then((payload) => {
        const prefix = `${state.view!.header.id}:${detailRef}:`;
        for (const key of inspectorCache.current.keys()) {
          if (key.startsWith(prefix)) inspectorCache.current.delete(key);
        }
        inspectorCache.current.set(cacheKey, payload);
        while (inspectorCache.current.size > 32) {
          const oldest = inspectorCache.current.keys().next().value;
          if (!oldest) break;
          inspectorCache.current.delete(oldest);
        }
        setInspectorView(payload);
      })
      .catch((error) => {
        if (!controller.signal.aborted) {
          setInspectorView(undefined);
          setInspectorError(errorMessage(error));
        }
      })
      .finally(() => {
        if (!controller.signal.aborted) setInspectorLoading(false);
      });
    return () => controller.abort();
  }, [inspectorMode, selected?.id, selectedDetailRef, state.view?.header.id, state.view?.capturedAt]);

  useEffect(() => {
    if (!promptRef || !state.view) return;
    const controller = new AbortController();
    setPrompt(undefined); setPromptError(undefined); setPromptLoading(true);
    void getDecisionPrompt(String(state.view.header.id), promptRef, controller.signal)
      .then(setPrompt)
      .catch((error) => {
        if (!controller.signal.aborted) setPromptError(errorMessage(error));
      })
      .finally(() => {
        if (!controller.signal.aborted) setPromptLoading(false);
      });
    return () => controller.abort();
  }, [promptRef, state.view?.header.id]);

  const closeInspector = useCallback(() => {
    pendingFocusRestore.current = { key: inspectorTriggerKey.current, element: inspectorTrigger.current };
    setSelected(null);
    setInspectorHistory([]);
    setInspectorMode("business");
  }, []);
  const closeArtifact = useCallback(() => {
    pendingFocusRestore.current = { key: inspectorTriggerKey.current, element: inspectorTrigger.current };
    setArtifactTarget(null);
  }, []);
  const openPrompt = useCallback((nextPromptRef: string) => {
    promptTrigger.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
    promptTriggerKey.current = promptTrigger.current?.dataset.focusReturn || null;
    setPromptRef(nextPromptRef);
  }, []);
  const openArtifactFromInspector = useCallback(() => {
    if (!selected) return;
    const snapshotRef = artifactRefFor(selected);
    if (!snapshotRef) return;
    inspectorTrigger.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
    inspectorTriggerKey.current = inspectorTrigger.current?.dataset.focusReturn || null;
    setArtifactTarget({ snapshotRef });
  }, [selected]);
  const closePrompt = useCallback(() => {
    pendingPromptFocusRestore.current = {
      key: promptTriggerKey.current,
      element: promptTrigger.current,
    };
    setPromptRef(undefined);
  }, []);

  useEffect(() => {
    const pending = pendingFocusRestore.current;
    if (artifactTarget || !pending) return;
    pendingFocusRestore.current = null;
    let secondFrame = 0;
    const firstFrame = requestAnimationFrame(() => {
      secondFrame = requestAnimationFrame(() => {
        const replacement = pending.key
          ? [...document.querySelectorAll<HTMLElement>("[data-focus-return]")]
            .find((element) => element.dataset.focusReturn === pending.key)
          : null;
        const target = replacement || (pending.element?.isConnected ? pending.element : null);
        target?.focus({ preventScroll: true });
      });
    });
    return () => {
      cancelAnimationFrame(firstFrame);
      if (secondFrame) cancelAnimationFrame(secondFrame);
    };
  }, [selected, artifactTarget]);

  useEffect(() => {
    const pending = pendingPromptFocusRestore.current;
    if (promptRef || !pending) return;
    pendingPromptFocusRestore.current = null;
    let secondFrame = 0;
    const firstFrame = requestAnimationFrame(() => {
      secondFrame = requestAnimationFrame(() => {
        const replacement = pending.key
          ? [...document.querySelectorAll<HTMLElement>("[data-focus-return]")]
            .find((element) => element.dataset.focusReturn === pending.key)
          : null;
        const target = replacement || (pending.element?.isConnected ? pending.element : null);
        target?.focus({ preventScroll: true });
      });
    });
    return () => {
      cancelAnimationFrame(firstFrame);
      if (secondFrame) cancelAnimationFrame(secondFrame);
    };
  }, [promptRef]);

  useEffect(() => { const close = (event: KeyboardEvent) => { if (event.key === "Escape" && !artifactTarget && !promptRef) { closeInspector(); setMobileCanvas(false); } }; window.addEventListener("keydown", close); return () => window.removeEventListener("keydown", close); }, [artifactTarget, closeInspector, promptRef]);

  const open = useCallback((item: CanvasItem, target: OpenTarget) => {
    inspectorTrigger.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
    inspectorTriggerKey.current = inspectorTrigger.current?.dataset.focusReturn || null;
    if (target === "artifact") {
      const snapshotRef = artifactRefFor(item);
      if (!snapshotRef) return;
      setSelected(null);
      setInspectorHistory([]);
      setArtifactTarget({ snapshotRef });
      return;
    }
    setInspectorHistory([]);
    setInspectorMode(target === "lineage" ? "lineage" : "business");
    setSelected(item);
    setRawDetail(undefined);
    setDetailError(undefined);
    setInspectorView(undefined);
    setInspectorError(undefined);
  }, []);
  const navigateInspector = useCallback((activity: RetrievalActivity) => {
    if (!selected || !activity.detailRef) return;
    const roundIndex = "roundIndex" in selected ? selected.roundIndex : 0;
    const branchId = "branchId" in selected ? selected.branchId : 0;
    setInspectorHistory((current) => [...current, selected]);
    setSelected(retrievalActivityItem(activity, roundIndex || 0, branchId || 0));
    setInspectorView(undefined);
    setInspectorError(undefined);
  }, [selected]);
  const navigateInspectorBack = useCallback(() => {
    setInspectorHistory((current) => {
      const parent = current[current.length - 1];
      if (!parent) return current;
      const refreshed = state.view ? findItem(state.view, parent.id) : undefined;
      setSelected(refreshed || parent);
      setInspectorView(undefined);
      setInspectorError(undefined);
      return current.slice(0, -1);
    });
  }, [state.view]);
  const toggleRound = useCallback((roundIndex: number) => {
    expansionPreference.current = "custom";
    setExpandedRounds((current) => { const next = new Set(current); next.has(roundIndex) ? next.delete(roundIndex) : next.add(roundIndex); return next; });
  }, []);
  const toggleBranch = useCallback((roundIndex: number, branchId: number) => setExpandedBranches((current) => {
    expansionPreference.current = "custom";
    const next = new Set(current);
    const key = `${roundIndex}:${branchId}`;
    next.has(key) ? next.delete(key) : next.add(key);
    return next;
  }), []);
  const toggleRetrievalAgent = useCallback((agentId: string) => setExpandedRetrievalAgents((current) => {
    expansionPreference.current = "custom";
    const next = new Set(current);
    next.has(agentId) ? next.delete(agentId) : next.add(agentId);
    return next;
  }), []);
  const navigateRound = useCallback((roundIndex: number) => {
    expansionPreference.current = "custom";
    setExpandedRounds((current) => new Set(current).add(roundIndex));
    setFocusRound((current) => ({ roundIndex, token: (current?.token || 0) + 1 }));
  }, []);
  const allExpanded = state.view
    ? isFullyExpanded(state.view, expandedRounds, expandedBranches, expandedRetrievalAgents)
    : false;
  const toggleAll = useCallback(() => {
    if (!state.view) return;
    if (isFullyExpanded(state.view, expandedRounds, expandedBranches, expandedRetrievalAgents)) {
      expansionPreference.current = "custom";
      setExpandedRounds(new Set());
      setExpandedBranches(new Set());
      setExpandedRetrievalAgents(new Set());
      return;
    }
    expansionPreference.current = "expanded";
    const expanded = expandedStateFor(state.view);
    setExpandedRounds(expanded.rounds);
    setExpandedBranches(expanded.branches);
    setExpandedRetrievalAgents(expanded.retrievalAgents);
  }, [state.view, expandedRounds, expandedBranches, expandedRetrievalAgents]);

  if (state.loading && !state.view) return <LoadingScreen />;
  if (!state.view) return <FatalState message={state.error || "无法读取真实构建数据"} />;
  const view = state.view;
  return <main className={`appFrame ${selected ? "withInspector" : ""} ${selected && inspectorMode === "lineage" ? "withLineageInspector" : ""}`}>
    <TopBar builds={state.builds} selectedId={state.selectedId} view={view} refreshing={state.refreshing} onSelect={state.selectBuild} expandedRounds={expandedRounds} allExpanded={allExpanded} onRound={navigateRound} onToggleAll={toggleAll} />
    {state.error ? <div className="errorBanner"><AlertTriangle size={15} /><span>{state.error}</span></div> : null}
    <div className="workspace">
      <MobileTimeline view={view} expandedRounds={expandedRounds} expandedBranches={expandedBranches} expandedRetrievalAgents={expandedRetrievalAgents} onToggleRound={toggleRound} onToggleBranch={toggleBranch} onToggleRetrievalAgent={toggleRetrievalAgent} onOpen={open} onCanvas={() => setMobileCanvas(true)} />
      <ReactFlowProvider><FlowCanvas view={view} expandedRounds={expandedRounds} expandedBranches={expandedBranches} expandedRetrievalAgents={expandedRetrievalAgents} onToggleRound={toggleRound} onToggleBranch={toggleBranch} onToggleRetrievalAgent={toggleRetrievalAgent} onOpen={open} onPrompt={openPrompt} selectedId={selected?.id} focusRound={focusRound} canvasFullscreen={mobileCanvas} onExitFullscreen={() => setMobileCanvas(false)} /></ReactFlowProvider>
      {selected ? <Inspector item={selected} mode={inspectorMode} onModeChange={setInspectorMode} onClose={closeInspector} onBack={inspectorHistory.length ? navigateInspectorBack : undefined} backLabel={inspectorHistory.length ? `返回${inspectorHistory[inspectorHistory.length - 1].title}` : undefined} onNavigate={navigateInspector} onPrompt={openPrompt} onArtifact={selected.itemType === "final-result" ? openArtifactFromInspector : undefined} detail={rawDetail} detailLoading={rawLoading} detailError={detailError} workbench={inspectorView} workbenchLoading={inspectorLoading} workbenchError={inspectorError} topOverlayOpen={Boolean(artifactTarget || promptRef)} /> : null}
    </div>
    {artifactTarget ? <ScriptArtifactViewer buildId={String(view.header.id)} snapshotRef={artifactTarget.snapshotRef} onClose={closeArtifact} /> : null}
    {promptRef ? <PromptDrawer prompt={prompt} loading={promptLoading} error={promptError} onClose={closePrompt} /> : null}
  </main>;
}

function artifactRefFor(item: CanvasItem): string | undefined {
  if (item.itemType === "final-result") return item.result.artifact.snapshotRef || item.detailRef || "artifact:base:current";
  if (item.itemType === "round-summary") return item.round.result.artifactRef || `artifact:round:${item.roundIndex}`;
  if (item.itemType === "branch-summary" && item.branch.output.type === "script-artifact") return item.branch.output.snapshotRef;
  if (item.itemType === "story") return item.artifactRef;
  return undefined;
}

function inspectorDetailRefFor(item: CanvasItem): string | undefined {
  if (item.itemType === "final-result") return "run:final-result";
  return "detailRef" in item ? item.detailRef : undefined;
}

function findItem(view: ExecutionViewV8, id: string): CanvasItem | undefined {
  if (id === view.planning?.id) return storyItem(view.planning);
  if (id === view.objective.id) return storyItem(view.objective);
  if (id === view.finalResult.id) return finalResultItem(view);
  for (const round of view.rounds) {
    if (id === `round:${round.roundIndex}:summary`) return roundSummaryItem(round);
    const main: StoryNode[] = [round.goal, round.plan.node, ...round.convergenceBatches.flatMap((batch) => [batch.missingReview, batch.missingDecision].filter((node): node is StoryNode => Boolean(node))), round.overallEvaluation, round.result];
    const mainStory = main.find((story) => story.id === id);
    if (mainStory) return storyItem(mainStory, round.roundIndex);
    for (const batch of round.convergenceBatches) {
      if (batch.review?.id === id) return multipathReviewItem(batch.review, round.roundIndex);
      if (batch.decision?.id === id) return multipathDecisionItem(batch.decision, round.roundIndex);
    }
    for (const branch of round.branches) {
      if (id === `round:${round.roundIndex}:branch:${branch.branchId}:summary`) return branchSummaryItem(branch, round.roundIndex);
      const branchStory = branchFlow(branch).find((story) => story.id === id);
      if (branchStory) return storyItem(branchStory, round.roundIndex, branch.branchId);
      if (branch.retrievalStage.id === id) return retrievalStageItem(branch.retrievalStage, round.roundIndex, branch.branchId);
      const direct = branch.retrievalStage.directToolGroups.find((group) => group.id === id);
      if (direct) return retrievalDirectItem(direct, round.roundIndex, branch.branchId);
      for (const group of branch.retrievalStage.directToolGroups) {
        const call = group.calls.find((item) => item.id === id || item.detailRef === id);
        if (call) return retrievalActivityItem({ id: call.id, kind: "tool", label: call.businessLabel, status: call.status, summary: call.resultSummary, detailRef: call.detailRef }, round.roundIndex, branch.branchId);
      }
      const agent = branch.retrievalStage.agentRuns.find((run) => run.id === id);
      if (agent) return retrievalAgentItem(agent, round.roundIndex, branch.branchId);
      for (const run of branch.retrievalStage.agentRuns) {
        const attempt = run.attempts.find((item) => item.id === id);
        if (attempt) return queryAttemptItem(attempt, round.roundIndex, branch.branchId);
      }
    }
  }
  return undefined;
}

function isTransientDetail(detail: unknown) {
  if (!detail || typeof detail !== "object" || Array.isArray(detail)) return false;
  const record = detail as { outcome?: { state?: string }; activities?: Array<{ status?: string }> };
  return record.outcome?.state === "running" || record.activities?.some((item) => item.status === "running") === true;
}

function LoadingScreen() { return <main className="loadingScreen"><h1>正在读取真实构建记录…</h1><div className="skeletonWide"><i /><i /><i /></div></main>; }
function FatalState({ message }: { message: string }) { return <main className="fatalState"><AlertTriangle size={26} /><h1>真实运行数据暂时不可用</h1><p>{message}</p><p>请确认只读数据库连接和可视化后端已经启动。</p></main>; }
