import { describe, it, expect, beforeEach, vi } from "vitest";
import { EventsController } from "./events.controller";
import { EventsService } from "./events.service";

describe("EventsController", () => {
  let controller: EventsController;
  let service: EventsService;

  const mockEventsService = {
    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 mockEventId = "550e8400-e29b-41d4-a716-446655440003";

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

  const mockEvent = {
    id: mockEventId,
    workspaceId: mockWorkspaceId,
    title: "Test Event",
    description: "Test Description",
    startTime: new Date("2026-02-01T10:00:00Z"),
    endTime: new Date("2026-02-01T11:00:00Z"),
    allDay: false,
    location: "Conference Room A",
    recurrence: null,
    creatorId: mockUserId,
    projectId: null,
    metadata: {},
    createdAt: new Date(),
    updatedAt: new Date(),
  };

  beforeEach(() => {
    service = mockEventsService as any;
    controller = new EventsController(service);

    vi.clearAllMocks();
  });

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

  describe("create", () => {
    it("should create an event", async () => {
      const createDto = {
        title: "New Event",
        startTime: new Date("2026-02-01T10:00:00Z"),
      };

      mockEventsService.create.mockResolvedValue(mockEvent);

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

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

    it("should pass undefined workspaceId to service (validation handled by guards in production)", async () => {
      const createDto = { title: "Test", startTime: new Date() };
      mockEventsService.create.mockResolvedValue(mockEvent);

      await controller.create(createDto, undefined as any, mockUser);

      expect(mockEventsService.create).toHaveBeenCalledWith(undefined, mockUserId, createDto);
    });
  });

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

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

      mockEventsService.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 in production)", async () => {
      const paginatedResult = { data: [], meta: { total: 0, page: 1, limit: 50, totalPages: 0 } };
      mockEventsService.findAll.mockResolvedValue(paginatedResult);

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

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

  describe("findOne", () => {
    it("should return an event by id", async () => {
      mockEventsService.findOne.mockResolvedValue(mockEvent);

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

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

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

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

      expect(mockEventsService.findOne).toHaveBeenCalledWith(mockEventId, undefined);
    });
  });

  describe("update", () => {
    it("should update an event", async () => {
      const updateDto = {
        title: "Updated Event",
      };

      const updatedEvent = { ...mockEvent, ...updateDto };
      mockEventsService.update.mockResolvedValue(updatedEvent);

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

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

    it("should pass undefined workspaceId to service (validation handled by guards in production)", async () => {
      const updateDto = { title: "Test" };
      mockEventsService.update.mockResolvedValue(mockEvent);

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

      expect(mockEventsService.update).toHaveBeenCalledWith(
        mockEventId,
        undefined,
        mockUserId,
        updateDto
      );
    });
  });

  describe("remove", () => {
    it("should delete an event", async () => {
      mockEventsService.remove.mockResolvedValue(undefined);

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

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

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

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

      expect(mockEventsService.remove).toHaveBeenCalledWith(mockEventId, undefined, mockUserId);
    });
  });
});
