import { describe, it, expect, vi, beforeEach } from "vitest";
import { renderHook, act } from "@testing-library/react";
import { useSchedule } from "../useSchedule";
import * as blocklistApi from "../../api/blocklist";

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

beforeEach(() => {
  vi.clearAllMocks();
});

const mockScheduleInfo = {
  config: {
    frequency: "daily" as const,
    interval_hours: 24,
    hour: 3,
    minute: 0,
    day_of_week: 0,
  },
  last_run_at: "2024-01-01T10:00:00Z",
  next_run_at: "2024-01-02T03:00:00Z",
  last_run_errors: false,
};

describe("useSchedule", () => {
  it("loads schedule info on mount", async () => {
    vi.mocked(blocklistApi.fetchSchedule).mockResolvedValue(mockScheduleInfo);
    vi.mocked(blocklistApi.updateSchedule).mockResolvedValue(mockScheduleInfo);

    const { result } = renderHook(() => useSchedule());

    expect(result.current.loading).toBe(true);
    expect(result.current.info).toBeNull();

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

    expect(vi.mocked(blocklistApi.fetchSchedule)).toHaveBeenCalledTimes(1);
    expect(result.current.info).toEqual(mockScheduleInfo);
    expect(result.current.loading).toBe(false);
    expect(result.current.error).toBeNull();
  });

  it("sets error when fetch fails", async () => {
    vi.mocked(blocklistApi.fetchSchedule).mockRejectedValue(new Error("network error"));
    vi.mocked(blocklistApi.updateSchedule).mockResolvedValue(mockScheduleInfo);

    const { result } = renderHook(() => useSchedule());

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

    expect(result.current.error).toBe("network error");
    expect(result.current.loading).toBe(false);
    expect(result.current.info).toBeNull();
  });

  it("saveSchedule updates info", async () => {
    vi.mocked(blocklistApi.fetchSchedule).mockResolvedValue(mockScheduleInfo);
    const updatedSchedule = {
      ...mockScheduleInfo,
      config: { ...mockScheduleInfo.config, hour: 5 },
    };
    vi.mocked(blocklistApi.updateSchedule).mockResolvedValue(updatedSchedule);

    const { result } = renderHook(() => useSchedule());

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

    await act(async () => {
      await result.current.saveSchedule({
        frequency: "daily",
        interval_hours: 24,
        hour: 5,
        minute: 0,
        day_of_week: 0,
      });
    });

    expect(result.current.info).toEqual(updatedSchedule);
  });

  it("refresh triggers a second fetch", async () => {
    vi.mocked(blocklistApi.fetchSchedule)
      .mockResolvedValueOnce(mockScheduleInfo)
      .mockResolvedValueOnce({
        ...mockScheduleInfo,
        config: { ...mockScheduleInfo.config, hour: 5 },
      });
    vi.mocked(blocklistApi.updateSchedule).mockResolvedValue(mockScheduleInfo);

    const { result } = renderHook(() => useSchedule());

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

    expect(result.current.info?.config.hour).toBe(3);

    await act(async () => {
      result.current.refresh();
      await Promise.resolve();
    });

    expect(vi.mocked(blocklistApi.fetchSchedule)).toHaveBeenCalledTimes(2);
    expect(result.current.info?.config.hour).toBe(5);
  });

  it("refresh exposes function to reuse in callbacks", async () => {
    vi.mocked(blocklistApi.fetchSchedule).mockResolvedValue(mockScheduleInfo);
    vi.mocked(blocklistApi.updateSchedule).mockResolvedValue(mockScheduleInfo);

    const { result } = renderHook(() => useSchedule());

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

    expect(typeof result.current.refresh).toBe("function");
    expect(() => result.current.refresh()).not.toThrow();
  });
});
