import { describe, it, expect } from 'vitest';
import { Address } from '../value-objects/address.vo';
import { GeoPoint } from '../value-objects/geo-point.vo';
import { Price } from '../value-objects/price.vo';

describe('Address', () => {
  it('should create a valid address', () => {
    const result = Address.create('123 Lê Lợi', 'Phường Bến Nghé', 'Quận 1', 'TP.HCM');
    expect(result.isOk).toBe(true);
    const addr = result.unwrap();
    expect(addr.fullAddress).toBe('123 Lê Lợi, Phường Bến Nghé, Quận 1, TP.HCM');
  });

  it('should reject empty address', () => {
    const result = Address.create('', 'Ward', 'District', 'City');
    expect(result.isErr).toBe(true);
  });

  it('should reject empty ward', () => {
    const result = Address.create('123 St', '', 'District', 'City');
    expect(result.isErr).toBe(true);
  });

  describe('normalize (TEC-2932)', () => {
    it('strips Vietnamese diacritics', () => {
      expect(Address.normalize('Căn hộ Quận 1')).toBe('can ho quan 1');
    });

    it('special-cases đ → d (which NFD does not decompose)', () => {
      expect(Address.normalize('Phường Bến Nghé, Đường Đồng Khởi')).toBe(
        'phuong ben nghe duong dong khoi',
      );
    });

    it('lowercases and collapses non-alphanumeric runs', () => {
      expect(Address.normalize('123 Lê Lợi --  /  Phường  Bến Nghé')).toBe(
        '123 le loi phuong ben nghe',
      );
    });

    it('returns the same key for equivalent addresses with different casing/punctuation', () => {
      const a = Address.normalize('123 Lê Lợi, Phường Bến Nghé, Quận 1, TP.HCM');
      const b = Address.normalize('123  LÊ  LỢI - Phường Bến Nghé, Quận 1 (TP.HCM)');
      expect(a).toBe(b);
    });

    it('returns empty string for empty input', () => {
      expect(Address.normalize('')).toBe('');
    });

    it('Address.normalized matches static normalize on fullAddress', () => {
      const addr = Address.create('123 Lê Lợi', 'Phường Bến Nghé', 'Quận 1', 'TP.HCM').unwrap();
      expect(addr.normalized).toBe(Address.normalize(addr.fullAddress));
    });
  });
});

describe('GeoPoint', () => {
  it('should create a valid geo point', () => {
    const result = GeoPoint.create(10.7769, 106.7009);
    expect(result.isOk).toBe(true);
    const point = result.unwrap();
    expect(point.latitude).toBe(10.7769);
    expect(point.longitude).toBe(106.7009);
  });

  it('should generate WKT', () => {
    const point = GeoPoint.create(10.7769, 106.7009).unwrap();
    expect(point.toWKT()).toBe('POINT(106.7009 10.7769)');
  });

  it('should reject invalid latitude', () => {
    expect(GeoPoint.create(91, 0).isErr).toBe(true);
    expect(GeoPoint.create(-91, 0).isErr).toBe(true);
  });

  it('should reject invalid longitude', () => {
    expect(GeoPoint.create(0, 181).isErr).toBe(true);
    expect(GeoPoint.create(0, -181).isErr).toBe(true);
  });
});

describe('Price', () => {
  it('should create a valid price', () => {
    const result = Price.create(5_000_000_000n);
    expect(result.isOk).toBe(true);
    expect(result.unwrap().amountVND).toBe(5_000_000_000n);
  });

  it('should reject zero or negative price', () => {
    expect(Price.create(0n).isErr).toBe(true);
    expect(Price.create(-1n).isErr).toBe(true);
  });

  it('should calculate price per m2', () => {
    const price = Price.create(5_000_000_000n).unwrap();
    expect(price.calculatePerM2(100)).toBe(50_000_000);
  });

  it('should return 0 for zero area', () => {
    const price = Price.create(5_000_000_000n).unwrap();
    expect(price.calculatePerM2(0)).toBe(0);
  });
});
