import { WalletAccount } from './wallet-account.aggregate';
import { UserId, Money, Hashpower, WalletStatus } from '@/domain/value-objects';

// Mock exceptions
jest.mock('@/shared/exceptions/domain.exception', () => ({
  DomainError: class DomainError extends Error {
    constructor(message: string) {
      super(message);
      this.name = 'DomainError';
    }
  },
  InsufficientBalanceError: class InsufficientBalanceError extends Error {
    constructor(assetType: string, required: string, available: string) {
      super(`Insufficient ${assetType} balance: required ${required}, available ${available}`);
      this.name = 'InsufficientBalanceError';
    }
  },
  WalletFrozenError: class WalletFrozenError extends Error {
    constructor(walletId: string) {
      super(`Wallet ${walletId} is frozen`);
      this.name = 'WalletFrozenError';
    }
  },
}));

describe('WalletAccount Aggregate', () => {
  let wallet: WalletAccount;

  beforeEach(() => {
    wallet = WalletAccount.createNew('D00000000001', UserId.create(1));
  });

  describe('createNew', () => {
    it('should create a new wallet with zero balances', () => {
      expect(wallet.status).toBe(WalletStatus.ACTIVE);
      expect(wallet.balances.usdt.available.isZero()).toBe(true);
      expect(wallet.hashpower.isZero()).toBe(true);
    });
  });

  describe('deposit', () => {
    it('should increase USDT balance on deposit', () => {
      const amount = Money.USDT(100);
      wallet.deposit(amount, 'KAVA', 'tx_hash_123');

      expect(wallet.balances.usdt.available.value).toBe(100);
      expect(wallet.domainEvents.length).toBe(1);
      expect(wallet.domainEvents[0].eventType).toBe('DepositCompletedEvent');
    });

    it('should throw error when wallet is frozen', () => {
      wallet.freezeWallet();
      const amount = Money.USDT(100);

      expect(() => wallet.deposit(amount, 'KAVA', 'tx_hash_123')).toThrow('Wallet');
    });
  });

  describe('deduct', () => {
    it('should decrease USDT balance on deduct', () => {
      wallet.deposit(Money.USDT(100), 'KAVA', 'tx_hash_123');
      wallet.clearDomainEvents();

      wallet.deduct(Money.USDT(30), 'Plant payment', 'order_123');

      expect(wallet.balances.usdt.available.value).toBe(70);
      expect(wallet.domainEvents.length).toBe(1);
      expect(wallet.domainEvents[0].eventType).toBe('BalanceDeductedEvent');
    });

    it('should throw error when insufficient balance', () => {
      wallet.deposit(Money.USDT(50), 'KAVA', 'tx_hash_123');

      expect(() => wallet.deduct(Money.USDT(100), 'Payment', 'order_123')).toThrow('Insufficient');
    });
  });

  describe('rewards', () => {
    it('should add pending rewards', () => {
      const usdt = Money.USDT(10);
      const hashpower = Hashpower.create(5);
      const expireAt = new Date(Date.now() + 86400000);

      wallet.addPendingReward(usdt, hashpower, expireAt, 'order_123');

      expect(wallet.rewards.pendingUsdt.value).toBe(10);
      expect(wallet.rewards.pendingHashpower.value).toBe(5);
      expect(wallet.rewards.pendingExpireAt).toEqual(expireAt);
    });

    it('should move pending rewards to settleable', () => {
      const usdt = Money.USDT(10);
      const hashpower = Hashpower.create(5);
      const expireAt = new Date(Date.now() + 86400000);

      wallet.addPendingReward(usdt, hashpower, expireAt, 'order_123');
      wallet.clearDomainEvents();
      wallet.movePendingToSettleable();

      expect(wallet.rewards.pendingUsdt.isZero()).toBe(true);
      expect(wallet.rewards.pendingHashpower.isZero()).toBe(true);
      expect(wallet.rewards.settleableUsdt.value).toBe(10);
      expect(wallet.rewards.settleableHashpower.value).toBe(5);
      expect(wallet.hashpower.value).toBe(5);
    });

    it('should throw error when no pending rewards to move', () => {
      expect(() => wallet.movePendingToSettleable()).toThrow('No pending rewards');
    });
  });

  describe('freeze/unfreeze wallet', () => {
    it('should freeze wallet', () => {
      wallet.freezeWallet();
      expect(wallet.status).toBe(WalletStatus.FROZEN);
    });

    it('should unfreeze wallet', () => {
      wallet.freezeWallet();
      wallet.unfreezeWallet();
      expect(wallet.status).toBe(WalletStatus.ACTIVE);
    });

    it('should throw error when freezing already frozen wallet', () => {
      wallet.freezeWallet();
      expect(() => wallet.freezeWallet()).toThrow('already frozen');
    });
  });
});
