import { isAfter, isBefore, isBeforeOrEqual, isEqual, iterateMinutes, iterateMonth, iterateWeek } from '@dvn/alaca/components/dvn-date-picker/date-utils';
import { Availability } from '@dvn/takvim/api/models';
const MAX_MONTH_FOR_RECURRENCE = 3;
const lastday = (date) => {
  return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
};
/**
 * Generates the availabilities from the raw availability list by using their recurrence.
 * Recurring availabilities are populated for every week/month until the maxMonthForRecurrence is reached.
 * @param availabilities
 * @param maxMonthForRecurrence
 * @param startDate used for calculating the end month
 */
export const generateAvailabilities = (availabilities, maxMonthForRecurrence = MAX_MONTH_FOR_RECURRENCE, startDate = new Date()) => {
  const maxDate = iterateMonth(startDate, maxMonthForRecurrence);
  maxDate.setDate(lastday(maxDate)); // last day is the end of the iterated month
  const now = new Date();
  const allAvailabilities = [];
  availabilities.forEach(availability => {
    if (availability.recurrence === 'weekly') {
      let baseStartDate = new Date(availability.startDate);
      let baseEndDate = new Date(availability.endDate);
      while (isBefore(baseStartDate, maxDate)) {
        allAvailabilities.push(Availability.decode(Object.assign(Object.assign({}, availability), { startDate: baseStartDate.toISOString(), endDate: baseEndDate.toISOString() })));
        baseStartDate = iterateWeek(baseStartDate, 1);
        baseEndDate = iterateWeek(baseEndDate, 1);
      }
    }
    else if (new Date(availability.endDate) >= now) {
      // single
      allAvailabilities.push(availability);
    }
  });
  return allAvailabilities;
};
/**
 * Returns the earliest start time and latest end time of the given availabilities.
 * @param availabilities
 */
export const getTimeFrameOfAvailabilities = (availabilities) => {
  let startDate;
  let endDate;
  availabilities.forEach(availability => {
    if (!startDate || isBefore(new Date(availability.startDate), startDate)) {
      startDate = new Date(availability.startDate);
    }
    if (!endDate || isAfter(new Date(availability.endDate), endDate)) {
      endDate = new Date(availability.endDate);
    }
  });
  return { startDate, endDate };
};
export const generateSlotsForAvailabilities = (availabilities = [], unavailabilitiesOfCurrentUser, unavailabilitiesOfOtherUser) => {
  const timeSlots = [];
  availabilities.forEach(availability => {
    const slots = generateSlotsForAvailability(availability);
    slots.forEach(slot => {
      if (new Date(slot.start) < new Date()) {
        return;
      }
      let hasIntersection = false;
      for (let i = 0; i < unavailabilitiesOfOtherUser.length; i++) {
        const unavailability = unavailabilitiesOfOtherUser[i];
        if (doTimesIntersect(slot.start, slot.end, new Date(unavailability.startDate), new Date(unavailability.endDate))) {
          hasIntersection = true;
          break;
        }
      }
      if (hasIntersection) {
        return;
      }
      for (let i = 0; i < unavailabilitiesOfCurrentUser.length; i++) {
        const unavailability = unavailabilitiesOfCurrentUser[i];
        if (doTimesIntersect(new Date(slot.start), new Date(slot.end), new Date(unavailability.startDate), new Date(unavailability.endDate))) {
          slot.sessionUserConflict = unavailability.reason;
          break;
        }
      }
      timeSlots.push(slot);
    });
  });
  return timeSlots;
};
export const doTimesIntersect = (start1, end1, start2, end2) => {
  if (isEqual(start1, start2) || isEqual(end1, end2)) {
    return true;
  }
  // s1    e1
  //    s2    e2
  if (isBefore(start1, start2) && isBefore(start2, end1)) {
    return true;
  }
  //    s1    e1
  // s2    e2
  if (isBefore(start2, start1) && isBefore(start1, end2)) {
    return true;
  }
  //    s1  e1
  // s2        e2
  if (isBefore(start2, start1) && isBefore(end1, end2)) {
    return true;
  }
  // s1        e1
  //    s2  e2
  if (isBefore(start1, start2) && isBefore(end2, end1)) {
    return true;
  }
  return false;
};
export const generateSlotsForAvailability = (availability) => {
  let slotStart = new Date(availability.startDate);
  let slotEnd = iterateMinutes(slotStart, availability.meetingDuration);
  const availabilityEnd = new Date(availability.endDate);
  const slots = [];
  do {
    slotStart.setSeconds(1);
    slots.push({
      availabilityId: availability._id,
      start: slotStart,
      end: slotEnd,
    });
    slotStart = iterateMinutes(slotEnd, availability.breakDuration || 0);
    slotEnd = iterateMinutes(slotStart, availability.meetingDuration);
  } while (isBeforeOrEqual(slotEnd, availabilityEnd));
  return slots;
};
