import { Test, TestingModule } from "@nestjs/testing";
import { AgentTasksController } from "./agent-tasks.controller";
import { AgentTasksService } from "./agent-tasks.service";
import { AgentTaskStatus, AgentTaskPriority } from "@prisma/client";
import { AuthGuard } from "../auth/guards/auth.guard";
import { WorkspaceGuard, PermissionGuard } from "../common/guards";
import { ExecutionContext } from "@nestjs/common";
import { describe, it, expect, beforeEach, vi } from "vitest";

describe("AgentTasksController", () => {
  let controller: AgentTasksController;
  let service: AgentTasksService;

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

  const mockAuthGuard = {
    canActivate: vi.fn(() => true),
  };

  const mockWorkspaceGuard = {
    canActivate: vi.fn(() => true),
  };

  const mockPermissionGuard = {
    canActivate: vi.fn(() => true),
  };

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      controllers: [AgentTasksController],
      providers: [
        {
          provide: AgentTasksService,
          useValue: mockAgentTasksService,
        },
      ],
    })
      .overrideGuard(AuthGuard)
      .useValue(mockAuthGuard)
      .overrideGuard(WorkspaceGuard)
      .useValue(mockWorkspaceGuard)
      .overrideGuard(PermissionGuard)
      .useValue(mockPermissionGuard)
      .compile();

    controller = module.get<AgentTasksController>(AgentTasksController);
    service = module.get<AgentTasksService>(AgentTasksService);

    // Reset mocks
    vi.clearAllMocks();
  });

  describe("create", () => {
    it("should create a new agent task", async () => {
      const workspaceId = "workspace-1";
      const user = { id: "user-1", email: "test@example.com" };
      const createDto = {
        title: "Test Task",
        description: "Test Description",
        agentType: "test-agent",
      };

      const mockTask = {
        id: "task-1",
        ...createDto,
        workspaceId,
        status: AgentTaskStatus.PENDING,
        priority: AgentTaskPriority.MEDIUM,
        agentConfig: {},
        result: null,
        error: null,
        createdById: user.id,
        createdAt: new Date(),
        updatedAt: new Date(),
        startedAt: null,
        completedAt: null,
      };

      mockAgentTasksService.create.mockResolvedValue(mockTask);

      const result = await controller.create(createDto, workspaceId, user);

      expect(mockAgentTasksService.create).toHaveBeenCalledWith(workspaceId, user.id, createDto);
      expect(result).toEqual(mockTask);
    });
  });

  describe("findAll", () => {
    it("should return paginated agent tasks", async () => {
      const workspaceId = "workspace-1";
      const query = {
        page: 1,
        limit: 10,
      };

      const mockResponse = {
        data: [
          { id: "task-1", title: "Task 1" },
          { id: "task-2", title: "Task 2" },
        ],
        meta: {
          total: 2,
          page: 1,
          limit: 10,
          totalPages: 1,
        },
      };

      mockAgentTasksService.findAll.mockResolvedValue(mockResponse);

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

      expect(mockAgentTasksService.findAll).toHaveBeenCalledWith({
        ...query,
        workspaceId,
      });
      expect(result).toEqual(mockResponse);
    });

    it("should apply filters when provided", async () => {
      const workspaceId = "workspace-1";
      const query = {
        status: AgentTaskStatus.PENDING,
        priority: AgentTaskPriority.HIGH,
        agentType: "test-agent",
      };

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

      mockAgentTasksService.findAll.mockResolvedValue(mockResponse);

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

      expect(mockAgentTasksService.findAll).toHaveBeenCalledWith({
        ...query,
        workspaceId,
      });
      expect(result).toEqual(mockResponse);
    });
  });

  describe("findOne", () => {
    it("should return a single agent task", async () => {
      const id = "task-1";
      const workspaceId = "workspace-1";

      const mockTask = {
        id,
        title: "Task 1",
        workspaceId,
        status: AgentTaskStatus.PENDING,
        priority: AgentTaskPriority.MEDIUM,
        agentType: "test-agent",
        agentConfig: {},
        result: null,
        error: null,
        createdById: "user-1",
        createdAt: new Date(),
        updatedAt: new Date(),
        startedAt: null,
        completedAt: null,
      };

      mockAgentTasksService.findOne.mockResolvedValue(mockTask);

      const result = await controller.findOne(id, workspaceId);

      expect(mockAgentTasksService.findOne).toHaveBeenCalledWith(id, workspaceId);
      expect(result).toEqual(mockTask);
    });
  });

  describe("update", () => {
    it("should update an agent task", async () => {
      const id = "task-1";
      const workspaceId = "workspace-1";
      const updateDto = {
        title: "Updated Task",
        status: AgentTaskStatus.RUNNING,
      };

      const mockTask = {
        id,
        ...updateDto,
        workspaceId,
        priority: AgentTaskPriority.MEDIUM,
        agentType: "test-agent",
        agentConfig: {},
        result: null,
        error: null,
        createdById: "user-1",
        createdAt: new Date(),
        updatedAt: new Date(),
        startedAt: new Date(),
        completedAt: null,
      };

      mockAgentTasksService.update.mockResolvedValue(mockTask);

      const result = await controller.update(id, updateDto, workspaceId);

      expect(mockAgentTasksService.update).toHaveBeenCalledWith(id, workspaceId, updateDto);
      expect(result).toEqual(mockTask);
    });
  });

  describe("remove", () => {
    it("should delete an agent task", async () => {
      const id = "task-1";
      const workspaceId = "workspace-1";

      const mockResponse = { message: "Agent task deleted successfully" };

      mockAgentTasksService.remove.mockResolvedValue(mockResponse);

      const result = await controller.remove(id, workspaceId);

      expect(mockAgentTasksService.remove).toHaveBeenCalledWith(id, workspaceId);
      expect(result).toEqual(mockResponse);
    });
  });
});
