/**
 * @jest-environment jsdom
 *
 */

import { renderHook, waitFor } from "@testing-library/react";
import { http, HttpResponse } from "msw";
import { useCurrencyData } from "../useMarketDataProvider";
import { marketApi } from "../../state-manager/marketApi";
import { server } from "@tests/server";
import { createTestStore, createWrapper } from "@tests/test-helpers/testUtils";

jest.mock("../../utils/timers", () => ({
  ...jest.requireActual("../../utils/timers"),
  REFETCH_TIME_ONE_MINUTE: 50,
  BASIC_REFETCH: 2,
}));

describe("useCurrencyData", () => {
  let store: ReturnType<typeof createTestStore>;
  let requestCount: number;
  let lastRequestUrl: string | null;
  let requestListener: (args: { request: Request }) => void;

  beforeAll(() => server.listen());

  afterEach(() => {
    server.events.removeListener("request:start", requestListener);
    server.resetHandlers();
    store.dispatch(marketApi.util.resetApiState());
  });

  afterAll(() => server.close());

  beforeEach(() => {
    store = createTestStore([marketApi], { disableSerializableCheck: true });
    requestCount = 0;
    lastRequestUrl = null;
    requestListener = ({ request }: { request: Request }) => {
      if (request.url.includes("/v3/markets")) {
        requestCount += 1;
        lastRequestUrl = request.url;
      }
    };
    server.events.on("request:start", requestListener);
  });

  it("should call API with correct params and polling interval config", async () => {
    const wrapper = createWrapper(store);
    const { unmount } = renderHook(
      () => useCurrencyData({ id: "bitcoin", counterCurrency: "usd" }),
      { wrapper },
    );

    await waitFor(() => expect(requestCount).toBe(1));

    const url = new URL(lastRequestUrl ?? "");
    expect(url.searchParams.get("ids")).toBe("bitcoin");
    expect(url.searchParams.get("to")).toBe("usd");

    unmount();
  });

  it("should use 180000ms (3 minutes) as polling interval in production", () => {
    const { REFETCH_TIME_ONE_MINUTE, BASIC_REFETCH } = jest.requireActual("../../utils/timers");
    expect(REFETCH_TIME_ONE_MINUTE * BASIC_REFETCH).toBe(180000);
  });

  it("should pass id and counterCurrency to the query", async () => {
    const wrapper = createWrapper(store);
    const { unmount } = renderHook(
      () => useCurrencyData({ id: "solana", counterCurrency: "gbp" }),
      { wrapper },
    );

    await waitFor(() => expect(requestCount).toBe(1));

    const url = new URL(lastRequestUrl ?? "");
    expect(url.searchParams.get("ids")).toBe("solana");
    expect(url.searchParams.get("to")).toBe("gbp");

    unmount();
  });

  it("should refetch data automatically based on pollingInterval", async () => {
    const wrapper = createWrapper(store);
    const { result, unmount } = renderHook(
      () => useCurrencyData({ id: "bitcoin", counterCurrency: "usd" }),
      { wrapper },
    );

    await waitFor(() => expect(result.current.isSuccess).toBe(true));
    expect(requestCount).toBe(1);

    await waitFor(() => expect(requestCount).toBe(2), { timeout: 2000 });
    await waitFor(() => expect(result.current.isFetching).toBe(false));

    unmount();
  });

  it("should return undefined data when API returns empty array (id not resolving to a currency)", async () => {
    server.use(http.get("*/v3/markets", () => HttpResponse.json([])));
    const wrapper = createWrapper(store);
    const { result, unmount } = renderHook(
      () => useCurrencyData({ id: "unknown-currency", counterCurrency: "usd" }),
      { wrapper },
    );

    await waitFor(() => expect(result.current.isSuccess).toBe(true));
    expect(result.current.data).toBeUndefined();
    expect(result.current.isError).toBe(false);

    unmount();
  });
});
