import { describe, it, expect, vi, beforeEach } from "vitest";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { FluentProvider, webLightTheme } from "@fluentui/react-components";
import { JailDistributionChart } from "../JailDistributionChart";
import * as chartTheme from "../../utils/chartTheme";
import * as useJailDistributionModule from "../../hooks/useJailDistribution";
import type { UseJailDistributionResult } from "../../hooks/useJailDistribution";

vi.mock("recharts", () => ({
  ResponsiveContainer: ({ children }: { children: React.ReactNode }) => (
    <div>{children}</div>
  ),
  BarChart: ({ children }: { children: React.ReactNode }) => (
    <div data-testid="bar-chart">{children}</div>
  ),
  Bar: () => null,
  CartesianGrid: () => null,
  XAxis: () => null,
  YAxis: () => null,
  Tooltip: () => null,
  Cell: () => null,
}));

vi.mock("../../hooks/useJailDistribution");

function wrap(ui: React.ReactElement) {
  return render(
    <FluentProvider theme={webLightTheme}>{ui}</FluentProvider>,
  );
}

const defaultResult: UseJailDistributionResult = {
  jails: [],
  total: 0,
  isLoading: false,
  error: null,
  reload: vi.fn(),
};

function mockHook(overrides: Partial<UseJailDistributionResult>) {
  vi.mocked(useJailDistributionModule.useJailDistribution).mockReturnValue({
    ...defaultResult,
    ...overrides,
  });
}

beforeEach(() => {
  vi.mocked(useJailDistributionModule.useJailDistribution).mockReturnValue(
    defaultResult,
  );
});

describe("JailDistributionChart", () => {
  it("shows a spinner while loading", () => {
    mockHook({ isLoading: true });
    wrap(<JailDistributionChart timeRange="24h" origin="all" />);
    expect(screen.getByRole("progressbar")).toBeInTheDocument();
  });

  it("shows error message and retry button on error", async () => {
    const reload = vi.fn();
    mockHook({ error: "Request failed", reload });
    const user = userEvent.setup();
    wrap(<JailDistributionChart timeRange="24h" origin="all" />);
    expect(screen.getByText("Request failed")).toBeInTheDocument();
    await user.click(screen.getByRole("button", { name: /retry/i }));
    expect(reload).toHaveBeenCalledOnce();
  });

  it("shows empty state when jails array is empty", () => {
    mockHook({ jails: [], total: 0 });
    wrap(<JailDistributionChart timeRange="24h" origin="all" />);
    expect(screen.getByText(/no bans/i)).toBeInTheDocument();
  });

  it("renders chart when jail data is present", () => {
    mockHook({
      jails: [
        { jail: "sshd", count: 42 },
        { jail: "nginx-http-auth", count: 7 },
      ],
      total: 49,
    });
    wrap(<JailDistributionChart timeRange="24h" origin="all" />);
    expect(screen.getByTestId("bar-chart")).toBeInTheDocument();
  });

  it("memoizes token resolution across rerenders", () => {
    const spy = vi.spyOn(chartTheme, "resolveFluentToken");
    const props = { timeRange: "24h" as const, origin: "all" as const };

    const { rerender } = wrap(<JailDistributionChart {...props} />);
    expect(spy).toHaveBeenCalledTimes(3);

    rerender(
      <FluentProvider theme={webLightTheme}>
        <JailDistributionChart {...props} />
      </FluentProvider>,
    );
    expect(spy).toHaveBeenCalledTimes(3);

    spy.mockRestore();
  });
});
