import { Test, TestingModule } from "@nestjs/testing";
import { describe, it, expect, beforeEach, vi } from "vitest";
import { FindingsController } from "./findings.controller";
import { FindingsService } from "./findings.service";
import { AuthGuard } from "../auth/guards/auth.guard";
import { WorkspaceGuard, PermissionGuard } from "../common/guards";
import { CreateFindingDto, QueryFindingsDto, SearchFindingsDto } from "./dto";

describe("FindingsController", () => {
  let controller: FindingsController;
  let service: FindingsService;

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

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

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

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

  const workspaceId = "550e8400-e29b-41d4-a716-446655440001";
  const findingId = "550e8400-e29b-41d4-a716-446655440002";

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

    controller = module.get<FindingsController>(FindingsController);
    service = module.get<FindingsService>(FindingsService);

    vi.clearAllMocks();
  });

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

  describe("create", () => {
    it("should create a finding", async () => {
      const createDto: CreateFindingDto = {
        agentId: "research-agent",
        type: "security",
        title: "SQL injection risk",
        data: { severity: "high" },
        summary: "Potential SQL injection in search endpoint.",
      };

      const createdFinding = {
        id: findingId,
        workspaceId,
        taskId: null,
        ...createDto,
        createdAt: new Date(),
        updatedAt: new Date(),
      };

      mockFindingsService.create.mockResolvedValue(createdFinding);

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

      expect(result).toEqual(createdFinding);
      expect(service.create).toHaveBeenCalledWith(workspaceId, createDto);
    });
  });

  describe("findAll", () => {
    it("should return paginated findings", async () => {
      const query: QueryFindingsDto = {
        page: 1,
        limit: 10,
        type: "security",
      };

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

      mockFindingsService.findAll.mockResolvedValue(response);

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

      expect(result).toEqual(response);
      expect(service.findAll).toHaveBeenCalledWith(workspaceId, query);
    });
  });

  describe("findOne", () => {
    it("should return a finding", async () => {
      const finding = {
        id: findingId,
        workspaceId,
        taskId: null,
        agentId: "research-agent",
        type: "security",
        title: "SQL injection risk",
        data: { severity: "high" },
        summary: "Potential SQL injection in search endpoint.",
        createdAt: new Date(),
        updatedAt: new Date(),
      };

      mockFindingsService.findOne.mockResolvedValue(finding);

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

      expect(result).toEqual(finding);
      expect(service.findOne).toHaveBeenCalledWith(findingId, workspaceId);
    });
  });

  describe("search", () => {
    it("should perform semantic search", async () => {
      const searchDto: SearchFindingsDto = {
        query: "sql injection",
        limit: 5,
      };

      const response = {
        data: [
          {
            id: findingId,
            workspaceId,
            taskId: null,
            agentId: "research-agent",
            type: "security",
            title: "SQL injection risk",
            data: { severity: "high" },
            summary: "Potential SQL injection in search endpoint.",
            createdAt: new Date(),
            updatedAt: new Date(),
            score: 0.91,
          },
        ],
        meta: {
          total: 1,
          page: 1,
          limit: 5,
          totalPages: 1,
        },
        query: "sql injection",
      };

      mockFindingsService.search.mockResolvedValue(response);

      const result = await controller.search(searchDto, workspaceId);

      expect(result).toEqual(response);
      expect(service.search).toHaveBeenCalledWith(workspaceId, searchDto);
    });
  });

  describe("remove", () => {
    it("should delete a finding", async () => {
      const response = { message: "Finding deleted successfully" };
      mockFindingsService.remove.mockResolvedValue(response);

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

      expect(result).toEqual(response);
      expect(service.remove).toHaveBeenCalledWith(findingId, workspaceId);
    });
  });
});
