import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { renderHook, act } from "@testing-library/react";
import { usePolledData } from "../usePolledData";

// Mock usePageVisibility to always return true (page visible)
vi.mock("../usePageVisibility", () => ({
  usePageVisibility: () => true,
}));

describe("usePolledData", () => {
  beforeEach(() => {
    vi.useFakeTimers();
  });

  afterEach(() => {
    vi.runOnlyPendingTimers();
    vi.useRealTimers();
  });

  it("stops polling when pollInterval is undefined", async () => {
    const fetcher = vi.fn().mockResolvedValue({ value: "test" });
    const selector = vi.fn((response: { value: string }) => response.value);

    renderHook(() =>
      usePolledData({
        fetcher,
        selector,
        errorMessage: "Failed to load",
      })
    );

    // Initial fetch should happen in useFetchData
    await act(async () => {
      vi.runAllTimersAsync();
    });

    const callCountAfterInitial = fetcher.mock.calls.length;

    // Reset timer and advance to ensure no more polls
    vi.clearAllTimers();
    fetcher.mockClear();

    await act(async () => {
      vi.advanceTimersByTime(10000);
    });

    // Should not poll since pollInterval is undefined
    expect(fetcher).not.toHaveBeenCalled();
  });

  it("implements drift correction: next poll is scheduled based on elapsed time", async () => {
    const fetcher = vi.fn().mockResolvedValue({ value: "test" });
    const selector = vi.fn((response: { value: string }) => response.value);

    renderHook(() =>
      usePolledData({
        fetcher,
        selector,
        errorMessage: "Failed to load",
        pollInterval: 5000,
      })
    );

    // Wait for initial setup
    await act(async () => {
      vi.advanceTimersByTime(100);
    });

    const initialCalls = fetcher.mock.calls.length;

    // Clear for clean test
    fetcher.mockClear();

    // First poll completes after ~0ms, next poll should be in ~5000ms
    // Advance 4000ms
    await act(async () => {
      vi.advanceTimersByTime(4000);
    });

    expect(fetcher).not.toHaveBeenCalled();

    // Advance to 5500ms total, poll should have happened
    await act(async () => {
      vi.advanceTimersByTime(1500);
    });

    expect(fetcher).toHaveBeenCalled();
  });

  it("cleans up timers on unmount", async () => {
    const fetcher = vi.fn().mockResolvedValue({ value: "test" });
    const selector = vi.fn((response: { value: string }) => response.value);

    const { unmount } = renderHook(() =>
      usePolledData({
        fetcher,
        selector,
        errorMessage: "Failed to load",
        pollInterval: 5000,
      })
    );

    await act(async () => {
      vi.advanceTimersByTime(1000);
    });

    const callCount = fetcher.mock.calls.length;

    // Unmount
    unmount();

    // Advance time and verify no new fetches
    await act(async () => {
      vi.advanceTimersByTime(10000);
    });

    // Should not increase beyond initial calls
    expect(fetcher.mock.calls.length).toBe(callCount);
  });

  it("calls refresh callback to trigger immediate fetch", async () => {
    const fetcher = vi.fn().mockResolvedValue({ value: "test" });
    const selector = vi.fn((response: { value: string }) => response.value);

    const { result } = renderHook(() =>
      usePolledData({
        fetcher,
        selector,
        errorMessage: "Failed to load",
        pollInterval: 5000,
      })
    );

    await act(async () => {
      vi.advanceTimersByTime(100);
    });

    const initialCalls = fetcher.mock.calls.length;
    fetcher.mockClear();

    // Call refresh
    await act(async () => {
      result.current.refresh();
      vi.advanceTimersByTime(100);
    });

    expect(fetcher).toHaveBeenCalled();
  });

  it("returns initial data if provided", () => {
    const fetcher = vi.fn().mockResolvedValue({ value: "updated" });
    const selector = vi.fn();

    const { result } = renderHook(() =>
      usePolledData({
        fetcher,
        selector,
        errorMessage: "Failed to load",
        initialData: "initial",
        pollInterval: 5000,
      })
    );

    expect(result.current.data).toBe("initial");
    expect(result.current.loading).toBe(true);
  });

  it("returns null when data is undefined and no initialData", () => {
    const fetcher = vi.fn().mockResolvedValue({ value: "test" });
    const selector = vi.fn();

    const { result } = renderHook(() =>
      usePolledData({
        fetcher,
        selector,
        errorMessage: "Failed to load",
        pollInterval: 5000,
      })
    );

    expect(result.current.data).toBeNull();
  });
});
