import { UserEntity } from '@modules/auth/domain/entities/user.entity';
import { type IUserRepository } from '@modules/auth/domain/repositories/user.repository';
import { HashedPassword } from '@modules/auth/domain/value-objects/hashed-password.vo';
import { Phone } from '@modules/auth/domain/value-objects/phone.vo';
import { BanUserCommand } from '../commands/ban-user/ban-user.command';
import { BanUserHandler } from '../commands/ban-user/ban-user.handler';

async function createUser(role = 'BUYER' as any, isActive = true): Promise<UserEntity> {
  const phone = Phone.create('0901234567').unwrap();
  const hash = await HashedPassword.fromPlain('Password123');
  const passwordHash = hash.isOk ? hash.unwrap() : null;
  const user = new UserEntity('user-1', {
    email: null,
    phone,
    passwordHash,
    fullName: 'Test User',
    avatarUrl: null,
    role,
    kycStatus: 'NONE',
    kycData: null,
    isActive,
  });
  return user;
}

describe('BanUserHandler', () => {
  let handler: BanUserHandler;
  let mockUserRepo: { [K in keyof IUserRepository]: ReturnType<typeof vi.fn> };
  let mockEventBus: { publish: ReturnType<typeof vi.fn> };

  beforeEach(() => {
    mockUserRepo = {
      findById: vi.fn(),
      findByPhone: vi.fn(),
      findByEmail: vi.fn(),
      save: vi.fn(),
      update: vi.fn(),
    };

    mockEventBus = { publish: vi.fn() };

    handler = new BanUserHandler(
      mockUserRepo as any,
      mockEventBus as any,
    );
  });

  it('bans an active user successfully', async () => {
    const user = await createUser('BUYER', true);
    mockUserRepo.findById.mockResolvedValue(user);
    mockUserRepo.update.mockResolvedValue(undefined);

    const command = new BanUserCommand('user-1', 'admin-1', 'Spam activity');
    const result = await handler.execute(command);

    expect(result.isActive).toBe(false);
    expect(result.message).toContain('bị ban');
    expect(mockUserRepo.update).toHaveBeenCalledTimes(1);
    expect(mockEventBus.publish).toHaveBeenCalled();
  });

  it('unbans a banned user successfully', async () => {
    const user = await createUser('BUYER', false);
    mockUserRepo.findById.mockResolvedValue(user);
    mockUserRepo.update.mockResolvedValue(undefined);

    const command = new BanUserCommand('user-1', 'admin-1', 'Resolved', true);
    const result = await handler.execute(command);

    expect(result.isActive).toBe(true);
    expect(result.message).toContain('gỡ ban');
  });

  it('throws NotFoundException when user does not exist', async () => {
    mockUserRepo.findById.mockResolvedValue(null);

    const command = new BanUserCommand('nonexistent', 'admin-1', 'test');

    await expect(handler.execute(command)).rejects.toThrow('Người dùng không tồn tại');
  });

  it('prevents banning an admin user', async () => {
    const admin = await createUser('ADMIN', true);
    mockUserRepo.findById.mockResolvedValue(admin);

    const command = new BanUserCommand('user-1', 'admin-1', 'test');

    await expect(handler.execute(command)).rejects.toThrow(/admin/i);
  });

  it('throws when trying to ban already banned user', async () => {
    const user = await createUser('BUYER', false);
    mockUserRepo.findById.mockResolvedValue(user);

    const command = new BanUserCommand('user-1', 'admin-1', 'test');

    await expect(handler.execute(command)).rejects.toThrow('Người dùng đã bị ban');
  });
});
