import { describe, it, expect, beforeEach, vi } from "vitest";
import { ActivityController } from "./activity.controller";
import { ActivityService } from "./activity.service";
import { ActivityAction, EntityType } from "@prisma/client";
import type { QueryActivityLogDto } from "./dto";

describe("ActivityController", () => {
  let controller: ActivityController;
  let service: ActivityService;

  const mockActivityService = {
    findAll: vi.fn(),
    findOne: vi.fn(),
    getAuditTrail: vi.fn(),
  };

  const mockWorkspaceId = "workspace-123";

  beforeEach(() => {
    service = mockActivityService as any;
    controller = new ActivityController(service);

    vi.clearAllMocks();
  });

  describe("findAll", () => {
    const mockPaginatedResult = {
      data: [
        {
          id: "activity-1",
          workspaceId: "workspace-123",
          userId: "user-123",
          action: ActivityAction.CREATED,
          entityType: EntityType.TASK,
          entityId: "task-123",
          details: {},
          createdAt: new Date("2024-01-01"),
          user: {
            id: "user-123",
            name: "Test User",
            email: "test@example.com",
          },
        },
      ],
      meta: {
        total: 1,
        page: 1,
        limit: 50,
        totalPages: 1,
      },
    };

    it("should return paginated activity logs using authenticated user's workspaceId", async () => {
      const query: QueryActivityLogDto = {
        workspaceId: "workspace-123",
        page: 1,
        limit: 50,
      };

      mockActivityService.findAll.mockResolvedValue(mockPaginatedResult);

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

      expect(result).toEqual(mockPaginatedResult);
      expect(mockActivityService.findAll).toHaveBeenCalledWith({
        ...query,
        workspaceId: "workspace-123",
      });
    });

    it("should handle query with filters", async () => {
      const query: QueryActivityLogDto = {
        workspaceId: "workspace-123",
        userId: "user-123",
        action: ActivityAction.CREATED,
        entityType: EntityType.TASK,
        page: 1,
        limit: 10,
      };

      mockActivityService.findAll.mockResolvedValue(mockPaginatedResult);

      await controller.findAll(query, mockWorkspaceId);

      expect(mockActivityService.findAll).toHaveBeenCalledWith({
        ...query,
        workspaceId: "workspace-123",
      });
    });

    it("should handle query with date range", async () => {
      const startDate = new Date("2024-01-01");
      const endDate = new Date("2024-01-31");

      const query: QueryActivityLogDto = {
        workspaceId: "workspace-123",
        startDate,
        endDate,
        page: 1,
        limit: 50,
      };

      mockActivityService.findAll.mockResolvedValue(mockPaginatedResult);

      await controller.findAll(query, mockWorkspaceId);

      expect(mockActivityService.findAll).toHaveBeenCalledWith({
        ...query,
        workspaceId: "workspace-123",
      });
    });

    it("should use user's workspaceId even if query provides different one", async () => {
      const query: QueryActivityLogDto = {
        workspaceId: "different-workspace",
        page: 1,
        limit: 50,
      };

      mockActivityService.findAll.mockResolvedValue(mockPaginatedResult);

      await controller.findAll(query, mockWorkspaceId);

      // Should use authenticated user's workspaceId, not query's
      expect(mockActivityService.findAll).toHaveBeenCalledWith({
        ...query,
        workspaceId: "workspace-123",
      });
    });
  });

  describe("findOne", () => {
    const mockActivity = {
      id: "activity-123",
      workspaceId: "workspace-123",
      userId: "user-123",
      action: ActivityAction.CREATED,
      entityType: EntityType.TASK,
      entityId: "task-123",
      details: {},
      createdAt: new Date(),
      user: {
        id: "user-123",
        name: "Test User",
        email: "test@example.com",
      },
    };

    it("should return a single activity log using authenticated user's workspaceId", async () => {
      mockActivityService.findOne.mockResolvedValue(mockActivity);

      const result = await controller.findOne("activity-123", mockWorkspaceId);

      expect(result).toEqual(mockActivity);
      expect(mockActivityService.findOne).toHaveBeenCalledWith("activity-123", "workspace-123");
    });

    it("should return null if activity not found", async () => {
      mockActivityService.findOne.mockResolvedValue(null);

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

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

    it("should return null if workspaceId is missing (service handles gracefully)", async () => {
      mockActivityService.findOne.mockResolvedValue(null);

      const result = await controller.findOne("activity-123", undefined as any);

      expect(result).toBeNull();
      expect(mockActivityService.findOne).toHaveBeenCalledWith("activity-123", undefined);
    });
  });

  describe("getAuditTrail", () => {
    const mockAuditTrail = [
      {
        id: "activity-1",
        workspaceId: "workspace-123",
        userId: "user-123",
        action: ActivityAction.CREATED,
        entityType: EntityType.TASK,
        entityId: "task-123",
        details: { title: "New Task" },
        createdAt: new Date("2024-01-01"),
        user: {
          id: "user-123",
          name: "Test User",
          email: "test@example.com",
        },
      },
      {
        id: "activity-2",
        workspaceId: "workspace-123",
        userId: "user-456",
        action: ActivityAction.UPDATED,
        entityType: EntityType.TASK,
        entityId: "task-123",
        details: { title: "Updated Task" },
        createdAt: new Date("2024-01-02"),
        user: {
          id: "user-456",
          name: "Another User",
          email: "another@example.com",
        },
      },
    ];

    it("should return audit trail for a task using authenticated user's workspaceId", async () => {
      mockActivityService.getAuditTrail.mockResolvedValue(mockAuditTrail);

      const result = await controller.getAuditTrail(EntityType.TASK, "task-123", mockWorkspaceId);

      expect(result).toEqual(mockAuditTrail);
      expect(mockActivityService.getAuditTrail).toHaveBeenCalledWith(
        "workspace-123",
        EntityType.TASK,
        "task-123"
      );
    });

    it("should return audit trail for an event", async () => {
      const eventAuditTrail = [
        {
          id: "activity-3",
          workspaceId: "workspace-123",
          userId: "user-123",
          action: ActivityAction.CREATED,
          entityType: EntityType.EVENT,
          entityId: "event-123",
          details: {},
          createdAt: new Date(),
          user: {
            id: "user-123",
            name: "Test User",
            email: "test@example.com",
          },
        },
      ];

      mockActivityService.getAuditTrail.mockResolvedValue(eventAuditTrail);

      const result = await controller.getAuditTrail(EntityType.EVENT, "event-123", mockWorkspaceId);

      expect(result).toEqual(eventAuditTrail);
      expect(mockActivityService.getAuditTrail).toHaveBeenCalledWith(
        "workspace-123",
        EntityType.EVENT,
        "event-123"
      );
    });

    it("should return audit trail for a project", async () => {
      const projectAuditTrail = [
        {
          id: "activity-4",
          workspaceId: "workspace-123",
          userId: "user-123",
          action: ActivityAction.CREATED,
          entityType: EntityType.PROJECT,
          entityId: "project-123",
          details: {},
          createdAt: new Date(),
          user: {
            id: "user-123",
            name: "Test User",
            email: "test@example.com",
          },
        },
      ];

      mockActivityService.getAuditTrail.mockResolvedValue(projectAuditTrail);

      const result = await controller.getAuditTrail(
        EntityType.PROJECT,
        "project-123",
        mockWorkspaceId
      );

      expect(result).toEqual(projectAuditTrail);
      expect(mockActivityService.getAuditTrail).toHaveBeenCalledWith(
        "workspace-123",
        EntityType.PROJECT,
        "project-123"
      );
    });

    it("should return empty array if no audit trail found", async () => {
      mockActivityService.getAuditTrail.mockResolvedValue([]);

      const result = await controller.getAuditTrail(
        EntityType.WORKSPACE,
        "workspace-999",
        mockWorkspaceId
      );

      expect(result).toEqual([]);
    });

    it("should return empty array if workspaceId is missing (service handles gracefully)", async () => {
      mockActivityService.getAuditTrail.mockResolvedValue([]);

      const result = await controller.getAuditTrail(EntityType.TASK, "task-123", undefined as any);

      expect(result).toEqual([]);
      expect(mockActivityService.getAuditTrail).toHaveBeenCalledWith(
        undefined,
        EntityType.TASK,
        "task-123"
      );
    });
  });
});
