import { Injectable } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class DateAdapter {
  getDatesAndCalendarWeeksForMonth(year: number, month: number) {
    const result: { week: number; dates: Date[] }[] = [];
    const weeks = this.getCalendarWeeksForMonth(year, month);

    for (const week of weeks) {
      let iWeek = week;
      if (month === 12 && week === 1) {
        iWeek = 53;
      }

      const dates = this.getDatesForCalendarWeek(year, iWeek);

      result.push({
        week,
        dates,
      });
    }

    return result;
  }

  getDatesForCalendarWeek(year: number, week: number): Date[] {
    const firstDateOfYear = this.parseDate(year, 1, 1);
    const firstDayOfYear = firstDateOfYear.getDay();
    let firstDayOfWeek = 0;

    if (firstDayOfYear === 0) {
      firstDayOfWeek = 2;
    } else if (firstDayOfYear <= 3) {
      firstDayOfWeek = -firstDayOfYear + 2;
    } else {
      firstDayOfWeek = -firstDayOfYear + 9;
    }

    const firstDateOfWeek = this.getDate(this.parseDate(year, 1, firstDayOfWeek));

    const dates: Date[] = [];

    for (let i = 0; i < 7; i++) {
      dates.push(this.parseDate(firstDateOfWeek.year, firstDateOfWeek.month, firstDateOfWeek.date + i + (week - 1) * 7));
    }

    return dates;
  }

  getCalendarWeek(year: number, month: number, date: number) {
    const weekDate = this.parseDate(year, month, date);
    const firstDateOfYear = this.parseDate(year, 1, 1);

    const totalDays = (weekDate.getTime() - firstDateOfYear.getTime()) / 86400000;

    const week = Math.ceil((totalDays + 4 - (weekDate.getDay() || 7)) / 7);

    return week > 52 ? 1 : week;
  }

  getMonthForCalendarWeek(year: number, week: number) {
    const dates = this.getDatesForCalendarWeek(year, week);
    return this.getDate(dates[3]).month;
  }

  getCalendarWeeksForMonth(year: number, month: number): number[] {
    const weekStart = this.getCalendarWeek(year, month, 1);
    let weekEnd = this.getCalendarWeek(year, month + 1, 0);

    let weeks: number[] = [];

    if (weekEnd === 1) {
      weekEnd = 53;
    }

    for (let index = 0; index <= weekEnd - weekStart; index++) {
      weeks.push(weekStart + index);
    }

    if (weeks[weeks.length - 1] === 53) {
      weeks = [...weeks.splice(0, weeks.length - 1), 1];
    }

    return weeks;
  }

  parseDate(year: number, month: number, date: number) {
    return new Date(year, month - 1, date);
  }

  getDate(date: Date): { year: number; month: number; date: number } {
    return {
      year: date.getFullYear(),
      month: date.getMonth() + 1,
      date: date.getDate(),
    };
  }

  equals(date1: Date, date2: Date) {
    date1 = new Date(date1);
    date2 = new Date(date2);

    return date1.getFullYear() === date2.getFullYear() && date1.getMonth() === date2.getMonth() && date1.getDate() === date2.getDate();
  }

  isLessThan(date1: Date, date2: Date): boolean {
    date1 = new Date(date1);
    date2 = new Date(date2);

    return date1.getFullYear() >= date2.getFullYear() && date1.getMonth() >= date2.getMonth() && date1.getDate() > date2.getDate();
  }

  isGreaterThan(date1: Date, date2: Date): boolean {
    date1 = new Date(date1);
    date2 = new Date(date2);

    return date1.getFullYear() <= date2.getFullYear() && date1.getMonth() <= date2.getMonth() && date1.getDate() < date2.getDate();
  }

  addDaysToDate(date: Date, daysToAdd: number): Date {
    const newDate = date;
    newDate.setDate(newDate.getDate() + daysToAdd);

    return newDate;
  }
}
