import cloneDeep from 'lodash/cloneDeep'
import moment from 'moment'
import {
  CreateTimeRangeRequest,
  GetTimeRangesInMonthRequest,
  TimeRange,
  TimeRangesInDate,
  UpdateTimeRangeRequest,
  DeleteTimeRangeRequest,
  CreateDateOffRequest,
  DeleteDateOffRequest,
  CopyTimeRangeRequest,
  DeleteWeekDayRequest,
  RepeatTimeRangeRequest
} from 'redux/TimeRange/types'
import RRule, { RRuleSet, rrulestr, Frequency } from 'rrule'
import { Response } from '../redux/Common/types'
import { Api } from './api'
import momentTZ from 'moment-timezone'
import uniq from 'lodash/uniq'
import { DateOff } from 'redux/Experience/types'

interface IAvailableSlotService {
  getSlotsInMonth(
    payload: GetTimeRangesInMonthRequest
  ): Response<TimeRangesInDate[]>
}

export default class AvailableSlotService implements IAvailableSlotService {
  constructor(private readonly api: Api) {
    this.api = api
  }

  getSlotsInMonth = (
    payload: GetTimeRangesInMonthRequest
  ): Response<TimeRangesInDate[]> => {
    try {
      const timeRanges: TimeRangesInDate[] = new Array(32)
      const dateOffs = payload?.experience?.dateOffs
      if (dateOffs) {
        dateOffs.forEach((dateOff: DateOff) => {
          timeRanges[moment(dateOff?.date).date()] = {
            timeRanges: [],
            isOffDate: true
          }
        })
      }
      payload?.experience?.timeRanges?.forEach((timeRange: TimeRange) => {
        const startTime = moment.utc(new Date(timeRange.startTime || ''))
        const rule = rrulestr(`DTSTART:${startTime.format(
          'YYYYMMDD[T]HHmm00[Z]'
        )}
          RRULE:INTERVAL=1;WKST=MO${
            (timeRange?.occurrence?.length || 0) > 0
              ? `;FREQ=WEEKLY;BYDAY=${timeRange?.occurrence?.join(',')}`
              : ''
          }`)

        const rruleSet = new RRuleSet()
        rruleSet.rrule(rule)
        if (
          (timeRange.occurrence?.length || 0) > 0 &&
          !timeRange.occurrence?.includes(
            startTime.format('dddd').toUpperCase().substring(0, 2)
          )
        ) {
          rruleSet.rdate(startTime.toDate())
        }

        timeRange.exdates?.forEach(exdate => {
          const date = momentTZ.utc(exdate).tz(payload.timezone)
          rruleSet.exrule(
            new RRule({
              dtstart: date.toDate(),
              freq: Frequency.MINUTELY,
              until: date.add(1439, 'minutes').toDate()
            })
          )
        })

        let slotsInEvent = rruleSet.between(
          moment(
            moment(startTime).startOf('month').hours(0).format('DD/MM/YYYY'),
            'DD/MM/YYYY'
          ).toDate(),
          moment
            .utc()
            .month(payload.month)
            .endOf('month')
            .year(payload.year)
            .toDate()
        )
        slotsInEvent = uniq(slotsInEvent)
        slotsInEvent.forEach(slot => {
          const start = moment
            .utc(slot)
            .dates(startTime.dates())
            .months(startTime.months())
            .year(startTime.year())
          const startTZ = momentTZ.utc(slot).tz(payload.timezone)
          if (startTZ.month() === payload.month) {
            const endTime = moment.utc(timeRange.endTime)
            const end = moment
              .utc(slot)
              .hour(endTime.hour())
              .minutes(endTime.minutes())
              .dates(endTime.dates())
              .months(endTime.months())
              .year(endTime.year())
            const newSlot = {
              id: timeRange.id,
              startTime: start.toISOString(),
              endTime: end.toISOString(),
              occurrence: timeRange.occurrence
            }
            if (timeRanges[startTZ.date()]) {
              timeRanges[startTZ.date()].timeRanges.push(newSlot)
            } else {
              timeRanges[startTZ.date()] = {
                timeRanges: [newSlot],
                isOffDate: false
              }
            }
          }
        })
      })
      return {
        ok: true,
        response: cloneDeep(timeRanges)
      }
    } catch (error) {
      return {
        ok: false,
        response: [],
        message: 'Cannot get time ranges'
      }
    }
  }

  createSlotEvent = (
    payload: CreateTimeRangeRequest
  ): Promise<Response<TimeRange>> => {
    return this.api.createTimeRange(payload)
  }

  copyTimeRange = (
    payload: CopyTimeRangeRequest
  ): Promise<Response<TimeRange>> => {
    return this.api.copyTimeRange(payload)
  }

  updateSlot = (
    payload: UpdateTimeRangeRequest
  ): Promise<Response<TimeRange>> => {
    return this.api.updateTimeRange(payload)
  }

  createDateOff = (
    payload: CreateDateOffRequest
  ): Promise<Response<TimeRange>> => {
    return this.api.createDateOff(payload)
  }

  deleteSlot = (payload: DeleteTimeRangeRequest): Promise<Response<any>> => {
    return this.api.deleteTimeRange(payload)
  }

  deleteDateOff = (payload: DeleteDateOffRequest): Promise<Response<any>> => {
    return this.api.deleteDateOff(payload)
  }

  deleteWeekDay = (payload: DeleteWeekDayRequest): Promise<Response<any>> => {
    return this.api.deleteWeekDay(payload)
  }

  setRepeatTimeRange = (
    payload: RepeatTimeRangeRequest
  ): Promise<Response<any>> => {
    return this.api.setRepeatTimeRange(payload)
  }

  removeRepeatTimeRange = (
    payload: RepeatTimeRangeRequest
  ): Promise<Response<any>> => {
    return this.api.removeRepeatTimeRange(payload)
  }
}
