import { Result, ValueObject } from '@modules/shared';

interface AddressProps {
  address: string;
  ward: string;
  district: string;
  city: string;
}

export class Address extends ValueObject<AddressProps> {
  get address(): string { return this.props.address; }
  get ward(): string { return this.props.ward; }
  get district(): string { return this.props.district; }
  get city(): string { return this.props.city; }

  get fullAddress(): string {
    return `${this.props.address}, ${this.props.ward}, ${this.props.district}, ${this.props.city}`;
  }

  /**
   * Vietnamese-aware normalized form used for duplicate detection (TEC-2932).
   *
   * Rules:
   *   1. Concatenate address, ward, district, city (comma-separated).
   *   2. Lowercase.
   *   3. Strip Vietnamese diacritics (NFD decomposition + remove combining marks,
   *      plus the special-case `đ → d` / `Đ → d`).
   *   4. Replace any run of non-alphanumeric characters with a single space.
   *   5. Trim and collapse repeated whitespace.
   *
   * The output is stable, lowercase, ASCII-only, and safe to compare with
   * `=` against the `Property.addressNormalized` column. The migration
   * backfill expression is the SQL equivalent of this rule.
   */
  get normalized(): string {
    return Address.normalize(this.fullAddress);
  }

  /**
   * Normalize an arbitrary Vietnamese address string the same way
   * {@link Address#normalized} does. Exposed as a static so call sites that
   * only have a raw string (e.g. admin lookups) can produce a key without
   * constructing a full Address aggregate.
   */
  static normalize(input: string): string {
    if (!input) return '';
    const lower = input.toLowerCase();
    // Strip diacritics: decompose to NFD then drop combining marks.
    const stripped = lower
      .normalize('NFD')
      .replace(/[\u0300-\u036f]/g, '')
      // `đ` and `Đ` do not decompose to `d` under NFD — handle explicitly.
      .replace(/đ/g, 'd');
    // Replace any non-alphanumeric run with a single space, then collapse.
    return stripped
      .replace(/[^a-z0-9]+/g, ' ')
      .replace(/\s+/g, ' ')
      .trim();
  }

  static create(address: string, ward: string, district: string, city: string): Result<Address, string> {
    if (!address?.trim()) return Result.err('Địa chỉ không được để trống');
    if (!ward?.trim()) return Result.err('Phường/xã không được để trống');
    if (!district?.trim()) return Result.err('Quận/huyện không được để trống');
    if (!city?.trim()) return Result.err('Thành phố không được để trống');

    return Result.ok(new Address({
      address: address.trim(),
      ward: ward.trim(),
      district: district.trim(),
      city: city.trim(),
    }));
  }
}
