import { describe, expect, it, vi, beforeEach } from "vitest";
import { renderHook, act, waitFor } from "@testing-library/react";
import type { BansByCountryResponse } from "../../types/map";
import { useMapData } from "../useMapData";
import * as api from "../../api/map";

vi.mock("../../api/map");

describe("useMapData", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it("delays loading until the debounced fetch begins", async () => {
    const fetchMock = vi.mocked(api.fetchBansByCountry);
    const firstResponse: BansByCountryResponse = {
      countries: { US: 1 },
      country_names: { US: "United States" },
      bans: [],
      total: 1,
    };
    const secondResponse: BansByCountryResponse = {
      countries: { US: 2 },
      country_names: { US: "United States" },
      bans: [],
      total: 2,
    };

    let secondResolve: (value: BansByCountryResponse) => void;
    fetchMock.mockResolvedValueOnce(firstResponse);
    fetchMock.mockImplementationOnce(
      () =>
        new Promise<BansByCountryResponse>((resolve) => {
          secondResolve = resolve;
        }),
    );

    const { result } = renderHook(() => useMapData("24h", "all", "fail2ban"));

    await act(async () => {
      await new Promise((resolve) => setTimeout(resolve, 310));
    });

    await waitFor(() => {
      expect(result.current.loading).toBe(false);
    });

    expect(fetchMock).toHaveBeenCalledTimes(1);

    act(() => {
      result.current.refresh();
    });

    expect(result.current.loading).toBe(false);

    await act(async () => {
      await new Promise((resolve) => setTimeout(resolve, 100));
    });

    expect(result.current.loading).toBe(false);
    expect(fetchMock).toHaveBeenCalledTimes(1);

    await act(async () => {
      await new Promise((resolve) => setTimeout(resolve, 250));
    });

    expect(result.current.loading).toBe(true);
    expect(fetchMock).toHaveBeenCalledTimes(2);

    await act(async () => {
      secondResolve(secondResponse);
      await Promise.resolve();
    });

    expect(result.current.loading).toBe(false);
    expect(result.current.total).toBe(2);
  });
});
