import { describe, it, expect, beforeEach, vi } from "vitest";
import { Test, TestingModule } from "@nestjs/testing";
import { PersonalitiesController } from "./personalities.controller";
import { PersonalitiesService } from "./personalities.service";
import { CreatePersonalityDto, UpdatePersonalityDto } from "./dto";
import { AuthGuard } from "../auth/guards/auth.guard";

describe("PersonalitiesController", () => {
  let controller: PersonalitiesController;
  let service: PersonalitiesService;

  const mockWorkspaceId = "workspace-123";
  const mockUserId = "user-123";
  const mockPersonalityId = "personality-123";

  const mockPersonality = {
    id: mockPersonalityId,
    workspaceId: mockWorkspaceId,
    name: "professional-assistant",
    displayName: "Professional Assistant",
    description: "A professional communication assistant",
    systemPrompt: "You are a professional assistant who helps with tasks.",
    temperature: 0.7,
    maxTokens: 2000,
    llmProviderInstanceId: "provider-123",
    isDefault: true,
    isEnabled: true,
    createdAt: new Date(),
    updatedAt: new Date(),
  };

  const mockRequest = {
    user: { id: mockUserId },
    workspaceId: mockWorkspaceId,
  };

  const mockPersonalitiesService = {
    create: vi.fn(),
    findAll: vi.fn(),
    findOne: vi.fn(),
    findByName: vi.fn(),
    findDefault: vi.fn(),
    update: vi.fn(),
    delete: vi.fn(),
    setDefault: vi.fn(),
  };

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      controllers: [PersonalitiesController],
      providers: [
        {
          provide: PersonalitiesService,
          useValue: mockPersonalitiesService,
        },
      ],
    })
      .overrideGuard(AuthGuard)
      .useValue({ canActivate: () => true })
      .compile();

    controller = module.get<PersonalitiesController>(PersonalitiesController);
    service = module.get<PersonalitiesService>(PersonalitiesService);

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

  describe("findAll", () => {
    it("should return all personalities", async () => {
      const mockPersonalities = [mockPersonality];
      mockPersonalitiesService.findAll.mockResolvedValue(mockPersonalities);

      const result = await controller.findAll(mockRequest);

      expect(result).toEqual(mockPersonalities);
      expect(service.findAll).toHaveBeenCalledWith(mockWorkspaceId);
    });
  });

  describe("findOne", () => {
    it("should return a personality by id", async () => {
      mockPersonalitiesService.findOne.mockResolvedValue(mockPersonality);

      const result = await controller.findOne(mockRequest, mockPersonalityId);

      expect(result).toEqual(mockPersonality);
      expect(service.findOne).toHaveBeenCalledWith(mockWorkspaceId, mockPersonalityId);
    });
  });

  describe("findByName", () => {
    it("should return a personality by name", async () => {
      mockPersonalitiesService.findByName.mockResolvedValue(mockPersonality);

      const result = await controller.findByName(mockRequest, "professional-assistant");

      expect(result).toEqual(mockPersonality);
      expect(service.findByName).toHaveBeenCalledWith(mockWorkspaceId, "professional-assistant");
    });
  });

  describe("findDefault", () => {
    it("should return the default personality", async () => {
      mockPersonalitiesService.findDefault.mockResolvedValue(mockPersonality);

      const result = await controller.findDefault(mockRequest);

      expect(result).toEqual(mockPersonality);
      expect(service.findDefault).toHaveBeenCalledWith(mockWorkspaceId);
    });
  });

  describe("create", () => {
    it("should create a new personality", async () => {
      const createDto: CreatePersonalityDto = {
        name: "casual-helper",
        displayName: "Casual Helper",
        description: "A casual helper",
        systemPrompt: "You are a casual assistant.",
        temperature: 0.8,
        maxTokens: 1500,
      };

      mockPersonalitiesService.create.mockResolvedValue({
        ...mockPersonality,
        ...createDto,
      });

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

      expect(result).toMatchObject(createDto);
      expect(service.create).toHaveBeenCalledWith(mockWorkspaceId, createDto);
    });
  });

  describe("update", () => {
    it("should update a personality", async () => {
      const updateDto: UpdatePersonalityDto = {
        description: "Updated description",
        temperature: 0.9,
      };

      mockPersonalitiesService.update.mockResolvedValue({
        ...mockPersonality,
        ...updateDto,
      });

      const result = await controller.update(mockRequest, mockPersonalityId, updateDto);

      expect(result).toMatchObject(updateDto);
      expect(service.update).toHaveBeenCalledWith(mockWorkspaceId, mockPersonalityId, updateDto);
    });
  });

  describe("delete", () => {
    it("should delete a personality", async () => {
      mockPersonalitiesService.delete.mockResolvedValue(undefined);

      await controller.delete(mockRequest, mockPersonalityId);

      expect(service.delete).toHaveBeenCalledWith(mockWorkspaceId, mockPersonalityId);
    });
  });

  describe("setDefault", () => {
    it("should set a personality as default", async () => {
      mockPersonalitiesService.setDefault.mockResolvedValue({
        ...mockPersonality,
        isDefault: true,
      });

      const result = await controller.setDefault(mockRequest, mockPersonalityId);

      expect(result).toMatchObject({ isDefault: true });
      expect(service.setDefault).toHaveBeenCalledWith(mockWorkspaceId, mockPersonalityId);
    });
  });
});
