import { describe, it, expect, vi, beforeEach } from "vitest";
import { render, screen, waitFor } from "@testing-library/react";
import type { Event } from "@mosaic/shared";
import CalendarPage from "./page";

// Mock the Calendar component
vi.mock("@/components/calendar/Calendar", () => ({
  Calendar: ({
    events,
    isLoading,
  }: {
    events: unknown[];
    isLoading: boolean;
  }): React.JSX.Element => (
    <div data-testid="calendar">{isLoading ? "Loading" : `${String(events.length)} events`}</div>
  ),
}));

// Mock MosaicSpinner
vi.mock("@/components/ui/MosaicSpinner", () => ({
  MosaicSpinner: ({ label }: { label?: string }): React.JSX.Element => (
    <div data-testid="mosaic-spinner">{label ?? "Loading..."}</div>
  ),
}));

// Mock useWorkspaceId
const mockUseWorkspaceId = vi.fn<() => string | null>();
vi.mock("@/lib/hooks", () => ({
  useWorkspaceId: (): string | null => mockUseWorkspaceId(),
}));

// Mock fetchEvents
const mockFetchEvents = vi.fn<() => Promise<Event[]>>();
vi.mock("@/lib/api/events", () => ({
  fetchEvents: (...args: unknown[]): Promise<Event[]> => mockFetchEvents(...(args as [])),
}));

const fakeEvents: Event[] = [
  {
    id: "event-1",
    title: "Team standup",
    description: "Daily standup meeting",
    startTime: new Date("2026-02-20T09:00:00Z"),
    endTime: new Date("2026-02-20T09:30:00Z"),
    allDay: false,
    location: null,
    recurrence: null,
    creatorId: "user-1",
    projectId: null,
    workspaceId: "ws-1",
    metadata: {},
    createdAt: new Date("2026-01-28"),
    updatedAt: new Date("2026-01-28"),
  },
  {
    id: "event-2",
    title: "Sprint planning",
    description: "Bi-weekly sprint planning",
    startTime: new Date("2026-02-21T14:00:00Z"),
    endTime: new Date("2026-02-21T15:00:00Z"),
    allDay: false,
    location: null,
    recurrence: null,
    creatorId: "user-1",
    projectId: null,
    workspaceId: "ws-1",
    metadata: {},
    createdAt: new Date("2026-01-28"),
    updatedAt: new Date("2026-01-28"),
  },
  {
    id: "event-3",
    title: "All-day workshop",
    description: null,
    startTime: new Date("2026-02-22T00:00:00Z"),
    endTime: null,
    allDay: true,
    location: "Conference Room A",
    recurrence: null,
    creatorId: "user-1",
    projectId: null,
    workspaceId: "ws-1",
    metadata: {},
    createdAt: new Date("2026-01-28"),
    updatedAt: new Date("2026-01-28"),
  },
];

describe("CalendarPage", (): void => {
  beforeEach((): void => {
    vi.clearAllMocks();
    mockUseWorkspaceId.mockReturnValue("ws-1");
    mockFetchEvents.mockResolvedValue(fakeEvents);
  });

  it("should render the page title", (): void => {
    render(<CalendarPage />);
    expect(screen.getByRole("heading", { level: 1 })).toHaveTextContent("Calendar");
  });

  it("should show loading state initially", (): void => {
    // Never resolve so we stay in loading state
    // eslint-disable-next-line @typescript-eslint/no-empty-function
    mockFetchEvents.mockReturnValue(new Promise<Event[]>(() => {}));
    render(<CalendarPage />);
    expect(screen.getByTestId("mosaic-spinner")).toBeInTheDocument();
  });

  it("should render the Calendar with events after loading", async (): Promise<void> => {
    render(<CalendarPage />);
    await waitFor((): void => {
      expect(screen.getByTestId("calendar")).toHaveTextContent("3 events");
    });
  });

  it("should have proper layout structure", (): void => {
    const { container } = render(<CalendarPage />);
    const main = container.querySelector("main");
    expect(main).toBeInTheDocument();
  });

  it("should render the subtitle text", (): void => {
    render(<CalendarPage />);
    expect(screen.getByText("View your schedule at a glance")).toBeInTheDocument();
  });

  it("should show empty state when no events exist", async (): Promise<void> => {
    mockFetchEvents.mockResolvedValue([]);
    render(<CalendarPage />);
    await waitFor((): void => {
      expect(screen.getByText("No events scheduled")).toBeInTheDocument();
    });
  });

  it("should show error state on API failure", async (): Promise<void> => {
    mockFetchEvents.mockRejectedValue(new Error("Network error"));
    render(<CalendarPage />);
    await waitFor((): void => {
      expect(screen.getByText("Network error")).toBeInTheDocument();
    });
    expect(screen.getByRole("button", { name: /try again/i })).toBeInTheDocument();
  });

  it("should not fetch when workspace ID is not available", async (): Promise<void> => {
    mockUseWorkspaceId.mockReturnValue(null);
    render(<CalendarPage />);

    // Wait a tick to ensure useEffect ran
    await waitFor((): void => {
      expect(mockFetchEvents).not.toHaveBeenCalled();
    });
  });
});
