import { describe, it, expect, vi, beforeEach } from "vitest";
import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import type { Task } from "@mosaic/shared";
import { TaskStatus, TaskPriority } from "@mosaic/shared";
import TasksPage from "./page";

// Mock the TaskList component
vi.mock("@/components/tasks/TaskList", () => ({
  TaskList: ({ tasks, isLoading }: { tasks: unknown[]; isLoading: boolean }): React.JSX.Element => (
    <div data-testid="task-list">{isLoading ? "Loading" : `${String(tasks.length)} tasks`}</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 fetchTasks
const mockFetchTasks = vi.fn<() => Promise<Task[]>>();
vi.mock("@/lib/api/tasks", () => ({
  fetchTasks: (...args: unknown[]): Promise<Task[]> => mockFetchTasks(...(args as [])),
}));

const fakeTasks: Task[] = [
  {
    id: "task-1",
    title: "Test task 1",
    description: "Description 1",
    status: TaskStatus.IN_PROGRESS,
    priority: TaskPriority.HIGH,
    dueDate: new Date("2026-02-01"),
    creatorId: "user-1",
    assigneeId: "user-1",
    workspaceId: "ws-1",
    projectId: null,
    parentId: null,
    sortOrder: 0,
    metadata: {},
    completedAt: null,
    createdAt: new Date("2026-01-28"),
    updatedAt: new Date("2026-01-28"),
  },
  {
    id: "task-2",
    title: "Test task 2",
    description: "Description 2",
    status: TaskStatus.NOT_STARTED,
    priority: TaskPriority.MEDIUM,
    dueDate: new Date("2026-02-02"),
    creatorId: "user-1",
    assigneeId: "user-1",
    workspaceId: "ws-1",
    projectId: null,
    parentId: null,
    sortOrder: 1,
    metadata: {},
    completedAt: null,
    createdAt: new Date("2026-01-28"),
    updatedAt: new Date("2026-01-28"),
  },
];

describe("TasksPage", (): void => {
  beforeEach((): void => {
    vi.clearAllMocks();
    mockUseWorkspaceId.mockReturnValue("ws-1");
    mockFetchTasks.mockResolvedValue(fakeTasks);
  });

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

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

  it("should render the TaskList with tasks after loading", async (): Promise<void> => {
    render(<TasksPage />);
    await waitFor((): void => {
      expect(screen.getByTestId("task-list")).toHaveTextContent("2 tasks");
    });
  });

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

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

  it("should retry fetching on retry button click", async (): Promise<void> => {
    mockFetchTasks.mockRejectedValueOnce(new Error("Network error"));
    render(<TasksPage />);
    await waitFor((): void => {
      expect(screen.getByText("Network error")).toBeInTheDocument();
    });

    mockFetchTasks.mockResolvedValueOnce(fakeTasks);
    const user = userEvent.setup();
    await user.click(screen.getByRole("button", { name: /try again/i }));

    await waitFor((): void => {
      expect(screen.getByTestId("task-list")).toHaveTextContent("2 tasks");
    });
  });

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

  it("should render the subtitle text", (): void => {
    render(<TasksPage />);
    expect(screen.getByText("Organize your work at your own pace")).toBeInTheDocument();
  });

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

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