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 type { CreatePersonalityDto } from "./dto/create-personality.dto";
import type { UpdatePersonalityDto } from "./dto/update-personality.dto";
import { AuthGuard } from "../auth/guards/auth.guard";
import { WorkspaceGuard, PermissionGuard } from "../common/guards";
import { FormalityLevel } from "@prisma/client";

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

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

  /** API response shape (frontend field names) */
  const mockPersonality = {
    id: mockPersonalityId,
    workspaceId: mockWorkspaceId,
    name: "professional-assistant",
    description: "A professional communication assistant",
    tone: "professional",
    formalityLevel: FormalityLevel.FORMAL,
    systemPromptTemplate: "You are a professional assistant who helps with tasks.",
    isDefault: true,
    isActive: true,
    createdAt: new Date("2026-01-01"),
    updatedAt: new Date("2026-01-01"),
  };

  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 })
      .overrideGuard(WorkspaceGuard)
      .useValue({
        canActivate: (ctx: {
          switchToHttp: () => { getRequest: () => { workspaceId: string } };
        }) => {
          const req = ctx.switchToHttp().getRequest();
          req.workspaceId = mockWorkspaceId;
          return true;
        },
      })
      .overrideGuard(PermissionGuard)
      .useValue({ canActivate: () => true })
      .compile();

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

    vi.clearAllMocks();
  });

  describe("findAll", () => {
    it("should return success response with personalities list", async () => {
      const mockList = [mockPersonality];
      mockPersonalitiesService.findAll.mockResolvedValue(mockList);

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

      expect(result).toEqual({ success: true, data: mockList });
      expect(service.findAll).toHaveBeenCalledWith(mockWorkspaceId, {});
    });

    it("should pass isActive query filter to service", async () => {
      mockPersonalitiesService.findAll.mockResolvedValue([mockPersonality]);

      await controller.findAll(mockWorkspaceId, { isActive: true });

      expect(service.findAll).toHaveBeenCalledWith(mockWorkspaceId, { isActive: true });
    });
  });

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

      const result = await controller.findDefault(mockWorkspaceId);

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

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

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

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

  describe("create", () => {
    it("should create a new personality", async () => {
      const createDto: CreatePersonalityDto = {
        name: "casual-helper",
        description: "A casual helper",
        tone: "casual",
        formalityLevel: FormalityLevel.CASUAL,
        systemPromptTemplate: "You are a casual assistant.",
      };

      const created = { ...mockPersonality, ...createDto, isActive: true, isDefault: false };
      mockPersonalitiesService.create.mockResolvedValue(created);

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

      expect(result).toMatchObject({ name: createDto.name, tone: createDto.tone });
      expect(service.create).toHaveBeenCalledWith(mockWorkspaceId, createDto);
    });
  });

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

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

      const result = await controller.update(mockWorkspaceId, 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(mockWorkspaceId, mockPersonalityId);

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

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

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

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