import { describe, it, expect, beforeEach, vi } from "vitest";
import { Test, TestingModule } from "@nestjs/testing";
import { AdminController } from "./admin.controller";
import { AdminService } from "./admin.service";
import { AuthGuard } from "../auth/guards/auth.guard";
import { AdminGuard } from "../auth/guards/admin.guard";
import { WorkspaceMemberRole } from "@prisma/client";
import type { ExecutionContext } from "@nestjs/common";

describe("AdminController", () => {
  let controller: AdminController;
  let service: AdminService;

  const mockAdminService = {
    listUsers: vi.fn(),
    inviteUser: vi.fn(),
    updateUser: vi.fn(),
    deactivateUser: vi.fn(),
    createWorkspace: vi.fn(),
    updateWorkspace: vi.fn(),
  };

  const mockAuthGuard = {
    canActivate: vi.fn((context: ExecutionContext) => {
      const request = context.switchToHttp().getRequest();
      request.user = {
        id: "550e8400-e29b-41d4-a716-446655440001",
        email: "admin@example.com",
        name: "Admin User",
      };
      return true;
    }),
  };

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

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

  const mockAdminUser = {
    id: mockAdminId,
    email: "admin@example.com",
    name: "Admin User",
  };

  const mockUserResponse = {
    id: mockUserId,
    name: "Test User",
    email: "test@example.com",
    emailVerified: false,
    image: null,
    createdAt: new Date("2026-01-01"),
    deactivatedAt: null,
    isLocalAuth: false,
    invitedAt: null,
    invitedBy: null,
    workspaceMemberships: [],
  };

  const mockWorkspaceResponse = {
    id: mockWorkspaceId,
    name: "Test Workspace",
    ownerId: mockAdminId,
    settings: {},
    createdAt: new Date("2026-01-01"),
    updatedAt: new Date("2026-01-01"),
    memberCount: 1,
  };

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      controllers: [AdminController],
      providers: [
        {
          provide: AdminService,
          useValue: mockAdminService,
        },
      ],
    })
      .overrideGuard(AuthGuard)
      .useValue(mockAuthGuard)
      .overrideGuard(AdminGuard)
      .useValue(mockAdminGuard)
      .compile();

    controller = module.get<AdminController>(AdminController);
    service = module.get<AdminService>(AdminService);

    vi.clearAllMocks();
  });

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

  describe("listUsers", () => {
    it("should return paginated users", async () => {
      const paginatedResult = {
        data: [mockUserResponse],
        meta: { total: 1, page: 1, limit: 50, totalPages: 1 },
      };
      mockAdminService.listUsers.mockResolvedValue(paginatedResult);

      const result = await controller.listUsers({ page: 1, limit: 50 });

      expect(result).toEqual(paginatedResult);
      expect(service.listUsers).toHaveBeenCalledWith(1, 50);
    });

    it("should use default pagination", async () => {
      const paginatedResult = {
        data: [],
        meta: { total: 0, page: 1, limit: 50, totalPages: 0 },
      };
      mockAdminService.listUsers.mockResolvedValue(paginatedResult);

      await controller.listUsers({});

      expect(service.listUsers).toHaveBeenCalledWith(undefined, undefined);
    });
  });

  describe("inviteUser", () => {
    it("should invite a user", async () => {
      const inviteDto = { email: "new@example.com" };
      const invitationResponse = {
        userId: "new-id",
        invitationToken: "token",
        email: "new@example.com",
        invitedAt: new Date(),
      };
      mockAdminService.inviteUser.mockResolvedValue(invitationResponse);

      const result = await controller.inviteUser(inviteDto, mockAdminUser);

      expect(result).toEqual(invitationResponse);
      expect(service.inviteUser).toHaveBeenCalledWith(inviteDto, mockAdminId);
    });

    it("should invite a user with workspace and role", async () => {
      const inviteDto = {
        email: "new@example.com",
        workspaceId: mockWorkspaceId,
        role: WorkspaceMemberRole.ADMIN,
      };
      mockAdminService.inviteUser.mockResolvedValue({
        userId: "new-id",
        invitationToken: "token",
        email: "new@example.com",
        invitedAt: new Date(),
      });

      await controller.inviteUser(inviteDto, mockAdminUser);

      expect(service.inviteUser).toHaveBeenCalledWith(inviteDto, mockAdminId);
    });
  });

  describe("updateUser", () => {
    it("should update a user", async () => {
      const updateDto = { name: "Updated Name" };
      mockAdminService.updateUser.mockResolvedValue({
        ...mockUserResponse,
        name: "Updated Name",
      });

      const result = await controller.updateUser(mockUserId, updateDto);

      expect(result.name).toBe("Updated Name");
      expect(service.updateUser).toHaveBeenCalledWith(mockUserId, updateDto);
    });

    it("should deactivate a user via update", async () => {
      const deactivatedAt = "2026-02-28T00:00:00.000Z";
      const updateDto = { deactivatedAt };
      mockAdminService.updateUser.mockResolvedValue({
        ...mockUserResponse,
        deactivatedAt: new Date(deactivatedAt),
      });

      const result = await controller.updateUser(mockUserId, updateDto);

      expect(result.deactivatedAt).toEqual(new Date(deactivatedAt));
    });
  });

  describe("deactivateUser", () => {
    it("should soft-delete a user", async () => {
      mockAdminService.deactivateUser.mockResolvedValue({
        ...mockUserResponse,
        deactivatedAt: new Date(),
      });

      const result = await controller.deactivateUser(mockUserId);

      expect(result.deactivatedAt).toBeDefined();
      expect(service.deactivateUser).toHaveBeenCalledWith(mockUserId);
    });
  });

  describe("createWorkspace", () => {
    it("should create a workspace", async () => {
      const createDto = { name: "New Workspace", ownerId: mockAdminId };
      mockAdminService.createWorkspace.mockResolvedValue(mockWorkspaceResponse);

      const result = await controller.createWorkspace(createDto);

      expect(result).toEqual(mockWorkspaceResponse);
      expect(service.createWorkspace).toHaveBeenCalledWith(createDto);
    });

    it("should create workspace with settings", async () => {
      const createDto = {
        name: "New Workspace",
        ownerId: mockAdminId,
        settings: { feature: true },
      };
      mockAdminService.createWorkspace.mockResolvedValue({
        ...mockWorkspaceResponse,
        settings: { feature: true },
      });

      const result = await controller.createWorkspace(createDto);

      expect(result.settings).toEqual({ feature: true });
    });
  });

  describe("updateWorkspace", () => {
    it("should update a workspace", async () => {
      const updateDto = { name: "Updated Workspace" };
      mockAdminService.updateWorkspace.mockResolvedValue({
        ...mockWorkspaceResponse,
        name: "Updated Workspace",
      });

      const result = await controller.updateWorkspace(mockWorkspaceId, updateDto);

      expect(result.name).toBe("Updated Workspace");
      expect(service.updateWorkspace).toHaveBeenCalledWith(mockWorkspaceId, updateDto);
    });

    it("should update workspace settings", async () => {
      const updateDto = { settings: { notifications: false } };
      mockAdminService.updateWorkspace.mockResolvedValue({
        ...mockWorkspaceResponse,
        settings: { notifications: false },
      });

      const result = await controller.updateWorkspace(mockWorkspaceId, updateDto);

      expect(result.settings).toEqual({ notifications: false });
    });
  });
});
