import { describe, it, expect } from 'vitest';
import { CheckoutPrice } from 'apps/companion/main/automation/domain/value-objects/CheckoutPrice';

/**
 * CheckoutPrice Value Object - GREEN PHASE
 *
 * Tests for price validation, parsing, and formatting.
 */

describe('CheckoutPrice Value Object', () => {
  describe('Construction', () => {
    it('should create with valid price $0.50', () => {
      // @ts-expect-error Testing private constructor invariants
      expect(() => new CheckoutPrice(0.50)).not.toThrow();
    });

    it('should create with valid price $10.00', () => {
      // @ts-expect-error Testing private constructor invariants
      expect(() => new CheckoutPrice(10.00)).not.toThrow();
    });

    it('should create with valid price $100.00', () => {
      // @ts-expect-error Testing private constructor invariants
      expect(() => new CheckoutPrice(100.00)).not.toThrow();
    });

    it('should reject negative prices', () => {
      // @ts-expect-error Testing private constructor invariants
      expect(() => new CheckoutPrice(-0.50)).toThrow(/negative/i);
    });

    it('should reject excessive prices over $10,000', () => {
      // @ts-expect-error Testing private constructor invariants
      expect(() => new CheckoutPrice(10000.01)).toThrow(/excessive|maximum/i);
    });

    it('should accept exactly $10,000', () => {
      // @ts-expect-error Testing private constructor invariants
      expect(() => new CheckoutPrice(10000.00)).not.toThrow();
    });

    it('should accept $0.00 (zero price)', () => {
      // @ts-expect-error Testing private constructor invariants
      expect(() => new CheckoutPrice(0.00)).not.toThrow();
    });
  });

  describe('fromString() parsing', () => {
    it('should extract $0.50 from string', () => {
      const price = CheckoutPrice.fromString('$0.50');
      expect(price.getAmount()).toBe(0.50);
    });

    it('should extract $10.00 from string', () => {
      const price = CheckoutPrice.fromString('$10.00');
      expect(price.getAmount()).toBe(10.00);
    });

    it('should extract $100.00 from string', () => {
      const price = CheckoutPrice.fromString('$100.00');
      expect(price.getAmount()).toBe(100.00);
    });

    it('should reject string without dollar sign', () => {
      expect(() => CheckoutPrice.fromString('10.00')).toThrow(/invalid.*format/i);
    });

    it('should reject string with multiple dollar signs', () => {
      expect(() => CheckoutPrice.fromString('$$10.00')).toThrow(/invalid.*format/i);
    });

    it('should reject non-numeric values', () => {
      expect(() => CheckoutPrice.fromString('$abc')).toThrow(/invalid.*format/i);
    });

    it('should reject empty string', () => {
      expect(() => CheckoutPrice.fromString('')).toThrow(/invalid.*format/i);
    });

    it('should handle prices with commas $1,000.00', () => {
      const price = CheckoutPrice.fromString('$1,000.00');
      expect(price.getAmount()).toBe(1000.00);
    });

    it('should handle whitespace around price', () => {
      const price = CheckoutPrice.fromString('  $5.00  ');
      expect(price.getAmount()).toBe(5.00);
    });
  });

  describe('Display formatting', () => {
    it('should format $0.50 as "$0.50"', () => {
      // @ts-expect-error Testing private constructor invariants
      const price = new CheckoutPrice(0.50);
      expect(price.toDisplayString()).toBe('$0.50');
    });

    it('should format $10.00 as "$10.00"', () => {
      // @ts-expect-error Testing private constructor invariants
      const price = new CheckoutPrice(10.00);
      expect(price.toDisplayString()).toBe('$10.00');
    });

    it('should format $100.00 as "$100.00"', () => {
      // @ts-expect-error Testing private constructor invariants
      const price = new CheckoutPrice(100.00);
      expect(price.toDisplayString()).toBe('$100.00');
    });

    it('should always show two decimal places', () => {
      // @ts-expect-error Testing private constructor invariants
      const price = new CheckoutPrice(5);
      expect(price.toDisplayString()).toBe('$5.00');
    });

    it('should round to two decimal places', () => {
      // @ts-expect-error Testing private constructor invariants
      const price = new CheckoutPrice(5.129);
      expect(price.toDisplayString()).toBe('$5.13');
    });
  });

  describe('Zero check', () => {
    it('should detect $0.00 correctly', () => {
      // @ts-expect-error Testing private constructor invariants
      const price = new CheckoutPrice(0.00);
      expect(price.isZero()).toBe(true);
    });

    it('should return false for non-zero prices', () => {
      // @ts-expect-error Testing private constructor invariants
      const price = new CheckoutPrice(0.50);
      expect(price.isZero()).toBe(false);
    });

    it('should handle floating point precision for zero', () => {
      // @ts-expect-error Testing private constructor invariants
      const price = new CheckoutPrice(0.0000001);
      expect(price.isZero()).toBe(true);
    });
  });

  describe('Edge Cases', () => {
    it('should handle very small prices $0.01', () => {
      // @ts-expect-error Testing private constructor invariants
      const price = new CheckoutPrice(0.01);
      expect(price.toDisplayString()).toBe('$0.01');
    });

    it('should handle large prices $9,999.99', () => {
      // @ts-expect-error Testing private constructor invariants
      const price = new CheckoutPrice(9999.99);
      expect(price.toDisplayString()).toBe('$9999.99');
    });

    it('should be immutable after creation', () => {
      // @ts-expect-error Testing private constructor invariants
      const price = new CheckoutPrice(5.00);
      const amount = price.getAmount();
      expect(amount).toBe(5.00);
      // Verify no setters exist
      const mutablePrice = price as unknown as { setAmount?: unknown };
      expect(typeof mutablePrice.setAmount).toBe('undefined');
    });
  });

  describe('BDD Scenarios', () => {
    it('Given price string "$0.50", When parsing, Then amount is 0.50', () => {
      const price = CheckoutPrice.fromString('$0.50');
      expect(price.getAmount()).toBe(0.50);
    });

    it('Given amount 10.00, When formatting, Then display is "$10.00"', () => {
      // @ts-expect-error Testing private constructor invariants
      const price = new CheckoutPrice(10.00);
      expect(price.toDisplayString()).toBe('$10.00');
    });

    it('Given negative amount, When constructing, Then error is thrown', () => {
      // @ts-expect-error Testing private constructor invariants
      expect(() => new CheckoutPrice(-5.00)).toThrow();
    });
  });
});