import { ReportPeriod } from './report-period.enum';

export class DateRange {
  private constructor(
    public readonly startDate: Date,
    public readonly endDate: Date,
  ) {
    if (startDate > endDate) {
      throw new Error('开始日期不能大于结束日期');
    }
  }

  static create(startDate: Date, endDate: Date): DateRange {
    return new DateRange(startDate, endDate);
  }

  static today(): DateRange {
    const now = new Date();
    const start = new Date(
      now.getFullYear(),
      now.getMonth(),
      now.getDate(),
      0,
      0,
      0,
    );
    const end = new Date(
      now.getFullYear(),
      now.getMonth(),
      now.getDate(),
      23,
      59,
      59,
      999,
    );
    return new DateRange(start, end);
  }

  static thisWeek(): DateRange {
    const now = new Date();
    const dayOfWeek = now.getDay();
    const diffToMonday = dayOfWeek === 0 ? -6 : 1 - dayOfWeek;

    const monday = new Date(now);
    monday.setDate(now.getDate() + diffToMonday);
    monday.setHours(0, 0, 0, 0);

    const sunday = new Date(monday);
    sunday.setDate(monday.getDate() + 6);
    sunday.setHours(23, 59, 59, 999);

    return new DateRange(monday, sunday);
  }

  static thisMonth(): DateRange {
    const now = new Date();
    const start = new Date(now.getFullYear(), now.getMonth(), 1, 0, 0, 0);
    const end = new Date(
      now.getFullYear(),
      now.getMonth() + 1,
      0,
      23,
      59,
      59,
      999,
    );
    return new DateRange(start, end);
  }

  static thisQuarter(): DateRange {
    const now = new Date();
    const quarter = Math.floor(now.getMonth() / 3);
    const start = new Date(now.getFullYear(), quarter * 3, 1, 0, 0, 0);
    const end = new Date(
      now.getFullYear(),
      quarter * 3 + 3,
      0,
      23,
      59,
      59,
      999,
    );
    return new DateRange(start, end);
  }

  static thisYear(): DateRange {
    const now = new Date();
    const start = new Date(now.getFullYear(), 0, 1, 0, 0, 0);
    const end = new Date(now.getFullYear(), 11, 31, 23, 59, 59, 999);
    return new DateRange(start, end);
  }

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

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

  toPeriodKey(period: ReportPeriod): string {
    const year = this.startDate.getFullYear();
    const month = (this.startDate.getMonth() + 1).toString().padStart(2, '0');
    const day = this.startDate.getDate().toString().padStart(2, '0');

    switch (period) {
      case ReportPeriod.DAILY:
        return `${year}-${month}-${day}`;
      case ReportPeriod.WEEKLY:
        const weekNumber = this.getWeekNumber(this.startDate);
        return `${year}-W${weekNumber.toString().padStart(2, '0')}`;
      case ReportPeriod.MONTHLY:
        return `${year}-${month}`;
      case ReportPeriod.QUARTERLY:
        const quarter = Math.floor(this.startDate.getMonth() / 3) + 1;
        return `${year}-Q${quarter}`;
      case ReportPeriod.YEARLY:
        return `${year}`;
      default:
        return `${year}-${month}-${day}_to_${this.endDate.getFullYear()}-${(this.endDate.getMonth() + 1).toString().padStart(2, '0')}-${this.endDate.getDate().toString().padStart(2, '0')}`;
    }
  }

  private getWeekNumber(date: Date): number {
    const firstDayOfYear = new Date(date.getFullYear(), 0, 1);
    const pastDaysOfYear =
      (date.getTime() - firstDayOfYear.getTime()) / 86400000;
    return Math.ceil((pastDaysOfYear + firstDayOfYear.getDay() + 1) / 7);
  }
}
