import { Money } from './money.vo';
import { DomainError } from '@/shared/exceptions/domain.exception';

export class Balance {
  private readonly _available: Money;
  private readonly _frozen: Money;

  constructor(available: Money, frozen: Money) {
    if (available.currency !== frozen.currency) {
      throw new DomainError('Available and frozen must have same currency');
    }
    this._available = available;
    this._frozen = frozen;
  }

  static create(available: Money, frozen: Money): Balance {
    return new Balance(available, frozen);
  }

  static zero(currency: string = 'USDT'): Balance {
    return new Balance(Money.zero(currency), Money.zero(currency));
  }

  get available(): Money {
    return this._available;
  }

  get frozen(): Money {
    return this._frozen;
  }

  get total(): Money {
    return this._available.add(this._frozen);
  }

  get currency(): string {
    return this._available.currency;
  }

  add(amount: Money): Balance {
    return new Balance(this._available.add(amount), this._frozen);
  }

  deduct(amount: Money): Balance {
    if (this._available.lessThan(amount)) {
      throw new DomainError('Insufficient available balance');
    }
    return new Balance(this._available.subtract(amount), this._frozen);
  }

  freeze(amount: Money): Balance {
    if (this._available.lessThan(amount)) {
      throw new DomainError('Insufficient available balance to freeze');
    }
    return new Balance(
      this._available.subtract(amount),
      this._frozen.add(amount),
    );
  }

  unfreeze(amount: Money): Balance {
    if (this._frozen.lessThan(amount)) {
      throw new DomainError('Insufficient frozen balance to unfreeze');
    }
    return new Balance(
      this._available.add(amount),
      this._frozen.subtract(amount),
    );
  }

  deductFrozen(amount: Money): Balance {
    if (this._frozen.lessThan(amount)) {
      throw new DomainError('Insufficient frozen balance to deduct');
    }
    return new Balance(this._available, this._frozen.subtract(amount));
  }
}
