import { EventErrorHandler } from '@/components/Error/EventErrorHandler'
import { EventChip } from '@/components/Event/EventChip/EventChip'
import { Calendar } from '@/features/Calendars/CalendarTypes'
import { userAttendee } from '@/features/User/models/attendee'
import {
  CalendarApi,
  DayHeaderMountArg,
  EventContentArg,
  EventMountArg,
  NowIndicatorContentArg,
  ViewMountArg
} from '@fullcalendar/core'
import React from 'react'
import { CALENDAR_VIEWS } from '../utils/constants'
import { createMouseHandlers } from './mouseHandlers'

export interface ViewHandlersProps {
  calendarRef: React.RefObject<CalendarApi | null>
  setSelectedDate: (date: Date) => void
  setSelectedMiniDate: (date: Date) => void
  onViewChange?: (view: string) => void
  calendars: Record<string, Calendar>
  tempcalendars: Record<string, Calendar>
  errorHandler: EventErrorHandler
}

export const createViewHandlers = (props: ViewHandlersProps) => {
  const {
    calendarRef,
    setSelectedDate,
    setSelectedMiniDate,
    onViewChange,
    calendars,
    tempcalendars,
    errorHandler
  } = props

  const handleNowIndicatorContent = (arg: NowIndicatorContentArg) => {
    if (arg.isAxis) {
      return React.createElement(
        'div',
        { style: { display: 'flex', alignItems: 'center' } },
        React.createElement(
          'div',
          { className: 'now-time-label' },
          new Date().toLocaleTimeString(undefined, {
            hour: '2-digit',
            minute: '2-digit',
            hour12: false,
            timeZone: arg.view.dateEnv.timeZone
          })
        )
      )
    }
  }

  const handleDayHeaderDidMount = (arg: DayHeaderMountArg) => {
    if (arg.view.type === CALENDAR_VIEWS.timeGridWeek) {
      const headerEl = arg.el

      const handleDayHeaderClick = () => {
        calendarRef.current?.changeView(CALENDAR_VIEWS.timeGridDay, arg.date)
        setSelectedDate(new Date(arg.date))
        setSelectedMiniDate(new Date(arg.date))

        if (onViewChange) {
          onViewChange(CALENDAR_VIEWS.timeGridDay)
        }
      }

      headerEl.addEventListener('click', handleDayHeaderClick)
      headerEl.__dayHeaderClickHandler = handleDayHeaderClick
    }
  }

  const handleDayHeaderWillUnmount = (arg: DayHeaderMountArg) => {
    const headerEl = arg.el
    if (headerEl.__dayHeaderClickHandler) {
      headerEl.removeEventListener('click', headerEl.__dayHeaderClickHandler)
      delete headerEl.__dayHeaderClickHandler
    }
  }

  const handleViewDidMount = (arg: ViewMountArg) => {
    if (
      arg.view.type === CALENDAR_VIEWS.timeGridWeek ||
      arg.view.type === CALENDAR_VIEWS.timeGridDay
    ) {
      const calendarEl = document.querySelector('.fc') as HTMLElement
      if (calendarEl) {
        const mouseHandlers = createMouseHandlers({ calendarEl })
        mouseHandlers.addMouseEventListeners()
      }
    }
  }

  const handleViewWillUnmount = (arg: ViewMountArg) => {
    if (arg.el.__timeInterval) {
      clearInterval(arg.el.__timeInterval)
      delete arg.el.__timeInterval
    }

    if (arg.el.__timeObserver) {
      arg.el.__timeObserver.disconnect()
      delete arg.el.__timeObserver
    }

    const calendarEl = document.querySelector('.fc') as HTMLElement
    if (calendarEl) {
      const mouseHandlers = createMouseHandlers({ calendarEl })
      mouseHandlers.removeMouseEventListeners()
    }
  }

  const handleEventContent = (arg: EventContentArg) => {
    return React.createElement(EventChip, {
      arg,
      calendars,
      tempcalendars,
      errorHandler
    })
  }

  const handleEventDidMount = (arg: EventMountArg) => {
    const attendees = arg.event._def.extendedProps.attendee || []
    if (!calendars[arg.event._def.extendedProps.calId]) return
    const ownerEmails = new Set(
      calendars[arg.event._def.extendedProps.calId].owner?.emails?.map(email =>
        email.toLowerCase()
      )
    )
    const showSpecialDisplay = attendees.filter((att: userAttendee) =>
      ownerEmails.has(att.cal_address.toLowerCase())
    )

    if (!showSpecialDisplay[0]) return

    arg.el.classList.remove(
      'declined-event',
      'tentative-event',
      'needs-action-event'
    )

    switch (showSpecialDisplay[0].partstat) {
      case 'DECLINED':
        arg.el.classList.add('declined-event')
        break
      case 'TENTATIVE':
        arg.el.classList.add('tentative-event')
        break
      case 'NEEDS-ACTION':
        arg.el.classList.add('needs-action-event')
        break
      default:
        break
    }
  }

  return {
    handleNowIndicatorContent,
    handleDayHeaderDidMount,
    handleDayHeaderWillUnmount,
    handleViewDidMount,
    handleViewWillUnmount,
    handleEventContent,
    handleEventDidMount
  }
}
