import { describe, expect, it, vi } from 'vitest';
import { AdminController } from './AdminController';
import { DashboardStatsResponseDto } from './dto/DashboardStatsResponseDto';
import { ListUsersRequestDto } from './dtos/ListUsersRequestDto';
import { UserListResponseDto, UserResponseDto } from './dtos/UserResponseDto';

describe('AdminController', () => {
  let controller: AdminController;
  let mockService: {
    listUsers: ReturnType<typeof vi.fn>;
    getDashboardStats: ReturnType<typeof vi.fn>;
  };

  beforeEach(() => {
    mockService = {
      listUsers: vi.fn(),
      getDashboardStats: vi.fn(),
    };

    controller = new AdminController(mockService as never);
  });

  describe('listUsers', () => {
    it('should list users with basic query params', async () => {
      const mockUser: UserResponseDto = {
        id: 'user-1',
        email: 'test@example.com',
        displayName: 'Test User',
        roles: ['admin'],
        status: 'active',
        isSystemAdmin: false,
        createdAt: new Date(),
        updatedAt: new Date(),
      };

      const mockResponse: UserListResponseDto = {
        users: [mockUser],
        total: 1,
        page: 1,
        limit: 10,
        totalPages: 1,
      };

      mockService.listUsers.mockResolvedValue(mockResponse);

      const query: ListUsersRequestDto = {
        page: 1,
        limit: 10,
      };

      const req = { user: { userId: 'admin-1' } } as never;
      const result = await controller.listUsers(query, req);

      expect(mockService.listUsers).toHaveBeenCalledWith({
        actorId: 'admin-1',
        page: 1,
        limit: 10,
      });
      expect(result).toEqual(mockResponse);
    });

    it('should list users with all query params', async () => {
      const mockResponse: UserListResponseDto = {
        users: [],
        total: 0,
        page: 2,
        limit: 20,
        totalPages: 0,
      };

      mockService.listUsers.mockResolvedValue(mockResponse);

      const query: ListUsersRequestDto = {
        page: 2,
        limit: 20,
        role: 'owner',
        status: 'active',
        email: 'admin',
        search: 'test',
        sortBy: 'email',
        sortDirection: 'desc',
      };

      const req = { user: { userId: 'owner-1' } } as never;
      const result = await controller.listUsers(query, req);

      expect(mockService.listUsers).toHaveBeenCalledWith({
        actorId: 'owner-1',
        page: 2,
        limit: 20,
        role: 'owner',
        status: 'active',
        email: 'admin',
        search: 'test',
        sortBy: 'email',
        sortDirection: 'desc',
      });
      expect(result).toEqual(mockResponse);
    });

    it('should handle missing user ID from request', async () => {
      const mockResponse: UserListResponseDto = {
        users: [],
        total: 0,
        page: 1,
        limit: 10,
        totalPages: 0,
      };

      mockService.listUsers.mockResolvedValue(mockResponse);

      const query: ListUsersRequestDto = { page: 1, limit: 10 };
      const req = {} as never;

      await controller.listUsers(query, req);

      expect(mockService.listUsers).toHaveBeenCalledWith({
        actorId: 'current-user',
        page: 1,
        limit: 10,
      });
    });

    it('should handle optional query params being undefined', async () => {
      const mockResponse: UserListResponseDto = {
        users: [],
        total: 0,
        page: 1,
        limit: 10,
        totalPages: 0,
      };

      mockService.listUsers.mockResolvedValue(mockResponse);

      const query: ListUsersRequestDto = {
        page: 1,
        limit: 10,
      };

      const req = { user: { userId: 'admin-1' } } as never;
      await controller.listUsers(query, req);

      expect(mockService.listUsers).toHaveBeenCalledWith({
        actorId: 'admin-1',
        page: 1,
        limit: 10,
      });
    });
  });

  describe('getDashboardStats', () => {
    it('should return dashboard stats', async () => {
      const mockStats: DashboardStatsResponseDto = {
        totalUsers: 150,
        activeUsers: 120,
        suspendedUsers: 20,
        deletedUsers: 10,
        systemAdmins: 5,
        recentLogins: 25,
        newUsersToday: 8,
        userGrowth: [],
        roleDistribution: [],
        statusDistribution: {
          active: 120,
          suspended: 20,
          deleted: 10,
        },
        activityTimeline: [],
      };

      mockService.getDashboardStats.mockResolvedValue(mockStats);

      const req = { user: { userId: 'admin-1' } } as never;
      const result = await controller.getDashboardStats(req);

      expect(mockService.getDashboardStats).toHaveBeenCalledWith({
        actorId: 'admin-1',
      });
      expect(result).toEqual(mockStats);
    });

    it('should handle missing user ID from request', async () => {
      const mockStats: DashboardStatsResponseDto = {
        totalUsers: 0,
        activeUsers: 0,
        suspendedUsers: 0,
        deletedUsers: 0,
        systemAdmins: 0,
        recentLogins: 0,
        newUsersToday: 0,
        userGrowth: [],
        roleDistribution: [],
        statusDistribution: {
          active: 0,
          suspended: 0,
          deleted: 0,
        },
        activityTimeline: [],
      };

      mockService.getDashboardStats.mockResolvedValue(mockStats);

      const req = {} as never;
      const result = await controller.getDashboardStats(req);

      expect(mockService.getDashboardStats).toHaveBeenCalledWith({
        actorId: 'current-user',
      });
      expect(result).toEqual(mockStats);
    });

    it('should handle service errors gracefully', async () => {
      mockService.getDashboardStats.mockRejectedValue(new Error('Database connection failed'));

      const req = { user: { userId: 'admin-1' } } as never;

      await expect(controller.getDashboardStats(req)).rejects.toThrow('Database connection failed');
    });
  });
});