/**
 * DateRange Value Object
 *
 * Encapsulates ETD/ETA date range with validation
 *
 * Business Rules:
 * - End date must be after start date
 * - Dates cannot be in the past (for new shipments)
 * - Date range is immutable
 */

export class DateRange {
  private readonly startDate: Date;
  private readonly endDate: Date;

  private constructor(startDate: Date, endDate: Date) {
    this.startDate = startDate;
    this.endDate = endDate;
  }

  static create(startDate: Date, endDate: Date, allowPastDates = false): DateRange {
    if (!startDate || !endDate) {
      throw new Error('Start date and end date are required.');
    }

    if (endDate <= startDate) {
      throw new Error('End date must be after start date.');
    }

    if (!allowPastDates) {
      const now = new Date();
      now.setHours(0, 0, 0, 0); // Reset time to start of day

      if (startDate < now) {
        throw new Error('Start date cannot be in the past.');
      }
    }

    return new DateRange(new Date(startDate), new Date(endDate));
  }

  /**
   * Create from ETD and transit days
   */
  static fromTransitDays(etd: Date, transitDays: number): DateRange {
    if (transitDays <= 0) {
      throw new Error('Transit days must be positive.');
    }

    const eta = new Date(etd);
    eta.setDate(eta.getDate() + transitDays);

    return DateRange.create(etd, eta, true);
  }

  getStartDate(): Date {
    return new Date(this.startDate);
  }

  getEndDate(): Date {
    return new Date(this.endDate);
  }

  getDurationInDays(): number {
    const diffTime = this.endDate.getTime() - this.startDate.getTime();
    return Math.ceil(diffTime / (1000 * 60 * 60 * 24));
  }

  getDurationInHours(): number {
    const diffTime = this.endDate.getTime() - this.startDate.getTime();
    return Math.ceil(diffTime / (1000 * 60 * 60));
  }

  contains(date: Date): boolean {
    return date >= this.startDate && date <= this.endDate;
  }

  overlaps(other: DateRange): boolean {
    return this.startDate <= other.endDate && this.endDate >= other.startDate;
  }

  isFutureRange(): boolean {
    const now = new Date();
    return this.startDate > now;
  }

  isPastRange(): boolean {
    const now = new Date();
    return this.endDate < now;
  }

  isCurrentRange(): boolean {
    const now = new Date();
    return this.contains(now);
  }

  equals(other: DateRange): boolean {
    return (
      this.startDate.getTime() === other.startDate.getTime() &&
      this.endDate.getTime() === other.endDate.getTime()
    );
  }

  toString(): string {
    return `${this.formatDate(this.startDate)} - ${this.formatDate(this.endDate)}`;
  }

  private formatDate(date: Date): string {
    return date.toISOString().split('T')[0];
  }

  toObject(): { startDate: Date; endDate: Date } {
    return {
      startDate: new Date(this.startDate),
      endDate: new Date(this.endDate),
    };
  }
}
