export class EthioDate {
  private MILLISECOND = 1;
  private SECOND = 1000 * this.MILLISECOND;
  private MINUTE = 60 * this.SECOND;
  private HOUR = 60 * this.MINUTE;
  private DAY = 24 * this.HOUR;
  private WEEK = 7 * this.DAY;
  private MONTH = 30 * this.DAY;
  private YEAR = 365 * this.DAY;
  private LEAP_YEAR = this.YEAR + this.DAY;
  private LEAPED_YEAR = this.YEAR * 3 + this.LEAP_YEAR;

  public static fromDate(date: Date): EthioDateTime {
    const ethioDate = new EthioDate();
    return ethioDate.fromGregorian(date);
  }

  public static toDate(date: EthioDateTime): Date {
    const ethioDate = new EthioDate();
    return ethioDate.toGregorian(date);
  }

  public fromGregorian(date: Date): EthioDateTime {
    // Calculate base difference in milliseconds from reference date
    const baseDate = new Date(1911, 9, 11, 6, 0, 0);
    let remainingTime = date.getTime() - baseDate.getTime();
    console.log("date.getFullYear()", date.getFullYear());

    // Calculate years and remaining time
    const leapedYears = Math.floor(remainingTime / this.LEAPED_YEAR);
    remainingTime = remainingTime - this.LEAPED_YEAR * leapedYears;

    const normalYears = Math.floor(remainingTime / this.YEAR);
    remainingTime = remainingTime - this.YEAR * normalYears;
    console.log("normalYears", normalYears);

    const months = Math.floor(remainingTime / this.MONTH);
    remainingTime = remainingTime - this.MONTH * months;

    const days = Math.floor(remainingTime / this.DAY);
    remainingTime = remainingTime - this.DAY * days;

    const hours = Math.floor(remainingTime / this.HOUR);
    remainingTime = remainingTime - this.HOUR * hours;

    const minutes = Math.floor(remainingTime / this.MINUTE);
    remainingTime = remainingTime - this.MINUTE * minutes;

    const seconds = Math.floor(remainingTime / this.SECOND);
    remainingTime = remainingTime - this.SECOND * seconds;

    const year = 1904 + leapedYears * 4 + normalYears;
    const month = days > 0 ? months + 1 : months;
    const day = hours >= 6 ? days + 1 : days;
    const hour = this.getHour(date.getHours());
    const minute = date.getMinutes();
    const second = date.getSeconds();

    return { year, month, day, hour, minute, second };
  }

  public toGregorian(data: EthioDateTime): Date {
    // Calculate time components in a single pass
    const total =
      Math.floor((data.year - 1904) / 4) * this.LEAPED_YEAR + // Leap years
      (data.year - 1904 - Math.floor((data.year - 1904) / 4)) * this.YEAR + // Normal years
      data.month * this.MONTH + // Months
      data.day * this.DAY + // Days
      data.hour * this.HOUR + // Hours
      data.minute * this.MINUTE + // Minutes
      data.second * this.SECOND; // Seconds

    return new Date(new Date(1911, 9, 12).getTime() + total);
  }

  private getHour(hr: number): number {
    if (hr < 0 || hr > 24) {
      throw new Error("Hour must be between 0 and 24");
    }
    return hr <= 6 ? hr + 6 : hr <= 18 ? hr - 6 : hr - 18;
  }
}

export type EthioDateTime = {
  year: number;
  month: number;
  day: number;
  hour: number;
  minute: number;
  second: number;
};
