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

describe('PropertyEntity', () => {
  const makeAddress = () =>
    Address.create('123 Nguyễn Huệ', 'Bến Nghé', 'Quận 1', 'Hồ Chí Minh').unwrap();
  const makeGeoPoint = () => GeoPoint.create(10.7769, 106.7009).unwrap();

  it('creates property with all required fields', () => {
    const property = PropertyEntity.createNew('prop-1', {
      propertyType: 'TOWNHOUSE',
      title: 'Nhà phố đẹp Quận 1',
      description: 'Nhà phố 3 tầng, mặt tiền rộng',
      address: makeAddress(),
      location: makeGeoPoint(),
      areaM2: 120,
      usableAreaM2: 100,
      bedrooms: 3,
      bathrooms: 2,
      floors: 3,
      floor: null,
      totalFloors: null,
      direction: 'EAST',
      yearBuilt: 2020,
      legalStatus: 'Sổ hồng',
      amenities: { parking: true },
      nearbyPOIs: [],
      metroDistanceM: 500,
      projectName: null,
    });

    expect(property.id).toBe('prop-1');
    expect(property.propertyType).toBe('TOWNHOUSE');
    expect(property.title).toBe('Nhà phố đẹp Quận 1');
    expect(property.areaM2).toBe(120);
    expect(property.usableAreaM2).toBe(100);
    expect(property.bedrooms).toBe(3);
    expect(property.bathrooms).toBe(2);
    expect(property.floors).toBe(3);
    expect(property.direction).toBe('EAST');
    expect(property.yearBuilt).toBe(2020);
    expect(property.legalStatus).toBe('Sổ hồng');
    expect(property.metroDistanceM).toBe(500);
    expect(property.projectName).toBeNull();
  });

  it('creates property with nullable fields as null', () => {
    const property = PropertyEntity.createNew('prop-2', {
      propertyType: 'APARTMENT',
      title: 'Căn hộ Thủ Đức',
      description: 'Căn hộ 2PN',
      address: makeAddress(),
      location: makeGeoPoint(),
      areaM2: 65,
      usableAreaM2: null,
      bedrooms: null,
      bathrooms: null,
      floors: null,
      floor: 10,
      totalFloors: 25,
      direction: null,
      yearBuilt: null,
      legalStatus: null,
      amenities: null,
      nearbyPOIs: null,
      metroDistanceM: null,
      projectName: 'Vinhomes Grand Park',
    });

    expect(property.usableAreaM2).toBeNull();
    expect(property.bedrooms).toBeNull();
    expect(property.direction).toBeNull();
    expect(property.floor).toBe(10);
    expect(property.totalFloors).toBe(25);
    expect(property.projectName).toBe('Vinhomes Grand Park');
  });

  describe('updateContent', () => {
    const makeProperty = () =>
      PropertyEntity.createNew('prop-1', {
        propertyType: 'TOWNHOUSE',
        title: 'Nhà phố đẹp Quận 1',
        description: 'Nhà phố 3 tầng, mặt tiền rộng',
        address: makeAddress(),
        location: makeGeoPoint(),
        areaM2: 120,
        usableAreaM2: 100,
        bedrooms: 3,
        bathrooms: 2,
        floors: 3,
        floor: null,
        totalFloors: null,
        direction: 'EAST',
        yearBuilt: 2020,
        legalStatus: 'Sổ hồng',
        amenities: { parking: true },
        nearbyPOIs: [],
        metroDistanceM: 500,
        projectName: null,
      });

    it('updates title', () => {
      const property = makeProperty();
      const fields = property.updateContent({ title: 'Tiêu đề mới' });
      expect(fields).toContain('title');
      expect(property.title).toBe('Tiêu đề mới');
    });

    it('updates description', () => {
      const property = makeProperty();
      const fields = property.updateContent({ description: 'Mô tả mới chi tiết hơn' });
      expect(fields).toContain('description');
      expect(property.description).toBe('Mô tả mới chi tiết hơn');
    });

    it('updates amenities', () => {
      const property = makeProperty();
      const fields = property.updateContent({ amenities: ['Hồ bơi', 'Gym'] });
      expect(fields).toContain('amenities');
      expect(property.amenities).toEqual(['Hồ bơi', 'Gym']);
    });

    it('returns empty array when no fields provided', () => {
      const property = makeProperty();
      const fields = property.updateContent({});
      expect(fields).toEqual([]);
    });

    it('updates multiple fields at once', () => {
      const property = makeProperty();
      const fields = property.updateContent({
        title: 'Tiêu đề mới',
        description: 'Mô tả mới',
        amenities: ['Gym'],
      });
      expect(fields).toEqual(['title', 'description', 'amenities']);
    });
  });
});

describe('PropertyMediaEntity', () => {
  it('creates media with all fields', () => {
    const media = PropertyMediaEntity.createNew(
      'media-1',
      'prop-1',
      'https://cdn.example.com/photo.jpg',
      'image',
      0,
      'Mặt tiền nhà',
    );

    expect(media.id).toBe('media-1');
    expect(media.propertyId).toBe('prop-1');
    expect(media.url).toBe('https://cdn.example.com/photo.jpg');
    expect(media.type).toBe('image');
    expect(media.order).toBe(0);
    expect(media.caption).toBe('Mặt tiền nhà');
    expect(media.aiTags).toBeNull();
  });

  it('creates media without caption', () => {
    const media = PropertyMediaEntity.createNew(
      'media-2',
      'prop-1',
      'https://cdn.example.com/video.mp4',
      'video',
      1,
    );

    expect(media.type).toBe('video');
    expect(media.caption).toBeNull();
  });
});

describe('Listings Value Objects', () => {
  describe('Price', () => {
    it('creates 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('rejects zero price', () => {
      const result = Price.create(0n);
      expect(result.isErr).toBe(true);
    });

    it('rejects negative price', () => {
      const result = Price.create(-1_000_000n);
      expect(result.isErr).toBe(true);
    });

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

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

    it('returns 0 for negative area', () => {
      const price = Price.create(5_000_000_000n).unwrap();
      expect(price.calculatePerM2(-10)).toBe(0);
    });
  });

  describe('GeoPoint', () => {
    it('creates 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('rejects invalid latitude above 90', () => {
      const result = GeoPoint.create(91, 106);
      expect(result.isErr).toBe(true);
    });

    it('rejects invalid latitude below -90', () => {
      const result = GeoPoint.create(-91, 106);
      expect(result.isErr).toBe(true);
    });

    it('rejects invalid longitude above 180', () => {
      const result = GeoPoint.create(10, 181);
      expect(result.isErr).toBe(true);
    });

    it('rejects invalid longitude below -180', () => {
      const result = GeoPoint.create(10, -181);
      expect(result.isErr).toBe(true);
    });

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

    it('accepts boundary values', () => {
      expect(GeoPoint.create(90, 180).isOk).toBe(true);
      expect(GeoPoint.create(-90, -180).isOk).toBe(true);
    });
  });

  describe('Address', () => {
    it('creates valid address', () => {
      const result = Address.create('123 Nguyễn Huệ', 'Bến Nghé', 'Quận 1', 'Hồ Chí Minh');
      expect(result.isOk).toBe(true);
      const addr = result.unwrap();
      expect(addr.address).toBe('123 Nguyễn Huệ');
      expect(addr.ward).toBe('Bến Nghé');
      expect(addr.district).toBe('Quận 1');
      expect(addr.city).toBe('Hồ Chí Minh');
    });

    it('generates full address string', () => {
      const addr = Address.create('123 Nguyễn Huệ', 'Bến Nghé', 'Quận 1', 'Hồ Chí Minh').unwrap();
      expect(addr.fullAddress).toBe('123 Nguyễn Huệ, Bến Nghé, Quận 1, Hồ Chí Minh');
    });

    it('trims whitespace', () => {
      const addr = Address.create('  123 Lê Lợi  ', '  Bến Thành  ', '  Quận 1  ', '  HCM  ').unwrap();
      expect(addr.address).toBe('123 Lê Lợi');
      expect(addr.ward).toBe('Bến Thành');
    });

    it('rejects empty address', () => {
      expect(Address.create('', 'Bến Nghé', 'Quận 1', 'HCM').isErr).toBe(true);
    });

    it('rejects empty ward', () => {
      expect(Address.create('123', '', 'Quận 1', 'HCM').isErr).toBe(true);
    });

    it('rejects empty district', () => {
      expect(Address.create('123', 'Bến Nghé', '', 'HCM').isErr).toBe(true);
    });

    it('rejects empty city', () => {
      expect(Address.create('123', 'Bến Nghé', 'Quận 1', '').isErr).toBe(true);
    });

    it('rejects whitespace-only values', () => {
      expect(Address.create('   ', 'Bến Nghé', 'Quận 1', 'HCM').isErr).toBe(true);
    });
  });
});
