import { LeaderboardType } from './leaderboard-type.enum';

/**
 * 龙虎榜周期值对象
 *
 * 表示一个榜单的时间周期
 */
export class LeaderboardPeriod {
  private constructor(
    public readonly type: LeaderboardType,
    public readonly key: string,           // 2024-01-15 / 2024-W03 / 2024-01
    public readonly startAt: Date,
    public readonly endAt: Date,
  ) {}

  /**
   * 创建当前日榜周期
   */
  static currentDaily(): LeaderboardPeriod {
    const now = new Date();
    const startAt = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0);
    const endAt = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59, 999);
    const key = this.formatDate(now);

    return new LeaderboardPeriod(LeaderboardType.DAILY, key, startAt, endAt);
  }

  /**
   * 创建当前周榜周期
   */
  static currentWeekly(): LeaderboardPeriod {
    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);

    const weekNumber = this.getWeekNumber(now);
    const key = `${now.getFullYear()}-W${weekNumber.toString().padStart(2, '0')}`;

    return new LeaderboardPeriod(LeaderboardType.WEEKLY, key, monday, sunday);
  }

  /**
   * 创建当前月榜周期
   */
  static currentMonthly(): LeaderboardPeriod {
    const now = new Date();
    const startAt = new Date(now.getFullYear(), now.getMonth(), 1, 0, 0, 0);
    const endAt = new Date(now.getFullYear(), now.getMonth() + 1, 0, 23, 59, 59, 999);
    const key = `${now.getFullYear()}-${(now.getMonth() + 1).toString().padStart(2, '0')}`;

    return new LeaderboardPeriod(LeaderboardType.MONTHLY, key, startAt, endAt);
  }

  /**
   * 根据类型创建当前周期
   */
  static current(type: LeaderboardType): LeaderboardPeriod {
    switch (type) {
      case LeaderboardType.DAILY:
        return this.currentDaily();
      case LeaderboardType.WEEKLY:
        return this.currentWeekly();
      case LeaderboardType.MONTHLY:
        return this.currentMonthly();
    }
  }

  /**
   * 从已有数据重建周期
   */
  static fromData(
    type: LeaderboardType,
    key: string,
    startAt: Date,
    endAt: Date,
  ): LeaderboardPeriod {
    return new LeaderboardPeriod(type, key, startAt, endAt);
  }

  /**
   * 检查是否在当前周期内
   */
  isCurrentPeriod(): boolean {
    const now = new Date();
    return now >= this.startAt && now <= this.endAt;
  }

  /**
   * 获取上一个周期
   */
  getPreviousPeriod(): LeaderboardPeriod {
    switch (this.type) {
      case LeaderboardType.DAILY: {
        const prevDay = new Date(this.startAt);
        prevDay.setDate(prevDay.getDate() - 1);
        const startAt = new Date(prevDay.getFullYear(), prevDay.getMonth(), prevDay.getDate(), 0, 0, 0);
        const endAt = new Date(prevDay.getFullYear(), prevDay.getMonth(), prevDay.getDate(), 23, 59, 59, 999);
        const key = LeaderboardPeriod.formatDate(prevDay);
        return new LeaderboardPeriod(LeaderboardType.DAILY, key, startAt, endAt);
      }
      case LeaderboardType.WEEKLY: {
        const prevMonday = new Date(this.startAt);
        prevMonday.setDate(prevMonday.getDate() - 7);
        const prevSunday = new Date(prevMonday);
        prevSunday.setDate(prevMonday.getDate() + 6);
        prevSunday.setHours(23, 59, 59, 999);
        const weekNumber = LeaderboardPeriod.getWeekNumber(prevMonday);
        const key = `${prevMonday.getFullYear()}-W${weekNumber.toString().padStart(2, '0')}`;
        return new LeaderboardPeriod(LeaderboardType.WEEKLY, key, prevMonday, prevSunday);
      }
      case LeaderboardType.MONTHLY: {
        const prevMonth = new Date(this.startAt);
        prevMonth.setMonth(prevMonth.getMonth() - 1);
        const startAt = new Date(prevMonth.getFullYear(), prevMonth.getMonth(), 1, 0, 0, 0);
        const endAt = new Date(prevMonth.getFullYear(), prevMonth.getMonth() + 1, 0, 23, 59, 59, 999);
        const key = `${prevMonth.getFullYear()}-${(prevMonth.getMonth() + 1).toString().padStart(2, '0')}`;
        return new LeaderboardPeriod(LeaderboardType.MONTHLY, key, startAt, endAt);
      }
    }
  }

  private static formatDate(date: Date): string {
    return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`;
  }

  private static 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);
  }

  equals(other: LeaderboardPeriod): boolean {
    return this.type === other.type && this.key === other.key;
  }

  toString(): string {
    return `${this.type}:${this.key}`;
  }
}
