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 { BanTrendChart } from "../BanTrendChart";
import * as chartTheme from "../../utils/chartTheme";
import * as useBanTrendModule from "../../hooks/useBanTrend";
import type { UseBanTrendResult } from "../../hooks/useBanTrend";
import type { BanTrendBucket } from "../../types/ban";

vi.mock("recharts", () => ({
  ResponsiveContainer: ({ children }: { children: React.ReactNode }) => (
    <div>{children}</div>
  ),
  AreaChart: ({ children }: { children: React.ReactNode }) => (
    <div data-testid="area-chart">{children}</div>
  ),
  Area: () => null,
  CartesianGrid: () => null,
  XAxis: () => null,
  YAxis: () => null,
  Tooltip: () => null,
  defs: () => null,
  linearGradient: () => null,
  stop: () => null,
}));

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

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

const defaultResult: UseBanTrendResult = {
  buckets: [],
  bucketSize: "1h",
  isLoading: false,
  error: null,
  reload: vi.fn(),
};

function mockHook(overrides: Partial<UseBanTrendResult>) {
  vi.mocked(useBanTrendModule.useBanTrend).mockReturnValue({
    ...defaultResult,
    ...overrides,
  });
}

beforeEach(() => {
  vi.mocked(useBanTrendModule.useBanTrend).mockReturnValue(defaultResult);
});

describe("BanTrendChart", () => {
  it("shows a spinner while loading", () => {
    mockHook({ isLoading: true });
    wrap(<BanTrendChart 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: "Failed to fetch trend", reload });
    const user = userEvent.setup();
    wrap(<BanTrendChart timeRange="24h" origin="all" />);
    expect(screen.getByText("Failed to fetch trend")).toBeInTheDocument();
    await user.click(screen.getByRole("button", { name: /retry/i }));
    expect(reload).toHaveBeenCalledOnce();
  });

  it("shows empty state when all buckets have zero count", () => {
    const emptyBuckets: BanTrendBucket[] = [
      { timestamp: "2024-01-01T00:00:00Z", count: 0 },
      { timestamp: "2024-01-01T01:00:00Z", count: 0 },
    ];
    mockHook({ buckets: emptyBuckets });
    wrap(<BanTrendChart timeRange="24h" origin="all" />);
    expect(screen.getByText("No bans in this time range.")).toBeInTheDocument();
  });

  it("renders chart when data is present", () => {
    const buckets: BanTrendBucket[] = [
      { timestamp: "2024-01-01T00:00:00Z", count: 5 },
      { timestamp: "2024-01-01T01:00:00Z", count: 12 },
    ];
    mockHook({ buckets });
    wrap(<BanTrendChart timeRange="24h" origin="all" />);
    expect(screen.getByTestId("area-chart")).toBeInTheDocument();
  });

  it("memoizes token resolution across rerenders", () => {
    const spy = vi.spyOn(chartTheme, "resolveFluentToken");
    const props = { timeRange: "24h" as const, origin: "all" as const };
    mockHook({ buckets: [{ timestamp: "2024-01-01T00:00:00Z", count: 5 }] });

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

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

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