/**
 * Money Value Object Unit Tests
 */

import { Money } from './money.vo';

describe('Money Value Object', () => {
  describe('create', () => {
    it('should create money with valid amount and currency', () => {
      const money = Money.create(100, 'USD');
      expect(money.getAmount()).toBe(100);
      expect(money.getCurrency()).toBe('USD');
    });

    it('should round to 2 decimal places', () => {
      const money = Money.create(100.999, 'USD');
      expect(money.getAmount()).toBe(101);
    });

    it('should throw error for negative amount', () => {
      expect(() => Money.create(-100, 'USD')).toThrow('Amount cannot be negative');
    });

    it('should throw error for invalid currency', () => {
      expect(() => Money.create(100, 'XXX')).toThrow('Invalid currency code');
    });

    it('should normalize currency to uppercase', () => {
      const money = Money.create(100, 'usd');
      expect(money.getCurrency()).toBe('USD');
    });
  });

  describe('zero', () => {
    it('should create zero amount', () => {
      const money = Money.zero('USD');
      expect(money.getAmount()).toBe(0);
      expect(money.isZero()).toBe(true);
    });
  });

  describe('add', () => {
    it('should add two money amounts', () => {
      const money1 = Money.create(100, 'USD');
      const money2 = Money.create(50, 'USD');
      const result = money1.add(money2);
      expect(result.getAmount()).toBe(150);
    });

    it('should throw error for currency mismatch', () => {
      const money1 = Money.create(100, 'USD');
      const money2 = Money.create(50, 'EUR');
      expect(() => money1.add(money2)).toThrow('Currency mismatch');
    });
  });

  describe('subtract', () => {
    it('should subtract two money amounts', () => {
      const money1 = Money.create(100, 'USD');
      const money2 = Money.create(30, 'USD');
      const result = money1.subtract(money2);
      expect(result.getAmount()).toBe(70);
    });

    it('should throw error for negative result', () => {
      const money1 = Money.create(50, 'USD');
      const money2 = Money.create(100, 'USD');
      expect(() => money1.subtract(money2)).toThrow('negative amount');
    });
  });

  describe('multiply', () => {
    it('should multiply money amount', () => {
      const money = Money.create(100, 'USD');
      const result = money.multiply(2);
      expect(result.getAmount()).toBe(200);
    });

    it('should throw error for negative multiplier', () => {
      const money = Money.create(100, 'USD');
      expect(() => money.multiply(-2)).toThrow('Multiplier cannot be negative');
    });
  });

  describe('divide', () => {
    it('should divide money amount', () => {
      const money = Money.create(100, 'USD');
      const result = money.divide(2);
      expect(result.getAmount()).toBe(50);
    });

    it('should throw error for zero divisor', () => {
      const money = Money.create(100, 'USD');
      expect(() => money.divide(0)).toThrow('Divisor must be positive');
    });
  });

  describe('comparisons', () => {
    it('should compare greater than', () => {
      const money1 = Money.create(100, 'USD');
      const money2 = Money.create(50, 'USD');
      expect(money1.isGreaterThan(money2)).toBe(true);
      expect(money2.isGreaterThan(money1)).toBe(false);
    });

    it('should compare less than', () => {
      const money1 = Money.create(50, 'USD');
      const money2 = Money.create(100, 'USD');
      expect(money1.isLessThan(money2)).toBe(true);
      expect(money2.isLessThan(money1)).toBe(false);
    });

    it('should compare equality', () => {
      const money1 = Money.create(100, 'USD');
      const money2 = Money.create(100, 'USD');
      const money3 = Money.create(50, 'USD');
      expect(money1.isEqualTo(money2)).toBe(true);
      expect(money1.isEqualTo(money3)).toBe(false);
    });
  });

  describe('format', () => {
    it('should format USD with $ symbol', () => {
      const money = Money.create(100.5, 'USD');
      expect(money.format()).toBe('$100.50');
    });

    it('should format EUR with € symbol', () => {
      const money = Money.create(100.5, 'EUR');
      expect(money.format()).toBe('€100.50');
    });
  });
});
