import { AdminUser } from '@core/admin/domain/entities/AdminUser';
import { Result } from '@core/shared/domain/Result';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { AdminService } from './AdminService';

// Mock use cases
const mockListUsersUseCase = {
  execute: vi.fn(),
};

const mockGetDashboardStatsUseCase = {
  execute: vi.fn(),
};

describe('AdminService', () => {
  describe('TDD - Test First', () => {
    let service: AdminService;

    beforeEach(() => {
      vi.clearAllMocks();
      service = new AdminService(
        mockListUsersUseCase as never,
        mockGetDashboardStatsUseCase as never
      );
    });

    describe('listUsers', () => {
      it('should return empty list when no users exist', async () => {
        // Arrange
        const expectedResult = {
          users: [],
          total: 0,
          page: 1,
          limit: 10,
          totalPages: 0,
        };
        
        mockListUsersUseCase.execute.mockResolvedValue(Result.ok(expectedResult));

        // Act
        const result = await service.listUsers({ actorId: 'actor-1' });

        // Assert
        expect(mockListUsersUseCase.execute).toHaveBeenCalledWith({ actorId: 'actor-1' });
        expect(result).toEqual({
          users: [],
          total: 0,
          page: 1,
          limit: 10,
          totalPages: 0,
        });
      });

      it('should return users when they exist', async () => {
        // Arrange
        const user1 = AdminUser.create({
          id: 'user-1',
          email: 'user1@example.com',
          displayName: 'User 1',
          roles: ['user'],
          status: 'active',
        });

        const user2 = AdminUser.create({
          id: 'user-2',
          email: 'user2@example.com',
          displayName: 'User 2',
          roles: ['admin'],
          status: 'active',
        });

        const expectedResult = {
          users: [user1, user2],
          total: 2,
          page: 1,
          limit: 10,
          totalPages: 1,
        };

        mockListUsersUseCase.execute.mockResolvedValue(Result.ok(expectedResult));

        // Act
        const result = await service.listUsers({ actorId: 'actor-1' });

        // Assert
        expect(result.users).toHaveLength(2);
        expect(result.total).toBe(2);
        // Check that users are converted to DTOs
        expect(result.users[0]?.id).toBe('user-1');
        expect(result.users[0]?.email).toBe('user1@example.com');
        expect(result.users[1]?.id).toBe('user-2');
        expect(result.users[1]?.email).toBe('user2@example.com');
      });

      it('should apply filters correctly', async () => {
        // Arrange
        const adminUser = AdminUser.create({
          id: 'admin-1',
          email: 'admin@example.com',
          displayName: 'Admin',
          roles: ['admin'],
          status: 'active',
        });

        const expectedResult = {
          users: [adminUser],
          total: 1,
          page: 1,
          limit: 10,
          totalPages: 1,
        };

        mockListUsersUseCase.execute.mockResolvedValue(Result.ok(expectedResult));

        // Act
        const result = await service.listUsers({
          actorId: 'actor-1',
          role: 'admin',
          status: 'active',
        });

        // Assert
        expect(mockListUsersUseCase.execute).toHaveBeenCalledWith(
          expect.objectContaining({
            actorId: 'actor-1',
            role: 'admin',
            status: 'active',
          })
        );
        expect(result.users).toHaveLength(1);
      });

      it('should apply pagination correctly', async () => {
        // Arrange
        const expectedResult = {
          users: [],
          total: 50,
          page: 3,
          limit: 10,
          totalPages: 5,
        };

        mockListUsersUseCase.execute.mockResolvedValue(Result.ok(expectedResult));

        // Act
        const result = await service.listUsers({
          actorId: 'actor-1',
          page: 3,
          limit: 10,
        });

        // Assert
        expect(mockListUsersUseCase.execute).toHaveBeenCalledWith(
          expect.objectContaining({
            actorId: 'actor-1',
            page: 3,
            limit: 10,
          })
        );
        expect(result.page).toBe(3);
        expect(result.limit).toBe(10);
        expect(result.totalPages).toBe(5);
      });

      it('should apply sorting correctly', async () => {
        // Arrange
        const expectedResult = {
          users: [],
          total: 0,
          page: 1,
          limit: 10,
          totalPages: 0,
        };

        mockListUsersUseCase.execute.mockResolvedValue(Result.ok(expectedResult));

        // Act
        await service.listUsers({
          actorId: 'actor-1',
          sortBy: 'email',
          sortDirection: 'desc',
        });

        // Assert
        expect(mockListUsersUseCase.execute).toHaveBeenCalledWith(
          expect.objectContaining({
            actorId: 'actor-1',
            sortBy: 'email',
            sortDirection: 'desc',
          })
        );
      });

      it('should handle search filter', async () => {
        // Arrange
        const expectedResult = {
          users: [],
          total: 0,
          page: 1,
          limit: 10,
          totalPages: 0,
        };

        mockListUsersUseCase.execute.mockResolvedValue(Result.ok(expectedResult));

        // Act
        await service.listUsers({
          actorId: 'actor-1',
          search: 'test',
        });

        // Assert
        expect(mockListUsersUseCase.execute).toHaveBeenCalledWith(
          expect.objectContaining({
            actorId: 'actor-1',
            search: 'test',
          })
        );
      });

      it('should handle email filter', async () => {
        // Arrange
        const expectedResult = {
          users: [],
          total: 0,
          page: 1,
          limit: 10,
          totalPages: 0,
        };

        mockListUsersUseCase.execute.mockResolvedValue(Result.ok(expectedResult));

        // Act
        await service.listUsers({
          actorId: 'actor-1',
          email: 'test@example.com',
        });

        // Assert
        expect(mockListUsersUseCase.execute).toHaveBeenCalledWith(
          expect.objectContaining({
            actorId: 'actor-1',
            email: 'test@example.com',
          })
        );
      });

      it('should throw error when use case fails', async () => {
        // Arrange
        const error = { code: 'UNAUTHORIZED', details: { message: 'Not authorized' } };
        mockListUsersUseCase.execute.mockResolvedValue(Result.err(error));

        // Act & Assert
        await expect(
          service.listUsers({ actorId: 'actor-1' })
        ).rejects.toThrow('UNAUTHORIZED: Not authorized');
      });

      it('should handle all filters together', async () => {
        // Arrange
        const expectedResult = {
          users: [],
          total: 0,
          page: 1,
          limit: 10,
          totalPages: 0,
        };

        mockListUsersUseCase.execute.mockResolvedValue(Result.ok(expectedResult));

        // Act
        await service.listUsers({
          actorId: 'actor-1',
          role: 'admin',
          status: 'active',
          email: 'admin@example.com',
          search: 'admin',
          page: 2,
          limit: 20,
          sortBy: 'displayName',
          sortDirection: 'asc',
        });

        // Assert
        expect(mockListUsersUseCase.execute).toHaveBeenCalledWith(
          expect.objectContaining({
            actorId: 'actor-1',
            role: 'admin',
            status: 'active',
            email: 'admin@example.com',
            search: 'admin',
            page: 2,
            limit: 20,
            sortBy: 'displayName',
            sortDirection: 'asc',
          })
        );
      });
    });
  });
});