import { AdminUser } from '@core/admin/domain/entities/AdminUser';
import { Email } from '@core/admin/domain/value-objects/Email';
import { UserId } from '@core/admin/domain/value-objects/UserId';
import { UserRole } from '@core/admin/domain/value-objects/UserRole';
import { UserStatus } from '@core/admin/domain/value-objects/UserStatus';
import { DataSource } from 'typeorm';
import { AdminUserOrmEntity } from '../entities/AdminUserOrmEntity';
import { TypeOrmAdminUserRepository } from './TypeOrmAdminUserRepository';

describe('TypeOrmAdminUserRepository', () => {
  let dataSource: DataSource;
  let repository: TypeOrmAdminUserRepository;

  beforeAll(async () => {
    // Create in-memory SQLite database for testing
    dataSource = new DataSource({
      type: 'sqlite',
      database: ':memory:',
      entities: [AdminUserOrmEntity],
      synchronize: true,
      logging: false,
    });

    await dataSource.initialize();
    repository = new TypeOrmAdminUserRepository(dataSource);
  });

  afterAll(async () => {
    if (dataSource.isInitialized) {
      await dataSource.destroy();
    }
  });

  beforeEach(async () => {
    // Clean database before each test
    await dataSource.getRepository(AdminUserOrmEntity).clear();
  });

  describe('TDD - Test First', () => {
    it('should save and retrieve an admin user', async () => {
      // Arrange
      const user = AdminUser.create({
        id: 'user-123',
        email: 'admin@example.com',
        displayName: 'Admin User',
        roles: ['owner'],
        status: 'active',
      });

      // Act
      await repository.save(user);
      const retrieved = await repository.findById(UserId.fromString('user-123'));

      // Assert
      expect(retrieved).not.toBeNull();
      expect(retrieved!.id.value).toBe('user-123');
      expect(retrieved!.email.value).toBe('admin@example.com');
      expect(retrieved!.displayName).toBe('Admin User');
      expect(retrieved!.roles[0]!.value).toBe('owner');
      expect(retrieved!.status.value).toBe('active');
    });

    it('should return null when user not found by ID', async () => {
      // Act
      const result = await repository.findById(UserId.fromString('non-existent'));

      // Assert
      expect(result).toBeNull();
    });

    it('should find user by email', async () => {
      // Arrange
      const user = AdminUser.create({
        id: 'user-123',
        email: 'test@example.com',
        displayName: 'Test User',
        roles: ['user'],
        status: 'active',
      });

      await repository.save(user);

      // Act
      const result = await repository.findByEmail(Email.fromString('test@example.com'));

      // Assert
      expect(result).not.toBeNull();
      expect(result!.email.value).toBe('test@example.com');
    });

    it('should check if user exists by ID', async () => {
      // Arrange
      const user = AdminUser.create({
        id: 'user-123',
        email: 'test@example.com',
        displayName: 'Test User',
        roles: ['user'],
        status: 'active',
      });

      await repository.save(user);

      // Act & Assert
      expect(await repository.existsById(UserId.fromString('user-123'))).toBe(true);
      expect(await repository.existsById(UserId.fromString('non-existent'))).toBe(false);
    });

    it('should check if user exists by email', async () => {
      // Arrange
      const user = AdminUser.create({
        id: 'user-123',
        email: 'test@example.com',
        displayName: 'Test User',
        roles: ['user'],
        status: 'active',
      });

      await repository.save(user);

      // Act & Assert
      expect(await repository.existsByEmail(Email.fromString('test@example.com'))).toBe(true);
      expect(await repository.existsByEmail(Email.fromString('other@example.com'))).toBe(false);
    });

    it('should delete a user', async () => {
      // Arrange
      const user = AdminUser.create({
        id: 'user-123',
        email: 'test@example.com',
        displayName: 'Test User',
        roles: ['user'],
        status: 'active',
      });

      await repository.save(user);

      // Act
      await repository.delete(UserId.fromString('user-123'));
      const result = await repository.findById(UserId.fromString('user-123'));

      // Assert
      expect(result).toBeNull();
    });

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

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

      await repository.save(user1);
      await repository.save(user2);

      // Act
      const result = await repository.list();

      // Assert
      expect(result.users).toHaveLength(2);
      expect(result.total).toBe(2);
      expect(result.page).toBe(1);
      expect(result.limit).toBe(10);
      expect(result.totalPages).toBe(1);
    });

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

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

      await repository.save(user1);
      await repository.save(user2);

      // Act
      const result = await repository.list({ filter: { role: UserRole.fromString('admin') } });

      // Assert
      expect(result.users).toHaveLength(1);
      expect(result.users[0]!.id.value).toBe('user-2');
    });

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

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

      await repository.save(user1);
      await repository.save(user2);

      // Act
      const result = await repository.list({ filter: { status: UserStatus.fromString('suspended') } });

      // Assert
      expect(result.users).toHaveLength(1);
      expect(result.users[0]!.id.value).toBe('user-2');
    });

    it('should search users by email or display name', async () => {
      // Arrange
      const user1 = AdminUser.create({
        id: 'user-1',
        email: 'john@example.com',
        displayName: 'John Doe',
        roles: ['user'],
        status: 'active',
      });

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

      await repository.save(user1);
      await repository.save(user2);

      // Act
      const result = await repository.list({ filter: { search: 'john' } });

      // Assert
      expect(result.users).toHaveLength(1);
      expect(result.users[0]!.id.value).toBe('user-1');
    });

    it('should paginate results', async () => {
      // Arrange
      for (let i = 1; i <= 5; i++) {
        const user = AdminUser.create({
          id: `user-${i}`,
          email: `user${i}@example.com`,
          displayName: `User ${i}`,
          roles: ['user'],
          status: 'active',
        });
        await repository.save(user);
      }

      // Act - Get page 1 with limit 2
      const result = await repository.list({ pagination: { page: 1, limit: 2 } });

      // Assert
      expect(result.users).toHaveLength(2);
      expect(result.total).toBe(5);
      expect(result.page).toBe(1);
      expect(result.limit).toBe(2);
      expect(result.totalPages).toBe(3);
    });

    it('should sort results', async () => {
      // Arrange
      const user1 = AdminUser.create({
        id: 'user-1',
        email: 'aaa@example.com',
        displayName: 'AAA',
        roles: ['user'],
        status: 'active',
      });

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

      await repository.save(user1);
      await repository.save(user2);

      // Act - Sort by email ascending
      const result = await repository.list({ sort: { field: 'email', direction: 'asc' } });

      // Assert
      expect(result.users).toHaveLength(2);
      expect(result.users[0]!.email.value).toBe('aaa@example.com');
      expect(result.users[1]!.email.value).toBe('zzz@example.com');
    });

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

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

      const user3 = AdminUser.create({
        id: 'user-3',
        email: 'user3@example.com',
        displayName: 'User Three',
        roles: ['user'],
        status: 'suspended',
      });

      await repository.save(user1);
      await repository.save(user2);
      await repository.save(user3);

      // Act
      const totalActive = await repository.count({ status: UserStatus.fromString('active') });
      const totalAdmins = await repository.count({ role: UserRole.fromString('admin') });
      const totalUsers = await repository.count();

      // Assert
      expect(totalActive).toBe(2);
      expect(totalAdmins).toBe(1);
      expect(totalUsers).toBe(3);
    });

    it('should handle multiple roles', async () => {
      // Arrange
      const user = AdminUser.create({
        id: 'user-123',
        email: 'multi@example.com',
        displayName: 'Multi Role',
        roles: ['owner', 'admin'],
        status: 'active',
      });

      await repository.save(user);

      // Act
      const retrieved = await repository.findById(UserId.fromString('user-123'));

      // Assert
      expect(retrieved).not.toBeNull();
      expect(retrieved!.roles).toHaveLength(2);
      expect(retrieved!.roles.map(r => r.value)).toContain('owner');
      expect(retrieved!.roles.map(r => r.value)).toContain('admin');
    });

    it('should handle optional fields', async () => {
      // Arrange
      const now = new Date();
      const user = AdminUser.create({
        id: 'user-123',
        email: 'full@example.com',
        displayName: 'Full User',
        roles: ['user'],
        status: 'active',
        primaryDriverId: 'driver-456',
        lastLoginAt: now,
      });

      await repository.save(user);

      // Act
      const retrieved = await repository.findById(UserId.fromString('user-123'));

      // Assert
      expect(retrieved).not.toBeNull();
      expect(retrieved!.primaryDriverId).toBe('driver-456');
      expect(retrieved!.lastLoginAt).toEqual(now);
    });

    it('should update existing user', async () => {
      // Arrange
      const user = AdminUser.create({
        id: 'user-123',
        email: 'test@example.com',
        displayName: 'Original Name',
        roles: ['user'],
        status: 'active',
      });

      await repository.save(user);

      // Act - Update the user
      user.updateDisplayName('Updated Name');
      await repository.save(user);

      const retrieved = await repository.findById(UserId.fromString('user-123'));

      // Assert
      expect(retrieved!.displayName).toBe('Updated Name');
    });

    it('should handle complex filtering combinations', async () => {
      // Arrange
      const user1 = AdminUser.create({
        id: 'user-1',
        email: 'admin@example.com',
        displayName: 'Admin User',
        roles: ['admin'],
        status: 'active',
      });

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

      await repository.save(user1);
      await repository.save(user2);

      // Act - Filter by role and status
      const result = await repository.list({ 
        filter: { 
          role: UserRole.fromString('user'), 
          status: UserStatus.fromString('suspended') 
        } 
      });

      // Assert
      expect(result.users).toHaveLength(1);
      expect(result.users[0]!.id.value).toBe('user-2');
    });

    it('should return empty list when no users match filter', async () => {
      // Arrange
      const user = AdminUser.create({
        id: 'user-1',
        email: 'test@example.com',
        displayName: 'Test User',
        roles: ['user'],
        status: 'active',
      });

      await repository.save(user);

      // Act
      const result = await repository.list({ filter: { role: UserRole.fromString('admin') } });

      // Assert
      expect(result.users).toHaveLength(0);
      expect(result.total).toBe(0);
    });

    it('should handle search with special characters', async () => {
      // Arrange
      const user = AdminUser.create({
        id: 'user-1',
        email: 'test+user@example.com',
        displayName: 'Test+User',
        roles: ['user'],
        status: 'active',
      });

      await repository.save(user);

      // Act
      const result = await repository.list({ filter: { search: 'test+' } });

      // Assert
      expect(result.users).toHaveLength(1);
    });

    it('should handle pagination edge cases', async () => {
      // Arrange
      const user = AdminUser.create({
        id: 'user-1',
        email: 'test@example.com',
        displayName: 'Test User',
        roles: ['user'],
        status: 'active',
      });

      await repository.save(user);

      // Act - Request page beyond total pages
      const result = await repository.list({ pagination: { page: 10, limit: 10 } });

      // Assert
      expect(result.users).toHaveLength(0);
      expect(result.page).toBe(10);
      expect(result.totalPages).toBe(1);
    });

    it('should handle case-insensitive email search', async () => {
      // Arrange
      const user = AdminUser.create({
        id: 'user-1',
        email: 'TestUser@example.com',
        displayName: 'Test User',
        roles: ['user'],
        status: 'active',
      });

      await repository.save(user);

      // Act
      const result = await repository.list({ filter: { search: 'testuser' } });

      // Assert
      expect(result.users).toHaveLength(1);
    });

    it('should handle case-insensitive display name search', async () => {
      // Arrange
      const user = AdminUser.create({
        id: 'user-1',
        email: 'test@example.com',
        displayName: 'Test User',
        roles: ['user'],
        status: 'active',
      });

      await repository.save(user);

      // Act
      const result = await repository.list({ filter: { search: 'TEST USER' } });

      // Assert
      expect(result.users).toHaveLength(1);
    });

    it('should handle empty search string', async () => {
      // Arrange
      const user = AdminUser.create({
        id: 'user-1',
        email: 'test@example.com',
        displayName: 'Test User',
        roles: ['user'],
        status: 'active',
      });

      await repository.save(user);

      // Act
      const result = await repository.list({ filter: { search: '' } });

      // Assert - Should return all users
      expect(result.users).toHaveLength(1);
    });

    it('should handle null/undefined optional fields', async () => {
      // Arrange
      const user = AdminUser.create({
        id: 'user-123',
        email: 'minimal@example.com',
        displayName: 'Minimal User',
        roles: ['user'],
        status: 'active',
      });

      await repository.save(user);

      // Act
      const retrieved = await repository.findById(UserId.fromString('user-123'));

      // Assert
      expect(retrieved).not.toBeNull();
      expect(retrieved!.primaryDriverId).toBeUndefined();
      expect(retrieved!.lastLoginAt).toBeUndefined();
    });

    it('should handle duplicate email prevention', async () => {
      // Arrange
      const user1 = AdminUser.create({
        id: 'user-1',
        email: 'duplicate@example.com',
        displayName: 'User One',
        roles: ['user'],
        status: 'active',
      });

      await repository.save(user1);

      // Act - Try to save another user with same email
      const user2 = AdminUser.create({
        id: 'user-2',
        email: 'duplicate@example.com',
        displayName: 'User Two',
        roles: ['user'],
        status: 'active',
      });

      // This should work at repository level (domain validation happens elsewhere)
      await repository.save(user2);

      // Both should exist (duplicate emails are a domain concern, not repo concern)
      const result = await repository.list();
      expect(result.users).toHaveLength(2);
    });

    it('should handle large number of users', async () => {
      // Arrange - Create 100 users
      const promises = [];
      for (let i = 1; i <= 100; i++) {
        const user = AdminUser.create({
          id: `user-${i}`,
          email: `user${i}@example.com`,
          displayName: `User ${i}`,
          roles: ['user'],
          status: 'active',
        });
        promises.push(repository.save(user));
      }

      await Promise.all(promises);

      // Act
      const result = await repository.list({ pagination: { page: 1, limit: 20 } });

      // Assert
      expect(result.users).toHaveLength(20);
      expect(result.total).toBe(100);
      expect(result.totalPages).toBe(5);
    });

    it('should handle sorting by different fields', async () => {
      // Arrange
      const user1 = AdminUser.create({
        id: 'user-1',
        email: 'b@example.com',
        displayName: 'Zebra',
        roles: ['user'],
        status: 'active',
      });

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

      await repository.save(user1);
      await repository.save(user2);

      // Act - Sort by display name
      const result = await repository.list({ sort: { field: 'displayName', direction: 'asc' } });

      // Assert
      expect(result.users[0]!.displayName).toBe('Alpha');
      expect(result.users[1]!.displayName).toBe('Zebra');
    });

    it('should handle descending sort order', async () => {
      // Arrange
      const user1 = AdminUser.create({
        id: 'user-1',
        email: 'a@example.com',
        displayName: 'Alpha',
        roles: ['user'],
        status: 'active',
      });

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

      await repository.save(user1);
      await repository.save(user2);

      // Act - Sort by email descending
      const result = await repository.list({ sort: { field: 'email', direction: 'desc' } });

      // Assert
      expect(result.users[0]!.email.value).toBe('b@example.com');
      expect(result.users[1]!.email.value).toBe('a@example.com');
    });

    it('should handle count with search', async () => {
      // Arrange
      const user1 = AdminUser.create({
        id: 'user-1',
        email: 'john@example.com',
        displayName: 'John Doe',
        roles: ['user'],
        status: 'active',
      });

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

      await repository.save(user1);
      await repository.save(user2);

      // Act
      const count = await repository.count({ search: 'john' });

      // Assert
      expect(count).toBe(1);
    });

    it('should handle count with multiple filters', async () => {
      // Arrange
      const user1 = AdminUser.create({
        id: 'user-1',
        email: 'admin@example.com',
        displayName: 'Admin User',
        roles: ['admin'],
        status: 'active',
      });

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

      await repository.save(user1);
      await repository.save(user2);

      // Act
      const count = await repository.count({ 
        role: UserRole.fromString('user'), 
        status: UserStatus.fromString('suspended') 
      });

      // Assert
      expect(count).toBe(1);
    });

    it('should handle count with no filters', async () => {
      // Arrange
      for (let i = 1; i <= 5; i++) {
        const user = AdminUser.create({
          id: `user-${i}`,
          email: `user${i}@example.com`,
          displayName: `User ${i}`,
          roles: ['user'],
          status: 'active',
        });
        await repository.save(user);
      }

      // Act
      const count = await repository.count();

      // Assert
      expect(count).toBe(5);
    });

    it('should handle count with empty result', async () => {
      // Act
      const count = await repository.count({ role: UserRole.fromString('admin') });

      // Assert
      expect(count).toBe(0);
    });

    it('should handle list with all optional parameters', async () => {
      // Arrange
      const user = AdminUser.create({
        id: 'user-1',
        email: 'test@example.com',
        displayName: 'Test User',
        roles: ['admin'],
        status: 'active',
      });

      await repository.save(user);

      // Act
      const result = await repository.list({
        filter: {
          role: UserRole.fromString('admin'),
          status: UserStatus.fromString('active'),
          search: 'test',
        },
        pagination: { page: 1, limit: 10 },
        sort: { field: 'email', direction: 'asc' },
      });

      // Assert
      expect(result.users).toHaveLength(1);
      expect(result.users[0]!.id.value).toBe('user-1');
    });

    it('should handle list with no parameters', async () => {
      // Arrange
      const user = AdminUser.create({
        id: 'user-1',
        email: 'test@example.com',
        displayName: 'Test User',
        roles: ['user'],
        status: 'active',
      });

      await repository.save(user);

      // Act
      const result = await repository.list();

      // Assert
      expect(result.users).toHaveLength(1);
      expect(result.total).toBe(1);
    });

    it('should handle count with no parameters', async () => {
      // Arrange
      const user = AdminUser.create({
        id: 'user-1',
        email: 'test@example.com',
        displayName: 'Test User',
        roles: ['user'],
        status: 'active',
      });

      await repository.save(user);

      // Act
      const count = await repository.count();

      // Assert
      expect(count).toBe(1);
    });

    it('should handle list with only search parameter', async () => {
      // Arrange
      const user = AdminUser.create({
        id: 'user-1',
        email: 'search@example.com',
        displayName: 'Search User',
        roles: ['user'],
        status: 'active',
      });

      await repository.save(user);

      // Act
      const result = await repository.list({ filter: { search: 'search' } });

      // Assert
      expect(result.users).toHaveLength(1);
    });

    it('should handle list with only role parameter', async () => {
      // Arrange
      const user = AdminUser.create({
        id: 'user-1',
        email: 'test@example.com',
        displayName: 'Test User',
        roles: ['admin'],
        status: 'active',
      });

      await repository.save(user);

      // Act
      const result = await repository.list({ filter: { role: UserRole.fromString('admin') } });

      // Assert
      expect(result.users).toHaveLength(1);
    });

    it('should handle list with only status parameter', async () => {
      // Arrange
      const user = AdminUser.create({
        id: 'user-1',
        email: 'test@example.com',
        displayName: 'Test User',
        roles: ['user'],
        status: 'suspended',
      });

      await repository.save(user);

      // Act
      const result = await repository.list({ filter: { status: UserStatus.fromString('suspended') } });

      // Assert
      expect(result.users).toHaveLength(1);
    });

    it('should handle list with only pagination parameters', async () => {
      // Arrange
      const user = AdminUser.create({
        id: 'user-1',
        email: 'test@example.com',
        displayName: 'Test User',
        roles: ['user'],
        status: 'active',
      });

      await repository.save(user);

      // Act
      const result = await repository.list({ pagination: { page: 1, limit: 10 } });

      // Assert
      expect(result.users).toHaveLength(1);
      expect(result.page).toBe(1);
      expect(result.limit).toBe(10);
    });

    it('should handle list with only sorting parameters', async () => {
      // Arrange
      const user = AdminUser.create({
        id: 'user-1',
        email: 'test@example.com',
        displayName: 'Test User',
        roles: ['user'],
        status: 'active',
      });

      await repository.save(user);

      // Act
      const result = await repository.list({ sort: { field: 'displayName', direction: 'desc' } });

      // Assert
      expect(result.users).toHaveLength(1);
    });

    it('should handle count with only role parameter', async () => {
      // Arrange
      const user = AdminUser.create({
        id: 'user-1',
        email: 'test@example.com',
        displayName: 'Test User',
        roles: ['admin'],
        status: 'active',
      });

      await repository.save(user);

      // Act
      const count = await repository.count({ role: UserRole.fromString('admin') });

      // Assert
      expect(count).toBe(1);
    });

    it('should handle count with only status parameter', async () => {
      // Arrange
      const user = AdminUser.create({
        id: 'user-1',
        email: 'test@example.com',
        displayName: 'Test User',
        roles: ['user'],
        status: 'suspended',
      });

      await repository.save(user);

      // Act
      const count = await repository.count({ status: UserStatus.fromString('suspended') });

      // Assert
      expect(count).toBe(1);
    });

    it('should handle count with only search parameter', async () => {
      // Arrange
      const user = AdminUser.create({
        id: 'user-1',
        email: 'search@example.com',
        displayName: 'Search User',
        roles: ['user'],
        status: 'active',
      });

      await repository.save(user);

      // Act
      const count = await repository.count({ search: 'search' });

      // Assert
      expect(count).toBe(1);
    });
  });
});