import { describe, it, expect, beforeEach, vi } from "vitest";
import { Test, TestingModule } from "@nestjs/testing";
import { DomainsController } from "./domains.controller";
import { DomainsService } from "./domains.service";
import { CreateDomainDto, UpdateDomainDto, QueryDomainsDto } from "./dto";
import { AuthGuard } from "../auth/guards/auth.guard";
import { WorkspaceGuard, PermissionGuard } from "../common/guards";
import { ExecutionContext } from "@nestjs/common";

describe("DomainsController", () => {
  let controller: DomainsController;
  let service: DomainsService;

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

  const mockAuthGuard = {
    canActivate: vi.fn((context: ExecutionContext) => {
      const request = context.switchToHttp().getRequest();
      request.user = {
        id: "550e8400-e29b-41d4-a716-446655440002",
        workspaceId: "550e8400-e29b-41d4-a716-446655440001",
      };
      return true;
    }),
  };

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

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

  const mockWorkspaceId = "550e8400-e29b-41d4-a716-446655440001";
  const mockUserId = "550e8400-e29b-41d4-a716-446655440002";
  const mockDomainId = "550e8400-e29b-41d4-a716-446655440003";

  const mockDomain = {
    id: mockDomainId,
    workspaceId: mockWorkspaceId,
    name: "Work",
    slug: "work",
    description: "Work-related tasks and projects",
    color: "#3B82F6",
    icon: "briefcase",
    sortOrder: 0,
    metadata: {},
    createdAt: new Date(),
    updatedAt: new Date(),
  };

  const mockUser = {
    id: mockUserId,
    email: "test@example.com",
    name: "Test User",
  };

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

    controller = module.get<DomainsController>(DomainsController);
    service = module.get<DomainsService>(DomainsService);

    // Clear all mocks before each test
    vi.clearAllMocks();
  });

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

  describe("create", () => {
    it("should create a domain", async () => {
      const createDto: CreateDomainDto = {
        name: "Work",
        slug: "work",
        description: "Work-related tasks",
        color: "#3B82F6",
        icon: "briefcase",
      };

      mockDomainsService.create.mockResolvedValue(mockDomain);

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

      expect(result).toEqual(mockDomain);
      expect(service.create).toHaveBeenCalledWith(mockWorkspaceId, mockUserId, createDto);
    });
  });

  describe("findAll", () => {
    it("should return paginated domains", async () => {
      const query: QueryDomainsDto = { page: 1, limit: 10 };
      const paginatedResult = {
        data: [mockDomain],
        meta: {
          total: 1,
          page: 1,
          limit: 10,
          totalPages: 1,
        },
      };

      mockDomainsService.findAll.mockResolvedValue(paginatedResult);

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

      expect(result).toEqual(paginatedResult);
      expect(service.findAll).toHaveBeenCalledWith({
        ...query,
        workspaceId: mockWorkspaceId,
      });
    });

    it("should handle search query", async () => {
      const query: QueryDomainsDto = {
        page: 1,
        limit: 10,
        search: "work",
      };

      mockDomainsService.findAll.mockResolvedValue({
        data: [mockDomain],
        meta: { total: 1, page: 1, limit: 10, totalPages: 1 },
      });

      await controller.findAll(query, mockWorkspaceId);

      expect(service.findAll).toHaveBeenCalledWith({
        ...query,
        workspaceId: mockWorkspaceId,
      });
    });
  });

  describe("findOne", () => {
    it("should return a domain by ID", async () => {
      mockDomainsService.findOne.mockResolvedValue(mockDomain);

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

      expect(result).toEqual(mockDomain);
      expect(service.findOne).toHaveBeenCalledWith(mockDomainId, mockWorkspaceId);
    });
  });

  describe("update", () => {
    it("should update a domain", async () => {
      const updateDto: UpdateDomainDto = {
        name: "Updated Work",
        color: "#10B981",
      };

      const updatedDomain = { ...mockDomain, ...updateDto };
      mockDomainsService.update.mockResolvedValue(updatedDomain);

      const result = await controller.update(mockDomainId, updateDto, mockWorkspaceId, mockUser);

      expect(result).toEqual(updatedDomain);
      expect(service.update).toHaveBeenCalledWith(
        mockDomainId,
        mockWorkspaceId,
        mockUserId,
        updateDto
      );
    });
  });

  describe("remove", () => {
    it("should delete a domain", async () => {
      mockDomainsService.remove.mockResolvedValue(undefined);

      await controller.remove(mockDomainId, mockWorkspaceId, mockUser);

      expect(service.remove).toHaveBeenCalledWith(mockDomainId, mockWorkspaceId, mockUserId);
    });
  });
});
