import moment from 'moment'
import httpClient from '../../../plugins/axios'
import { useSortedDateStrings, useUrlParams } from '../common/helper'
import { settings } from '../../../plugins/settings'
import { useAvailableSlots, useDuration } from '../common/appointments'
import { useCartItem } from './cart'

function useLocalFromUtcSlots(slots) {
  let formattedSlots = {}

  for (let date in slots) {
    for (let time in slots[date]) {
      let dateTime = moment
        .utc(date + ' ' + time, 'YYYY-MM-DD HH:mm')
        .local()
        .format('YYYY-MM-DD HH:mm')
        .split(' ')

      if (!(dateTime[0] in formattedSlots)) {
        formattedSlots[dateTime[0]] = {}
      }

      formattedSlots[dateTime[0]][dateTime[1]] = slots[date][time]
    }
  }

  return formattedSlots
}

function useAppointmentParams(store) {
  let employeeId = store.getters['booking/getEmployeeId']

  return {
    queryTimeZone: settings.general.showClientTimeZone
      ? Intl.DateTimeFormat().resolvedOptions().timeZone
      : null,
    monthsLoad: 1,
    locationId: store.getters['booking/getLocationId'],
    serviceId: store.getters['booking/getServiceId'],
    serviceDuration: store.getters['booking/getBookingDuration'],
    providerIds: !employeeId
      ? store.getters['entities/filteredEmployees'](store.getters['booking/getSelection']).map(
          (item) => item.id,
        )
      : [employeeId],
    extras: JSON.stringify(
      store.getters['entities/getService'](store.getters['booking/getServiceId'])
        .extras.map((extra) =>
          extra.quantity
            ? {
                id: extra.id,
                quantity: extra.quantity,
              }
            : null,
        )
        .filter((extra) => extra !== null),
    ),
    group: 1,
    page: 'booking',
    structured: true,
    persons: store.getters['booking/getBookingPersons'],
  }
}

function useAppointmentSlots(
  params,
  fetchedSlots,
  callback,
  customCallback,
  waitingListOptions = null,
) {
  httpClient.get('/slots', { params: useUrlParams(params) }).then((response) => {
    let resultSlots =
      'queryTimeZone' in params && params.queryTimeZone
        ? useLocalFromUtcSlots(response.data.data.slots)
        : response.data.data.slots

    let slots = fetchedSlots !== null ? fetchedSlots : resultSlots

    if (fetchedSlots !== null) {
      Object.keys(resultSlots).forEach((date) => {
        slots[date] = resultSlots[date]
      })
    }

    let occupied =
      'queryTimeZone' in params && params.queryTimeZone
        ? useLocalFromUtcSlots(response.data.data.occupied)
        : response.data.data.occupied

    // Waiting list slots provided by backend (preferred)
    let waitingListSlots = {}

    // Fallback: derive on frontend if backend doesn't yet supply and waiting list enabled
    if (!Object.keys(waitingListSlots).length) {
      const wlEnabled = !!(waitingListOptions && waitingListOptions.enabled)
      if (wlEnabled) {
        Object.keys(occupied || {}).forEach((date) => {
          Object.keys(occupied[date] || {}).forEach((time) => {
            if (!(date in slots) || !(time in slots[date])) {
              const providersData = occupied[date][time] || []
              const serviceId = params.serviceId
              const duration = params.serviceDuration
              // Consider only rows for this service; require at least one match
              let serviceRows = providersData.filter((p) => p.s === serviceId)
              if (
                'locationId' in params &&
                params.locationId !== null &&
                params.locationId !== ''
              ) {
                serviceRows = serviceRows.filter(
                  (p) => p.l != null && p.l !== '' && String(p.l) === String(params.locationId),
                )
              }
              if (!serviceRows.length) return
              if (duration) {
                const expectedDuration = duration / 60
                serviceRows = serviceRows.filter((p) => Number(p.d) === Number(expectedDuration))
                if (!serviceRows.length) return
              }
              // Filter for selected providers only
              const selectedProviderRows =
                params.providerIds && params.providerIds.length
                  ? serviceRows.filter((p) => params.providerIds.includes(p.e))
                  : serviceRows

              // Check if ALL selected providers are fully booked (no capacity available)
              const allFullyBooked =
                selectedProviderRows.length > 0 && selectedProviderRows.every((p) => p.c <= 0)

              if (!allFullyBooked) return

              // Filter providers that have waiting list capacity
              const waitingListProviders = selectedProviderRows.filter((p) => {
                return (
                  !waitingListOptions.maxCapacity || (p.w || 0) < waitingListOptions.maxCapacity
                )
              })

              // Add to waiting list only if all providers are fully booked and at least one has waiting capacity
              if (waitingListProviders.length > 0) {
                // If this time exists in regular slots, don't add waiting list (user can book the regular slot)
                let regularSlotExists = slots[date] && slots[date][time]

                if (!regularSlotExists) {
                  if (!(date in waitingListSlots)) waitingListSlots[date] = {}
                  waitingListSlots[date][time] = waitingListProviders
                }
              }
            }
          })
        })
      }
    }

    callback(
      slots,
      occupied,
      response.data.data.minimum,
      response.data.data.maximum,
      response.data.data.busyness,
      response.data.data.appCount,
      { providerId: response.data.data.lastProvider, fromBackend: true },
      customCallback,
      waitingListSlots,
    )
  })
}

function useRange(store) {
  return store.getters['booking/getMultipleAppointmentsRange']
}

function useSelectedDuration(store, value) {
  let cartItem = useCartItem(store)

  store.commit('booking/setDuration', value)

  let service = store.getters['entities/getService'](cartItem.serviceId)

  let extrasIds = store.getters['booking/getSelectedExtras'].map((i) => i.extraId)

  return useDuration(
    value,
    service.extras.filter((i) => extrasIds.includes(i.id)),
  )
}

function useSelectedDate(store, date, range) {
  store.commit('booking/setMultipleAppointmentsDate', date)

  store.commit('booking/setMultipleAppointmentsRange', range)

  return useAvailableSlots(store)
}

function useSelectedTime(store, time) {
  store.commit('booking/setMultipleAppointmentsTime', time)
}

function useDeselectedDate(store) {
  let cartItem = useCartItem(store)

  store.commit('booking/unsetMultipleAppointmentsData', cartItem.index)
}

function useSlotsCallback(
  store,
  slots,
  occupied,
  minimumDateTime,
  maximumDateTime,
  busyness,
  appCount,
  lastBookedProviderId,
  searchStart,
  searchEnd,
) {
  store.commit('booking/setMultipleAppointmentsSlots', slots)
  store.commit('booking/setMultipleAppointmentsOccupied', occupied)
  store.commit('booking/setMultipleAppointmentsLastDate', maximumDateTime)
  store.commit('booking/setBusyness', busyness)
  store.commit('booking/setLastBookedProviderId', lastBookedProviderId)
  store.commit('booking/setMultipleAppointmentsAppCount', appCount)

  let result = {}

  let cartItem = useCartItem(store)

  let activeService = cartItem.services[cartItem.serviceId]

  if (cartItem.index !== '' && activeService.list.length) {
    let dates = useSortedDateStrings(Object.keys(slots))

    result['calendarStartDate'] = activeService.list[cartItem.index].date
      ? activeService.list[cartItem.index].date
      : dates.length
        ? dates[0]
        : null

    result['calendarEventDate'] = activeService.list[cartItem.index].date
      ? activeService.list[cartItem.index].date
      : ''

    if (!(activeService.list[cartItem.index].date in slots)) {
      store.commit('booking/setMultipleAppointmentsDate', null)
      store.commit('booking/setMultipleAppointmentsTime', null)

      result['calendarEventSlot'] = ''

      result['calendarEventSlots'] = []
    } else if (
      activeService.list.length &&
      !(activeService.list[cartItem.index].time in slots[activeService.list[cartItem.index].date])
    ) {
      store.commit('booking/setMultipleAppointmentsTime', null)

      result['calendarEventSlot'] = ''
    }

    if (
      activeService.list.length &&
      activeService.list[cartItem.index].date &&
      (searchStart.value
        ? moment(activeService.list[cartItem.index].date).isSameOrAfter(searchStart.value)
        : true) &&
      (searchEnd.value
        ? moment(activeService.list[cartItem.index].date).isSameOrBefore(searchEnd.value)
        : true)
    ) {
      if (activeService.list[cartItem.index].date in activeService.slots) {
        let availableSlots = useAvailableSlots(store)

        result['calendarEventSlots'] = availableSlots.length
          ? availableSlots
          : Object.keys(activeService.slots[activeService.list[cartItem.index].date])

        if (activeService.list[cartItem.index].time) {
          result['calendarEventSlot'] = activeService.list[cartItem.index].time
        }
      }
    }
  }

  return result
}

function useSlotsPricing(store, slots, serviceId) {
  let employeesPrices = {}

  store.getters['entities/getEmployees'].forEach((item) => {
    let employeeService = item.serviceList.find((i) => i.id === serviceId)

    if (typeof employeeService !== 'undefined') {
      employeesPrices[item.id] = item.serviceList.find((i) => i.id === serviceId).price
    }
  })

  let result = {}

  let minPrice = null

  let midPrice = null

  let maxPrice = null

  let multipleMin = false

  let multipleMid = false

  let multipleMax = false

  Object.keys(slots).forEach((date) => {
    result[date] = { slots: {} }

    Object.keys(slots[date]).forEach((time) => {
      let haveHigh = false

      let haveLow = false

      let haveMid = false

      let minDatePrice = null

      let maxDatePrice = null

      slots[date][time].forEach((i) => {
        let price = i.p === null ? employeesPrices[i.e] : i.p

        if (minDatePrice === null || price < minDatePrice) {
          minDatePrice = price
        }

        if (maxDatePrice === null || price > maxDatePrice) {
          maxDatePrice = price
        }

        if (price === employeesPrices[i.e]) {
          if (midPrice !== null && price !== midPrice) {
            multipleMid = true
          }

          if (midPrice === null || price < midPrice) {
            midPrice = price
          }

          haveMid = true
        } else if (price < employeesPrices[i.e]) {
          if (minPrice !== null && price !== minPrice) {
            multipleMin = true
          }

          if (minPrice === null || minDatePrice < minPrice) {
            minPrice = minDatePrice
          }

          haveLow = true
        } else if (price > employeesPrices[i.e]) {
          if (maxPrice !== null && price !== maxPrice) {
            multipleMax = true
          }

          if (maxPrice === null || maxDatePrice < maxPrice) {
            maxPrice = maxDatePrice
          }

          haveHigh = true
        }
      })

      result[date].slots[time] = {
        type:
          haveLow && !haveHigh && !haveMid
            ? 'low'
            : haveHigh && !haveLow && !haveMid
              ? 'high'
              : 'mid',
        price: minDatePrice,
      }
    })

    result[date].price =
      Object.values(result[date].slots).filter((i) => i.price === null).length === 0

    let types = Object.values(result[date].slots).map((i) => i.type)

    if (types.filter((i) => i === 'low').length === types.length) {
      result[date].type = 'low'
    } else if (types.filter((i) => i === 'high').length === types.length) {
      result[date].type = 'high'
    } else {
      result[date].type = 'mid'
    }
  })

  return {
    price: {
      low: minPrice,
      mid: midPrice,
      high: maxPrice,
      uniqueMin: !multipleMin,
      uniqueMid: !multipleMid,
      uniqueMax: !multipleMax,
    },
    dates: result,
  }
}

export {
  useRange,
  useSelectedDuration,
  useSelectedDate,
  useSelectedTime,
  useDeselectedDate,
  useSlotsCallback,
  useAppointmentSlots,
  useAppointmentParams,
  useSlotsPricing,
}
