import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
import { render, screen, waitFor } from "@testing-library/react";
import type { Event } from "@mosaic/shared";
import { CalendarWidget } from "../CalendarWidget";
import { fetchEvents } from "@/lib/api/events";

vi.mock("@/lib/api/events", () => ({
  fetchEvents: vi.fn(),
}));

const mockEvents: Event[] = [
  {
    id: "event-1",
    title: "API Planning",
    description: null,
    startTime: new Date("2026-02-01T09:00:00Z"),
    endTime: new Date("2026-02-01T09:30:00Z"),
    allDay: false,
    location: "Zoom",
    recurrence: null,
    creatorId: "user-1",
    workspaceId: "workspace-1",
    projectId: null,
    metadata: {},
    createdAt: new Date("2026-01-30T09:00:00Z"),
    updatedAt: new Date("2026-01-30T09:00:00Z"),
  },
  {
    id: "event-2",
    title: "API Review",
    description: null,
    startTime: new Date("2026-02-02T10:00:00Z"),
    endTime: new Date("2026-02-02T11:00:00Z"),
    allDay: false,
    location: "Room 1",
    recurrence: null,
    creatorId: "user-1",
    workspaceId: "workspace-1",
    projectId: null,
    metadata: {},
    createdAt: new Date("2026-01-30T09:00:00Z"),
    updatedAt: new Date("2026-01-30T09:00:00Z"),
  },
];

async function finishWidgetLoad(): Promise<void> {
  await waitFor(() => {
    expect(screen.queryByText("Loading events...")).not.toBeInTheDocument();
  });
}

describe("CalendarWidget", (): void => {
  beforeEach((): void => {
    vi.clearAllMocks();
    vi.mocked(fetchEvents).mockResolvedValue(mockEvents);
    vi.setSystemTime(new Date("2026-02-01T08:00:00Z"));
  });

  afterEach((): void => {
    vi.useRealTimers();
  });

  it("renders loading state initially", (): void => {
    render(<CalendarWidget id="calendar-1" />);

    expect(screen.getByText("Loading events...")).toBeInTheDocument();
  });

  it("fetches and renders upcoming events after loading", async (): Promise<void> => {
    render(<CalendarWidget id="calendar-1" />);

    await finishWidgetLoad();

    expect(fetchEvents).toHaveBeenCalledTimes(1);
    expect(screen.getByText("Upcoming Events")).toBeInTheDocument();
    expect(screen.getByText("API Planning")).toBeInTheDocument();
    expect(screen.getByText("API Review")).toBeInTheDocument();
  });

  it("shows relative day labels", async (): Promise<void> => {
    render(<CalendarWidget id="calendar-1" />);

    await finishWidgetLoad();

    expect(screen.getAllByText("Today").length).toBeGreaterThan(0);
    expect(screen.getByText("Tomorrow")).toBeInTheDocument();
  });

  it("shows event locations when present", async (): Promise<void> => {
    render(<CalendarWidget id="calendar-1" />);

    await finishWidgetLoad();

    expect(screen.getByText("Zoom")).toBeInTheDocument();
    expect(screen.getByText("Room 1")).toBeInTheDocument();
  });

  it("shows empty state when no events are returned", async (): Promise<void> => {
    vi.mocked(fetchEvents).mockResolvedValueOnce([]);

    render(<CalendarWidget id="calendar-1" />);
    await finishWidgetLoad();

    expect(screen.getByText("No upcoming events")).toBeInTheDocument();
  });
});
