import {
  fireEvent,
  render,
  screen,
  waitFor,
  within,
} from "@testing-library/react";
import { queryFeaturesByIds } from "@/utils/mapQueryService";
import AnalysisReport, {
  clearAnalysisReportDiameterCache,
  matchesValveAnalysis,
} from "./AnalysisReport";
import { SchemeRecord, ValveIsolationResult } from "./types";
import {
  isAllowedAccidentPipe,
  normalizeValveIsolationResult,
} from "./valveIsolationScope";

jest.mock("@/utils/mapQueryService", () => ({
  queryFeaturesByIds: jest.fn(),
}));

const scheme: SchemeRecord = {
  id: 17,
  schemeName: "burst-report-demo",
  type: "burst_analysis",
  username: "operator",
  create_time: "2026-07-30T08:00:00+08:00",
  startTime: "2026-07-30T09:00:00+08:00",
  schemeDetail: {
    burst_ID: ["P-1", "P-2"],
    burst_size: [120, 80],
    modify_total_duration: 5400,
    modify_fixed_pump_pattern: null,
    modify_valve_opening: null,
    modify_variable_pump_pattern: null,
  },
};

const valveResult: ValveIsolationResult = {
  accident_elements: ["P-1"],
  affected_nodes: ["J-1", "J-2"],
  must_close_valves: ["V-1"],
  optional_valves: ["V-2"],
  isolatable: true,
};

const feature = (id: string, diameter: number) => ({
  getProperties: () => ({ id, diameter }),
});

describe("AnalysisReport", () => {
  beforeEach(() => {
    (queryFeaturesByIds as jest.Mock).mockImplementation(
      async (_ids: string[], layerName: string) =>
        layerName === "geo_pipes_mat"
          ? [feature("P-1", 315)]
          : [feature("P-2", 800)],
    );
  });

  afterEach(() => {
    clearAnalysisReportDiameterCache();
    jest.clearAllMocks();
    document.body.classList.remove("burst-analysis-report-printing");
  });

  it("guides the user to choose a scheme when no report is active", () => {
    render(
      <AnalysisReport
        scheme={null}
        valveResult={null}
        disabledValves={[]}
        generatedAt={null}
      />,
    );

    expect(screen.getByText("尚未选择分析方案")).toBeInTheDocument();
    expect(
      screen.getByText(
        "请在“方案查询”中打开一个方案，再查看分析报告。",
      ),
    ).toBeInTheDocument();
  });

  it("renders the selected scheme, pipe data, and matching valve result", async () => {
    render(
      <AnalysisReport
        scheme={scheme}
        valveResult={valveResult}
        disabledValves={["V-3"]}
        generatedAt={new Date("2026-07-30T10:00:00+08:00")}
      />,
    );

    const preview = within(
      screen.getByTestId("burst-analysis-report-preview"),
    );
    expect(
      preview.getByRole("heading", { name: "爆管分析报告" }),
    ).toBeInTheDocument();
    expect(preview.getByText("burst-report-demo")).toBeInTheDocument();
    expect(preview.getByText("可隔离")).toBeInTheDocument();
    expect(preview.getByText("V-1")).toBeInTheDocument();
    expect(preview.getByText("V-3")).toBeInTheDocument();

    await waitFor(() => {
      expect(preview.getByText("315 mm")).toBeInTheDocument();
      expect(preview.getByText("800 mm")).toBeInTheDocument();
    });
    expect(queryFeaturesByIds).toHaveBeenCalledWith(
      ["P-2"],
      "geo_pipes",
    );
  });

  it("does not mix an unrelated valve analysis into the report", async () => {
    render(
      <AnalysisReport
        scheme={scheme}
        valveResult={{ ...valveResult, accident_elements: ["P-99"] }}
        disabledValves={[]}
        generatedAt={new Date("2026-07-30T10:00:00+08:00")}
      />,
    );

    const preview = within(
      screen.getByTestId("burst-analysis-report-preview"),
    );
    expect(
      preview.getByText(/尚未对本方案执行匹配的关阀分析/),
    ).toBeInTheDocument();
    expect(preview.queryByText("V-1")).not.toBeInTheDocument();
    expect(
      matchesValveAnalysis(scheme, {
        ...valveResult,
        accident_elements: ["P-99"],
      }),
    ).toBe(false);
    await waitFor(() =>
      expect(preview.getByText("315 mm")).toBeInTheDocument(),
    );
  });

  it("shows only the affected count for a non-isolatable result", async () => {
    const legacyAffectedNodes = Array.from(
      { length: 100 },
      (_, index) => `legacy-node-${index}`,
    );
    const nonIsolatableResult = {
      ...valveResult,
      affected_nodes: legacyAffectedNodes,
      affected_node_count: 85747,
      must_close_valves: [],
      isolatable: false,
    };

    render(
      <AnalysisReport
        scheme={scheme}
        valveResult={nonIsolatableResult}
        disabledValves={[]}
        generatedAt={new Date("2026-07-30T10:00:00+08:00")}
      />,
    );

    const preview = within(
      screen.getByTestId("burst-analysis-report-preview"),
    );
    expect(preview.getByText("85747 个")).toBeInTheDocument();
    expect(
      preview.getByText("不可隔离，未生成受影响节点清单。"),
    ).toBeInTheDocument();
    expect(preview.queryByText("legacy-node-0")).not.toBeInTheDocument();
    expect(preview.queryByText("legacy-node-99")).not.toBeInTheDocument();
    await waitFor(() =>
      expect(preview.getByText("315 mm")).toBeInTheDocument(),
    );
  });

  it("limits scheme-bound valve analysis to the scheme accident pipes", () => {
    expect(isAllowedAccidentPipe("P-1", ["P-1", "P-2"])).toBe(true);
    expect(isAllowedAccidentPipe("P-99", ["P-1", "P-2"])).toBe(false);
    expect(isAllowedAccidentPipe("P-99", undefined)).toBe(true);
  });

  it("normalizes legacy valve results without changing isolatable lists", () => {
    expect(
      normalizeValveIsolationResult({
        ...valveResult,
        affected_nodes: ["J-1", "J-2", "J-3"],
        must_close_valves: [],
        isolatable: false,
      }),
    ).toMatchObject({
      affected_nodes: [],
      affected_node_count: 3,
      isolatable: false,
    });

    expect(normalizeValveIsolationResult(valveResult)).toMatchObject({
      affected_nodes: ["J-1", "J-2"],
      affected_node_count: 2,
      isolatable: true,
    });
  });

  it("prints only after assigning the report print state", async () => {
    const originalTitle = document.title;
    const print = jest
      .spyOn(window, "print")
      .mockImplementation(() => undefined);

    render(
      <AnalysisReport
        scheme={scheme}
        valveResult={null}
        disabledValves={[]}
        generatedAt={new Date("2026-07-30T10:00:00+08:00")}
      />,
    );

    const preview = within(
      screen.getByTestId("burst-analysis-report-preview"),
    );
    await waitFor(() =>
      expect(preview.getByText("315 mm")).toBeInTheDocument(),
    );
    expect(
      document.querySelector(".burst-analysis-report-print-root"),
    ).not.toBeInTheDocument();
    fireEvent.click(
      screen.getByRole("button", { name: "打印/保存 PDF" }),
    );
    expect(print).toHaveBeenCalledTimes(1);
    expect(
      document.querySelector(".burst-analysis-report-print-root"),
    ).toBeInTheDocument();
    expect(document.body).toHaveClass("burst-analysis-report-printing");
    expect(document.title).toBe("爆管分析报告-burst-report-demo");

    fireEvent(window, new Event("afterprint"));
    expect(document.body).not.toHaveClass(
      "burst-analysis-report-printing",
    );
    expect(document.title).toBe(originalTitle);
    print.mockRestore();
  });

  it("reuses pipe diameters after the report is remounted", async () => {
    const props = {
      scheme,
      valveResult: null,
      disabledValves: [],
      generatedAt: new Date("2026-07-30T10:00:00+08:00"),
    };
    const firstRender = render(<AnalysisReport {...props} />);

    await waitFor(() =>
      expect(
        within(screen.getByTestId("burst-analysis-report-preview")).getByText(
          "800 mm",
        ),
      ).toBeInTheDocument(),
    );
    expect(queryFeaturesByIds).toHaveBeenCalledTimes(2);

    firstRender.unmount();
    render(<AnalysisReport {...props} />);

    expect(
      within(screen.getByTestId("burst-analysis-report-preview")).getByText(
        "800 mm",
      ),
    ).toBeInTheDocument();
    expect(queryFeaturesByIds).toHaveBeenCalledTimes(2);
  });
});
