/* eslint-disable @typescript-eslint/no-unsafe-argument */
import { describe, it, expect, vi, beforeEach } from "vitest";
import { fetchTasks } from "./tasks";
import { TaskStatus, TaskPriority, type Task } from "@mosaic/shared";

// Mock the API client
vi.mock("./client", () => ({
  apiGet: vi.fn(),
}));

const { apiGet } = await import("./client");

describe("Task API Client", (): void => {
  beforeEach((): void => {
    vi.clearAllMocks();
  });

  it("should fetch tasks successfully", async (): Promise<void> => {
    const mockTasks: Task[] = [
      {
        id: "task-1",
        title: "Complete project setup",
        description: "Set up the development environment",
        status: TaskStatus.IN_PROGRESS,
        priority: TaskPriority.HIGH,
        dueDate: new Date("2026-02-01"),
        creatorId: "user-1",
        assigneeId: "user-1",
        workspaceId: "workspace-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: "Review documentation",
        description: "Review and update project docs",
        status: TaskStatus.NOT_STARTED,
        priority: TaskPriority.MEDIUM,
        dueDate: new Date("2026-02-05"),
        creatorId: "user-1",
        assigneeId: "user-1",
        workspaceId: "workspace-1",
        projectId: null,
        parentId: null,
        sortOrder: 1,
        metadata: {},
        completedAt: null,
        createdAt: new Date("2026-01-28"),
        updatedAt: new Date("2026-01-28"),
      },
    ];

    vi.mocked(apiGet).mockResolvedValueOnce({ data: mockTasks });

    const result = await fetchTasks();

    expect(apiGet).toHaveBeenCalledWith("/api/tasks", undefined);
    expect(result).toEqual(mockTasks);
  });

  it("should handle errors when fetching tasks", async (): Promise<void> => {
    vi.mocked(apiGet).mockRejectedValueOnce(new Error("Network error"));

    await expect(fetchTasks()).rejects.toThrow("Network error");
  });

  it("should fetch tasks with filters", async (): Promise<void> => {
    const mockTasks: Task[] = [];
    vi.mocked(apiGet).mockResolvedValueOnce({ data: mockTasks });

    await fetchTasks({ status: TaskStatus.IN_PROGRESS });

    expect(apiGet).toHaveBeenCalledWith("/api/tasks?status=IN_PROGRESS", undefined);
  });

  it("should fetch tasks with multiple filters", async (): Promise<void> => {
    const mockTasks: Task[] = [];
    vi.mocked(apiGet).mockResolvedValueOnce({ data: mockTasks });

    await fetchTasks({ status: TaskStatus.IN_PROGRESS, priority: TaskPriority.HIGH });

    expect(apiGet).toHaveBeenCalledWith("/api/tasks?status=IN_PROGRESS&priority=HIGH", undefined);
  });

  it("should fetch tasks with workspace ID", async (): Promise<void> => {
    const mockTasks: Task[] = [];
    vi.mocked(apiGet).mockResolvedValueOnce({ data: mockTasks });

    await fetchTasks({ workspaceId: "workspace-123" });

    // WorkspaceId should be sent via header (second param), not query string
    expect(apiGet).toHaveBeenCalledWith("/api/tasks", "workspace-123");
  });

  it("should fetch tasks with filters and workspace ID", async (): Promise<void> => {
    const mockTasks: Task[] = [];
    vi.mocked(apiGet).mockResolvedValueOnce({ data: mockTasks });

    await fetchTasks({
      status: TaskStatus.IN_PROGRESS,
      workspaceId: "workspace-456",
    });

    // Status in query, workspace in header
    expect(apiGet).toHaveBeenCalledWith("/api/tasks?status=IN_PROGRESS", "workspace-456");
  });

  describe("error handling", (): void => {
    it("should handle network errors when fetching tasks", async (): Promise<void> => {
      vi.mocked(apiGet).mockRejectedValueOnce(new Error("Network request failed"));

      await expect(fetchTasks()).rejects.toThrow("Network request failed");
    });

    it("should handle API returning malformed data", async (): Promise<void> => {
      vi.mocked(apiGet).mockResolvedValueOnce({
        data: null,
      });

      const result = await fetchTasks();

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

    it("should handle auth token expiration (401 error)", async (): Promise<void> => {
      vi.mocked(apiGet).mockRejectedValueOnce(new Error("Authentication required"));

      await expect(fetchTasks()).rejects.toThrow("Authentication required");
    });

    it("should handle server 500 errors", async (): Promise<void> => {
      vi.mocked(apiGet).mockRejectedValueOnce(new Error("Internal server error"));

      await expect(fetchTasks()).rejects.toThrow("Internal server error");
    });

    it("should handle forbidden access (403 error)", async (): Promise<void> => {
      vi.mocked(apiGet).mockRejectedValueOnce(new Error("Access denied"));

      await expect(fetchTasks()).rejects.toThrow("Access denied");
    });

    it("should handle rate limiting errors", async (): Promise<void> => {
      vi.mocked(apiGet).mockRejectedValueOnce(
        new Error("Too many requests. Please try again later.")
      );

      await expect(fetchTasks()).rejects.toThrow("Too many requests. Please try again later.");
    });

    it("should ignore malformed filter parameters", async (): Promise<void> => {
      const mockTasks: Task[] = [];
      vi.mocked(apiGet).mockResolvedValueOnce({ data: mockTasks });

      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      await fetchTasks({ invalidFilter: "value" } as any);

      // Function should ignore invalid filters and call without query params
      expect(apiGet).toHaveBeenCalledWith("/api/tasks", undefined);
    });

    it("should handle empty response data", async (): Promise<void> => {
      vi.mocked(apiGet).mockResolvedValueOnce({});

      const result = await fetchTasks();

      expect(result).toBeUndefined();
    });

    it("should handle timeout errors", async (): Promise<void> => {
      vi.mocked(apiGet).mockRejectedValueOnce(new Error("Request timeout"));

      await expect(fetchTasks()).rejects.toThrow("Request timeout");
    });
  });
});
