import { fireEvent, render, screen } from "@testing-library/react";
import { ReactFlowProvider } from "@xyflow/react";
import { describe, expect, it, vi } from "vitest";
import { CandidateCard } from "@/components/flow/CandidateCard";
import { RoundSummaryCard } from "@/components/flow/RoundSummaryCard";
import { StoryCard } from "@/components/flow/StoryCard";
import { RunResultCard } from "@/components/flow/RunResultCard";
import { AgentDecisionCard } from "@/components/decision/AgentDecisionCard";
import { MobileTimeline } from "@/components/workbench/MobileTimeline";
import { branchSummaryItem, finalResultItem, multipathDecisionItem, multipathReviewItem, roundSummaryItem, storyItem } from "@/lib/layout";
import { makeBranch, makeRound, makeStory, makeView } from "@/tests/fixtures";

const base = { onOpen: vi.fn(), onToggleRound: vi.fn(), onToggleBranch: vi.fn() };
const nodeProps = { selected: false, zIndex: 1, isConnectable: false, positionAbsoluteX: 0, positionAbsoluteY: 0, dragging: false, draggable: true, selectable: true, deletable: false };
const wrap = (node: React.ReactNode) => render(<ReactFlowProvider>{node}</ReactFlowProvider>);

describe("V8 business cards", () => {
  it("labels the creation objective action as details while keeping decision actions explicit", () => {
    const objective = storyItem(makeView().objective);
    const { unmount } = wrap(<AgentDecisionCard id="objective" type="agentDecision" {...nodeProps} data={{ ...base, item: objective }} />);
    expect(screen.getByRole("button", { name: "查看详情" })).toBeInTheDocument();
    expect(screen.queryByRole("button", { name: "查看决策" })).not.toBeInTheDocument();

    unmount();
    const dataDecision = storyItem(makeStory("data", "data-decision"), 1, 1);
    wrap(<AgentDecisionCard id="data" type="agentDecision" {...nodeProps} data={{ ...base, item: dataDecision }} />);
    expect(screen.getByRole("button", { name: "查看决策" })).toBeInTheDocument();
  });

  it("shows implementer data decisions as data decisions", () => {
    const item = storyItem(makeStory("data", "data-decision"), 1, 1);
    wrap(<AgentDecisionCard id="data" type="agentDecision" {...nodeProps} data={{ ...base, item }} />);
    expect(screen.getByText("数据取舍")).toBeInTheDocument();
    expect(screen.getByText("实现 Agent")).toBeInTheDocument();
  });

  it("distinguishes domain-information output from candidate script", () => {
    const branch = makeBranch(1, 4);
    wrap(<CandidateCard id="branch" type="candidateCard" {...nodeProps} data={{ ...base, item: branchSummaryItem(branch, 1), expanded: false }} />);
    expect(screen.getByText(/领域信息/)).toBeInTheDocument();
    expect(screen.getByText("领域事实")).toBeInTheDocument();
    expect(screen.getByRole("button", { name: /展开方案过程/ })).toBeInTheDocument();
    expect(screen.getByRole("button", { name: /查看方案汇总/ })).toBeInTheDocument();
    expect(screen.queryByRole("button", { name: "查看完整候选脚本" })).not.toBeInTheDocument();
  });

  it("opens the same artifact viewer from collapsed and expanded candidate output", () => {
    const onOpen = vi.fn();
    const branch = makeBranch(1, 1);
    const summaryItem = branchSummaryItem(branch, 1);
    const { unmount } = wrap(<CandidateCard id="branch" type="candidateCard" {...nodeProps} data={{ ...base, onOpen, item: summaryItem, expanded: false }} />);
    fireEvent.click(screen.getByRole("button", { name: "查看完整候选脚本" }));
    expect(onOpen).toHaveBeenCalledWith(summaryItem, "artifact");

    unmount();
    onOpen.mockClear();
    const creativeOutput = makeStory("output", "candidate-output", "decision", { artifactRef: "artifact:branch:1" });
    const outputItem = storyItem(creativeOutput, 1, 1);
    wrap(<AgentDecisionCard id="output" type="agentDecision" {...nodeProps} data={{ ...base, onOpen, onPrompt: vi.fn(), item: outputItem }} />);
    fireEvent.click(screen.getByRole("button", { name: "查看完整候选脚本" }));
    expect(onOpen).toHaveBeenCalledWith(outputItem, "artifact");
    expect(screen.queryByRole("button", { name: "查看实现任务与提示词" })).not.toBeInTheDocument();
  });

  it("keeps the candidate artifact action on an expanded mobile creative decision", () => {
    const view = makeView(1, 1, 1);
    const branch = view.rounds[0].branches[0];
    branch.output.node = makeStory("mobile-output", "candidate-output", "decision", { artifactRef: "artifact:branch:1" });
    const onOpen = vi.fn();
    render(<MobileTimeline
      view={view}
      expandedRounds={new Set([1])}
      expandedBranches={new Set(["1:1"])}
      expandedRetrievalAgents={new Set()}
      onToggleRound={vi.fn()}
      onToggleBranch={vi.fn()}
      onToggleRetrievalAgent={vi.fn()}
      onOpen={onOpen}
      onCanvas={vi.fn()}
    />);

    const artifactButtons = screen.getAllByRole("button", { name: "查看完整候选脚本" });
    expect(artifactButtons).toHaveLength(2);
    fireEvent.click(artifactButtons[1]);
    expect(onOpen).toHaveBeenCalledWith(expect.objectContaining({ id: "mobile-output" }), "artifact");
    expect(screen.queryByText("查看实现任务与提示词")).not.toBeInTheDocument();
  });

  it("keeps runtime association in technical detail instead of the business card", () => {
    const story = makeStory("evaluation", "round-evaluation");
    story.sourceNotice = "runtime-associated";
    story.detailRef = "event:987";
    wrap(<StoryCard id="evaluation" type="storyCard" {...nodeProps} data={{ ...base, item: storyItem(story, 1) }} />);
    expect(screen.queryByText("运行记录关联")).not.toBeInTheDocument();
    expect(screen.queryByText("event:987")).not.toBeInTheDocument();
  });

  it("keeps objective and step summaries visible on the mobile timeline", () => {
    const view = makeView(1, 1, 1);
    view.objective.card = {
      primary: { label: "核心目标", value: "形成清晰可执行的创作方向" },
      secondary: [
        { key: "inputs", label: "决策前读取", value: "选题 · 账号段落模式 · 账号人设" },
        { key: "shape", label: "目标结构", value: "2 个创作目标 · 1 个领域评估维度" },
      ],
    };
    view.objective.decision!.card = view.objective.card;
    view.rounds[0].plan.node.card = {
      primary: { label: "实现方式", value: "分工 · 2 路实现" },
      secondary: [
        { key: "paths", label: "各路任务", value: "方案 1：补充步骤；方案 2：增强信息" },
        { key: "reason", label: "规划理由", value: "两个任务没有强依赖" },
      ],
    };
    view.rounds[0].plan.node.decision!.card = view.rounds[0].plan.node.card;

    render(<MobileTimeline
      view={view}
      expandedRounds={new Set([1])}
      expandedBranches={new Set()}
      expandedRetrievalAgents={new Set()}
      onToggleRound={vi.fn()}
      onToggleBranch={vi.fn()}
      onToggleRetrievalAgent={vi.fn()}
      onOpen={vi.fn()}
      onCanvas={vi.fn()}
    />);

    expect(screen.getByText("选题 · 账号段落模式 · 账号人设")).toBeInTheDocument();
    expect(screen.getByText("2 个创作目标 · 1 个领域评估维度")).toBeInTheDocument();
    expect(screen.getByText("方案 1：补充步骤；方案 2：增强信息")).toBeInTheDocument();
    expect(screen.getByText("两个任务没有强依赖")).toBeInTheDocument();
    expect(screen.getAllByText("创作目标")).toHaveLength(1);
    expect(screen.getAllByText("本轮目标")).toHaveLength(1);
    expect(screen.getAllByText("实现规划")).toHaveLength(1);
  });

  it("keeps candidate review and main decision as two distinct cards", () => {
    const batch = makeRound(1, 2, 1).convergenceBatches[0];
    const { unmount } = wrap(<AgentDecisionCard id="review" type="agentDecision" {...nodeProps} data={{ ...base, item: multipathReviewItem(batch.review!, 1) }} />);
    expect(screen.getByText("多方案评审 Agent")).toBeInTheDocument();
    expect(screen.getAllByText("评审建议").length).toBeGreaterThanOrEqual(1);
    unmount();

    wrap(<AgentDecisionCard id="decision" type="agentDecision" {...nodeProps} data={{ ...base, item: multipathDecisionItem(batch.decision!, 1) }} />);
    expect(screen.getByText("主 Agent")).toBeInTheDocument();
    expect(screen.getAllByText("最终决定").length).toBeGreaterThanOrEqual(1);
    expect(screen.getByText("方案 1、方案 2")).toBeInTheDocument();
  });

  it("describes a stopped open branch as incomplete process instead of missing data", () => {
    const branch = makeBranch(1, 1);
    branch.status = "open";
    branch.outcome.status = "open";
    branch.outcome.label = "构建停止时尚未处置";
    branch.outcome.sourceNotice = "process-incomplete";
    wrap(<CandidateCard id="branch" type="candidateCard" {...nodeProps} data={{ ...base, item: branchSummaryItem(branch, 1), expanded: false }} />);
    expect(screen.getByText("尚未处置")).toBeInTheDocument();
    expect(screen.getByText("流程未完成")).toBeInTheDocument();
    expect(screen.queryByText("记录缺失")).not.toBeInTheDocument();
  });

  it("keeps round process and detail actions visible", () => {
    const onOpen = vi.fn();
    const round = makeRound(1, 3, 2);
    const item = roundSummaryItem(round);
    wrap(<RoundSummaryCard id="round" type="roundSummary" {...nodeProps} data={{ ...base, onOpen, item }} />);
    expect(screen.getByRole("button", { name: /展开本轮/ })).toBeInTheDocument();
    expect(screen.getByRole("button", { name: /查看详情/ })).toBeInTheDocument();
    fireEvent.click(screen.getByRole("button", { name: "查看本轮产出脚本表" }));
    expect(onOpen).toHaveBeenCalledWith(item, "artifact");
    expect(screen.getByText("本轮目标 1")).toBeInTheDocument();
    expect(screen.getByText("3 项具体任务 · 完整内容见详情")).toBeInTheDocument();
    expect(screen.getByText("分工 · 3 个实现方案")).toBeInTheDocument();
    expect(screen.getByText("2 个决策批次 · 涉及方案 1、2、3")).toBeInTheDocument();
    expect(screen.getByText("形成第 1 轮内容产出")).toBeInTheDocument();
  });

  it("opens the round artifact from the expanded round result", () => {
    const onOpen = vi.fn();
    const result = makeStory("round:1:result", "round-result", "result", { artifactRef: "artifact:round:1" });
    const item = storyItem(result, 1);
    wrap(<StoryCard id="round-result" type="storyCard" {...nodeProps} data={{ ...base, onOpen, item }} />);
    fireEvent.click(screen.getByRole("button", { name: "查看本轮产出脚本表" }));
    expect(onOpen).toHaveBeenCalledWith(item, "artifact");
  });

  it("labels a completed round without an overall evaluation explicitly", () => {
    const result = makeStory("round:1:result", "round-result", "result", { status: "not-evaluated" });
    wrap(<StoryCard id="round-result" type="storyCard" {...nodeProps} data={{ ...base, item: storyItem(result, 1) }} />);
    expect(screen.getByText("未整体评审")).toBeInTheDocument();
    expect(screen.queryByText("尚无结论")).not.toBeInTheDocument();
  });

  it("uses the run-aware decision summary instead of a generic missing-record label", () => {
    const round = makeRound(1, 2, 0);
    round.summary.decision.summary = "构建停止前尚未形成多路决策";
    wrap(<RoundSummaryCard id="round" type="roundSummary" {...nodeProps} data={{ ...base, item: roundSummaryItem(round) }} />);
    expect(screen.getByText("构建停止前尚未形成多路决策")).toBeInTheDocument();
    expect(screen.queryByText("未记录主 Agent 多路决策")).not.toBeInTheDocument();
  });

  it("opens the dedicated artifact viewer from the final result", () => {
    const onOpen = vi.fn();
    const view = makeView();
    view.finalResult.summary = "脚本构建终结：已经完成结构、内容和表达检查。\n1. 脚本结构概述：四个段落已经形成完整闭环。";
    const item = finalResultItem(view);
    wrap(<RunResultCard id="final" type="finalResult" {...nodeProps} data={{ ...base, onOpen, item }} />);
    expect(screen.getByText("当前主脚本已经形成")).toBeInTheDocument();
    expect(screen.getByText("构建摘要")).toBeInTheDocument();
    expect(screen.getByText("3", { selector: ".artifactStats b" })).toBeInTheDocument();
    fireEvent.click(screen.getByRole("button", { name: "查看结果详情" }));
    expect(onOpen).toHaveBeenCalledWith(item, "business");
    onOpen.mockClear();
    fireEvent.click(screen.getByRole("button", { name: "查看完整主脚本" }));
    expect(onOpen).toHaveBeenCalledWith(item, "artifact");
    onOpen.mockClear();
    fireEvent.keyDown(screen.getByRole("button", { name: "查看完整主脚本" }), { key: "Enter" });
    expect(onOpen).not.toHaveBeenCalledWith(item, "business");
  });
});
