import { fireEvent, render, screen } from "@testing-library/react";
import { afterEach, describe, expect, it, vi } from "vitest";
import { DataFlowRenderer } from "@/components/inspector/DataFlowRenderer";
import { Inspector } from "@/components/inspector/Inspector";
import { getCardData } from "@/lib/api";
import { finalResultItem, storyItem } from "@/lib/layout";
import type { CardBusinessData } from "@/lib/types";
import { makeStory, makeView } from "@/tests/fixtures";

function makeCardData(overrides: Partial<CardBusinessData> = {}): CardBusinessData {
  return {
    cardKind: "data-decision",
    relationKind: "business-record",
    completeness: "partial",
    businessInputs: [{
      id: "input:cases",
      label: "候选案例",
      value: "案例 A、案例 B",
      source: { kind: "database", label: "取数结果", ref: "retrieval-agent:3786", fieldPath: "output.cases" },
      relation: "available-upstream",
      completeness: "complete",
    }],
    runtime: {
      summary: "实现 Agent 根据可用材料形成取舍记录。",
      units: [{ id: "event:3786", label: "解构 Case Agent", status: "completed", durationMs: 1250, summary: "返回两个候选案例。", input: "查找相关案例", output: { count: 2 }, eventRef: "event:3786", completeness: "complete" }],
      calculation: "按同一分支和时间顺序聚合，不使用文本相似度补链。",
    },
    businessOutputs: [{
      id: "output:decision",
      label: "明确取舍",
      value: "采用案例 A，舍弃案例 B。",
      source: { kind: "database", label: "数据取舍记录", ref: "data-decision:115" },
      relation: "persisted-output",
      completeness: "complete",
    }],
    displayUse: {
      businessModules: [{ id: "decision", title: "明确取舍", sourceIds: ["output:decision"] }],
      cardFields: [{ key: "headline", label: "取舍结论", sourceIds: ["output:decision"], transform: "直接使用结构化结论" }],
      unusedSourceIds: ["input:cases"],
    },
    notices: [{ level: "warning", message: "上游材料存在，但运行记录没有证明它被本次决策采用。" }],
    ...overrides,
  };
}

describe("card data flow", () => {
  afterEach(() => vi.unstubAllGlobals());

  it("keeps the old Inspector focused on business, changes and technical detail", () => {
    const item = storyItem(makeStory("candidate", "candidate-output", "result", { hasChanges: true }));
    const detail = { businessSections: [], changes: { sections: [], exactness: "exact" }, technical: {} };
    render(<Inspector item={item} tab="business" onTab={vi.fn()} onClose={vi.fn()} detail={detail} />);

    expect(screen.getAllByRole("tab").map((tab) => tab.textContent)).toEqual(["业务详情", "产出与改动", "技术详情"]);
  });

  it("renders the vertical lineage, explicit uncertainty and display mappings", () => {
    render(<DataFlowRenderer data={makeCardData()} />);

    expect(screen.getByRole("heading", { name: "数据关系与完整性" })).toBeInTheDocument();
    expect(screen.getByText("业务记录")).toBeInTheDocument();
    expect(screen.getByText("部分记录")).toBeInTheDocument();
    expect(screen.getByRole("heading", { name: "业务输入" })).toBeInTheDocument();
    expect(screen.getByRole("heading", { name: "运行过程 / I/O" })).toBeInTheDocument();
    expect(screen.getByRole("heading", { name: "业务输出" })).toBeInTheDocument();
    expect(screen.getByRole("heading", { name: "页面如何使用这些数据" })).toBeInTheDocument();
    expect(screen.getByText("可确认是上游信息，但无法确认是否被本次决策采用")).toBeInTheDocument();
    expect(screen.getAllByText("数据库")).toHaveLength(2);
    expect(screen.getByText("直接使用结构化结论")).toBeInTheDocument();
    expect(screen.getAllByText("F2")).toHaveLength(3);
  });

  it("uses keyboard-friendly disclosures for runtime units and long values", () => {
    const longText = "完整业务正文。".repeat(150);
    render(<DataFlowRenderer data={makeCardData({ businessOutputs: [{
      id: "output:decision",
      label: "完整正文",
      value: longText,
      source: { kind: "artifact", label: "候选快照" },
      relation: "persisted-output",
      completeness: "complete",
    }] })} />);

    const runtimeSummary = screen.getByText("解构 Case Agent").closest("summary");
    expect(runtimeSummary).toHaveAttribute("aria-expanded", "false");
    fireEvent.click(runtimeSummary!);
    expect(runtimeSummary).toHaveAttribute("aria-expanded", "true");
    expect(screen.getByText("输入")).toBeInTheDocument();

    const longSummary = screen.getByText("展开完整内容").closest("summary");
    expect(longSummary).toHaveAttribute("aria-expanded", "false");
    expect(screen.queryByText(longText)).not.toBeInTheDocument();
    fireEvent.click(longSummary!);
    expect(screen.getByText("收起完整内容").closest("summary")).toHaveAttribute("aria-expanded", "true");
    expect(screen.getByText(longText)).toBeInTheDocument();
  });

  it("shows dedicated loading, error and missing-reference states", () => {
    const { rerender } = render(<DataFlowRenderer loading />);
    expect(screen.getByLabelText("正在加载数据流")).toBeInTheDocument();
    rerender(<DataFlowRenderer error="API 503" />);
    expect(screen.getByText("数据流暂时不可用")).toBeInTheDocument();
    expect(screen.getByText("API 503")).toBeInTheDocument();
    rerender(<DataFlowRenderer missingRef />);
    expect(screen.getByText("没有稳定的数据定位")).toBeInTheDocument();
  });

  it("requests card data with the run and encoded detail reference", async () => {
    const payload = makeCardData();
    const fetchMock = vi.fn().mockResolvedValue({ ok: true, json: async () => payload });
    vi.stubGlobal("fetch", fetchMock);

    await expect(getCardData("443", "run:final-result")).resolves.toEqual(payload);
    expect(fetchMock).toHaveBeenCalledWith(
      "http://127.0.0.1:8788/api/script-builds/443/card-data/run%3Afinal-result",
      expect.objectContaining({ cache: "no-store" }),
    );
  });

  it("uses an independent final-result detail while preserving the artifact action", () => {
    const view = makeView();
    const item = finalResultItem(view);
    const onArtifact = vi.fn();
    expect(item.detailRef).toBe("run:final-result");

    render(<Inspector item={item} tab="business" onTab={vi.fn()} onClose={vi.fn()} onArtifact={onArtifact} detail={{ businessSections: [], technical: {} }} />);
    fireEvent.click(screen.getByRole("button", { name: "查看当前主脚本" }));
    expect(onArtifact).toHaveBeenCalledOnce();
    expect(view.finalResult.artifact.snapshotRef).toBe("artifact:base:current");
  });
});
