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 { ApproveKycCommand } from '../commands/approve-kyc/approve-kyc.command';
import { ApproveKycHandler } from '../commands/approve-kyc/approve-kyc.handler';

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

describe('ApproveKycHandler', () => {
  let handler: ApproveKycHandler;
  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 ApproveKycHandler(mockUserRepo as any, mockEventBus as any);
  });

  it('approves pending KYC successfully', async () => {
    const user = await createUser('PENDING');
    mockUserRepo.findById.mockResolvedValue(user);
    mockUserRepo.update.mockResolvedValue(undefined);

    const command = new ApproveKycCommand('user-1', 'admin-1', 'Hồ sơ hợp lệ');
    const result = await handler.execute(command);

    expect(result.kycStatus).toBe('VERIFIED');
    expect(result.message).toContain('duyệt thành công');
    expect(mockUserRepo.update).toHaveBeenCalledTimes(1);
    expect(mockEventBus.publish).toHaveBeenCalled();
  });

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

    const command = new ApproveKycCommand('nonexistent', 'admin-1');
    await expect(handler.execute(command)).rejects.toThrow('Người dùng không tồn tại');
  });

  it('throws when KYC is not pending', async () => {
    const user = await createUser('VERIFIED');
    mockUserRepo.findById.mockResolvedValue(user);

    const command = new ApproveKycCommand('user-1', 'admin-1');
    await expect(handler.execute(command)).rejects.toThrow(/VERIFIED/);
  });
});
