import axios from 'axios';
import dayjs from 'dayjs';

import CalendarDay, { ICalendarDay } from '../models/CalandarDay.model';
import { CalendarRepository } from '../repositories/calendar.repository';

interface IWorkingDaysApi {
  working_day: number;
  work_hours: number;
  wages: number;
  morning_start: string;
  morning_end: string;
  afternoon_start: string;
  afternoon_end: string;
  public_holiday: string;
  public_holiday_description: string;
  weekend_day: number;
  custom_date: number;
  custom_date_description: string;
  custom_date_color: string;
  type: number;
}

export class CalendarService {

  constructor(
    private readonly calendarRepository: CalendarRepository,
  ) {
  }

  /**
   * Download a calendar from working days API and save it to DB.
   * It will download all days between startDate and endDate inclusive,
   * for example,
   * startDate as 2025-01-01
   * endDate as 2026-01-01
   */
  async fulfillCalendar(startDate: string, endDate: string) {

    const getDays = () => {
      const days: string[] = [];
      const start = dayjs(startDate);
      const end = dayjs(endDate);
      for (let day = dayjs(start); day.isBefore(end); day = day.add(1, 'day')) {
        days.push(day.format('YYYY-MM-DD'));
      }
      return days;
    };

    const options = (date: string) => ({
      method: 'GET',
      url: 'https://working-days.p.rapidapi.com/1.3/get_info_day',
      params: {
        country_code: 'US',
        date,
      },
      headers: {
        'X-RapidAPI-Key': '07a550d083msh90e492b0eeb739cp180bb6jsn4005ea3ba2a7',
        'X-RapidAPI-Host': 'working-days.p.rapidapi.com',
      },
    });

    try {
      const dates = getDays();
      const datesToCreate: string[] = [];
      await Promise.all(dates.map(async (date) => {
        const foundDate = await CalendarDay.findOne({ date });
        if (!foundDate) {
          datesToCreate.push(date);
        }
      }));
      await Promise.all(datesToCreate.map(async (date, i) => {
        setTimeout(async () => {
          const response = await axios.request(options(date));
          await this.createDate(date, response.data);
          console.log('created', date);
          if (datesToCreate.length === i + 1) {
            console.log('done!');
          }
        }, i * 500);
      }));
    } catch (error) {
      console.error(error);
    }
  }

  async createDate(date: string, dateProp: IWorkingDaysApi) {
    const newDate: ICalendarDay = {
      date: date,
      customDate: dateProp.custom_date === 1,
      customDateDescription: dateProp.custom_date_description,
      publicHoliday: dateProp.public_holiday === '1',
      publicHolidayDescription: dateProp.public_holiday_description,
      weekendDay: dateProp.weekend_day === 1,
      workingDay: dateProp.working_day === 1,
    };
    await CalendarDay.create(newDate);
  }

  async isWorkingDay(date: string): Promise<boolean> {
    const calendarDay = await CalendarDay.findOne({ date });
    return calendarDay.workingDay;
  }

  async isLastWorkingDay(date: string): Promise<boolean> {
    const calendarDay = await CalendarDay.findOne({ date });
    const isNextDayWorking = await this.isWorkingDay(dayjs(calendarDay.date).add(1, 'day').format('YYYY-MM-DD'));
    return !isNextDayWorking;
  }

  async getNextWorkingDay(date: string) {
    return this.calendarRepository.getNextWorkingDay(date);
  }

  async getLastWorkingDay(date: string) {
    let previous = dayjs(new Date(date));
    let isWorking = false;
    do {
      previous = previous.subtract(1, 'day');
      const calendarDay = await CalendarDay.findOne({ date: previous.format('YYYY-MM-DD') }).lean();
      if (!calendarDay) {
        console.error(`no data in calendar for ${previous.format('YYYY-MM-DD')}`);
      }
      isWorking = calendarDay ? calendarDay.workingDay : true;
    } while (isWorking === false);
    return previous.format('YYYY-MM-DD');
  }

  async getFullCalendarList() {
    return CalendarDay.find().sort({ date: 1 }).lean();
  }
}
