import { describe, it, expect, beforeEach, vi } from "vitest";
import { ProjectsController } from "./projects.controller";
import { ProjectsService } from "./projects.service";
import { ProjectStatus } from "@prisma/client";

describe("ProjectsController", () => {
  let controller: ProjectsController;
  let service: ProjectsService;

  const mockProjectsService = {
    create: vi.fn(),
    findAll: vi.fn(),
    findOne: vi.fn(),
    update: vi.fn(),
    remove: vi.fn(),
  };

  const mockWorkspaceId = "550e8400-e29b-41d4-a716-446655440001";
  const mockUserId = "550e8400-e29b-41d4-a716-446655440002";
  const mockProjectId = "550e8400-e29b-41d4-a716-446655440003";

  const mockUser = {
    id: mockUserId,
    workspaceId: mockWorkspaceId,
  };

  const mockProject = {
    id: mockProjectId,
    workspaceId: mockWorkspaceId,
    name: "Test Project",
    description: "Test Description",
    status: ProjectStatus.PLANNING,
    startDate: new Date("2026-02-01"),
    endDate: new Date("2026-03-01"),
    creatorId: mockUserId,
    color: "#FF5733",
    metadata: {},
    createdAt: new Date(),
    updatedAt: new Date(),
  };

  beforeEach(() => {
    service = mockProjectsService as any;
    controller = new ProjectsController(service);

    vi.clearAllMocks();
  });

  it("should be defined", () => {
    expect(controller).toBeDefined();
  });

  describe("create", () => {
    it("should create a project", async () => {
      const createDto = {
        name: "New Project",
        description: "Project description",
      };

      mockProjectsService.create.mockResolvedValue(mockProject);

      const result = await controller.create(createDto, mockWorkspaceId, mockUser);

      expect(result).toEqual(mockProject);
      expect(service.create).toHaveBeenCalledWith(mockWorkspaceId, mockUserId, createDto);
    });

    it("should pass undefined workspaceId to service (validation handled by guards)", async () => {
      mockProjectsService.create.mockResolvedValue(mockProject);

      await controller.create({ name: "Test" }, undefined as any, mockUser);

      expect(mockProjectsService.create).toHaveBeenCalledWith(undefined, mockUserId, {
        name: "Test",
      });
    });
  });

  describe("findAll", () => {
    it("should return paginated projects", async () => {
      const query = {
        workspaceId: mockWorkspaceId,
      };

      const paginatedResult = {
        data: [mockProject],
        meta: {
          total: 1,
          page: 1,
          limit: 50,
          totalPages: 1,
        },
      };

      mockProjectsService.findAll.mockResolvedValue(paginatedResult);

      const result = await controller.findAll(query, mockWorkspaceId);

      expect(result).toEqual(paginatedResult);
    });

    it("should pass undefined workspaceId to service (validation handled by guards)", async () => {
      const paginatedResult = { data: [], meta: { total: 0, page: 1, limit: 50, totalPages: 0 } };
      mockProjectsService.findAll.mockResolvedValue(paginatedResult);

      await controller.findAll({}, undefined as any);

      expect(mockProjectsService.findAll).toHaveBeenCalledWith({ workspaceId: undefined });
    });
  });

  describe("findOne", () => {
    it("should return a project by id", async () => {
      mockProjectsService.findOne.mockResolvedValue(mockProject);

      const result = await controller.findOne(mockProjectId, mockWorkspaceId);

      expect(result).toEqual(mockProject);
    });

    it("should pass undefined workspaceId to service (validation handled by guards)", async () => {
      mockProjectsService.findOne.mockResolvedValue(null);

      await controller.findOne(mockProjectId, undefined as any);

      expect(mockProjectsService.findOne).toHaveBeenCalledWith(mockProjectId, undefined);
    });
  });

  describe("update", () => {
    it("should update a project", async () => {
      const updateDto = {
        name: "Updated Project",
      };

      const updatedProject = { ...mockProject, ...updateDto };
      mockProjectsService.update.mockResolvedValue(updatedProject);

      const result = await controller.update(mockProjectId, updateDto, mockWorkspaceId, mockUser);

      expect(result).toEqual(updatedProject);
    });

    it("should pass undefined workspaceId to service (validation handled by guards)", async () => {
      const updateDto = { name: "Test" };
      mockProjectsService.update.mockResolvedValue(mockProject);

      await controller.update(mockProjectId, updateDto, undefined as any, mockUser);

      expect(mockProjectsService.update).toHaveBeenCalledWith(
        mockProjectId,
        undefined,
        mockUserId,
        updateDto
      );
    });
  });

  describe("remove", () => {
    it("should delete a project", async () => {
      mockProjectsService.remove.mockResolvedValue(undefined);

      await controller.remove(mockProjectId, mockWorkspaceId, mockUser);

      expect(service.remove).toHaveBeenCalledWith(mockProjectId, mockWorkspaceId, mockUserId);
    });

    it("should pass undefined workspaceId to service (validation handled by guards)", async () => {
      mockProjectsService.remove.mockResolvedValue(undefined);

      await controller.remove(mockProjectId, undefined as any, mockUser);

      expect(mockProjectsService.remove).toHaveBeenCalledWith(mockProjectId, undefined, mockUserId);
    });
  });
});
