// React Imports
import { useEffect, useRef } from 'react'

// MUI Imports
import { useTheme } from '@mui/material/styles'

// Third-party imports
import 'bootstrap-icons/font/bootstrap-icons.css'

import FullCalendar from '@fullcalendar/react'
import faLocale from '@fullcalendar/core/locales/fa'
import listPlugin from '@fullcalendar/list'
import dayGridPlugin from '@fullcalendar/daygrid'
import timeGridPlugin from '@fullcalendar/timegrid'
import interactionPlugin from '@fullcalendar/interaction'
import type { CalendarOptions } from '@fullcalendar/core'

// Type Imports
import type { AddEventType, CalendarColors, CalendarType } from '@/types/apps/calendarTypes'
import type { AppDispatch } from '@/redux-store'

// Slice Imports
import { filterEvents, selectedEvent, updateEventAsync } from '@/redux-store/slices/calendar'

type CalenderProps = {
  calendarStore: CalendarType
  calendarApi: any
  setCalendarApi: (val: any) => void
  calendarsColor: CalendarColors
  dispatch: AppDispatch
  handleLeftSidebarToggle: () => void
  handleAddEventSidebarToggle: () => void
}

const blankEvent: AddEventType = {
  title: '',
  start: '',
  end: '',
  allDay: false,
  url: '',
  extendedProps: {
    calendar: '',
    guests: [],
    description: ''
  }
}

const Calendar = (props: CalenderProps) => {
  // Props
  const {
    calendarStore,
    calendarApi,
    setCalendarApi,
    calendarsColor,
    dispatch,
    handleAddEventSidebarToggle,
    handleLeftSidebarToggle
  } = props

  // Refs
  const calendarRef = useRef()

  // Hooks
  const theme = useTheme()

  useEffect(() => {
    if (calendarApi === null) {
      // @ts-ignore
      setCalendarApi(calendarRef.current?.getApi())
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [])

  // calendarOptions(Props)
  const calendarOptions: CalendarOptions = {
    events: calendarStore.events,
    plugins: [interactionPlugin, dayGridPlugin, timeGridPlugin, listPlugin],
    initialView: 'dayGridMonth',
    locale: 'fa',
    headerToolbar: {
      start: 'sidebarToggle,prev,next,title',
      end: 'dayGridMonth,timeGridWeek,timeGridDay,listMonth'
    },
    views: {
      week: {
        titleFormat(arg: any) {
          const start = arg.start
          const end = arg.end

          const formatter = new Intl.DateTimeFormat('fa-IR-u-ca-persian', {
            year: 'numeric',
            month: 'long',
            day: 'numeric'
          })

          if (!start && !end) {
            return ''
          }

          if (start && !end) {
            return formatter.format(start instanceof Date ? start : new Date(start.marker ?? start))
          }

          if (!start && end) {
            return formatter.format(end instanceof Date ? end : new Date(end.marker ?? end))
          }

          const s = start instanceof Date ? start : new Date(start.marker ?? start)
          const e = end instanceof Date ? end : new Date(end.marker ?? end)

          return `${formatter.format(s)} - ${formatter.format(e)}`
        }
      }
    },
    dayHeaderContent(arg) {
      const formatter = new Intl.DateTimeFormat('fa-IR-u-ca-persian', { weekday: 'short' })

      return formatter.format(arg.date)
    },
    titleFormat(arg: any) {
      const { start, end } = arg

      const monthFormatter = new Intl.DateTimeFormat('fa-IR-u-ca-persian', {
        year: 'numeric',
        month: 'long'
      })

      // FullCalendar بعضی وقت‌ها به‌جای Date، DateMarker خودش را می‌فرستد؛
      // با new Date(...) مطمئن می‌شویم ورودی برای Intl معتبر است.
      const target = start || end

      if (!target) {
        return ''
      }

      const asDate = target instanceof Date ? target : new Date(target as any)

      if (Number.isNaN(asDate.getTime())) {
        return ''
      }

      return monthFormatter.format(asDate)
    },
    buttonText: {
      today: 'امروز',
      month: 'ماه',
      week: 'هفته',
      day: 'روز',
      list: 'لیست'
    },

    /*
      Enable dragging and resizing event
      ? Docs: https://fullcalendar.io/docs/editable
    */
    editable: true,

    /*
      Enable resizing event from start
      ? Docs: https://fullcalendar.io/docs/eventResizableFromStart
    */
    eventResizableFromStart: true,

    /*
      Automatically scroll the scroll-containers during event drag-and-drop and date selecting
      ? Docs: https://fullcalendar.io/docs/dragScroll
    */
    dragScroll: true,

    /*
      Max number of events within a given day
      ? Docs: https://fullcalendar.io/docs/dayMaxEvents
    */
    dayMaxEvents: 2,

    /*
      Determines if day names and week names are clickable
      ? Docs: https://fullcalendar.io/docs/navLinks
    */
    navLinks: true,

    eventClassNames({ event: calendarEvent }: any) {
      // @ts-ignore
      const colorName = calendarsColor[calendarEvent._def.extendedProps.calendar]

      return [
        // Background Color
        `event-bg-${colorName}`
      ]
    },

    eventClick({ event: clickedEvent, jsEvent }: any) {
      jsEvent.preventDefault()

      dispatch(selectedEvent(clickedEvent))
      handleAddEventSidebarToggle()

      if (clickedEvent.url) {
        // Open the URL in a new tab
        window.open(clickedEvent.url, '_blank')
      }

      //* Only grab required field otherwise it goes in infinity loop
      //! Always grab all fields rendered by form (even if it get `undefined`)
      // event.value = grabEventDataFromEventApi(clickedEvent)
      // isAddNewEventSidebarActive.value = true
    },

    customButtons: {
      sidebarToggle: {
        icon: 'tabler tabler-menu-2',
        click() {
          handleLeftSidebarToggle()
        }
      }
    },

    dateClick(info: any) {
      const ev = { ...blankEvent }

      ev.start = info.date
      ev.end = info.date
      ev.allDay = true

      dispatch(selectedEvent(ev))
      handleAddEventSidebarToggle()
    },

    /*
      Handle event drop (Also include dragged event)
      ? Docs: https://fullcalendar.io/docs/eventDrop
      ? We can use `eventDragStop` but it doesn't return updated event so we have to use `eventDrop` which returns updated event
    */
    eventDrop({ event: droppedEvent }: any) {
      // Convert FullCalendar event to API format
      const eventData = {
        start: droppedEvent.start ? new Date(droppedEvent.start).toISOString() : '',
        end: droppedEvent.end ? new Date(droppedEvent.end).toISOString() : '',
        allDay: droppedEvent.allDay || false
      }
      dispatch(updateEventAsync({ id: droppedEvent.id, data: eventData }))
      dispatch(filterEvents())
    },

    /*
      Handle event resize
      ? Docs: https://fullcalendar.io/docs/eventResize
    */
    eventResize({ event: resizedEvent }: any) {
      // Convert FullCalendar event to API format
      const eventData = {
        start: resizedEvent.start ? new Date(resizedEvent.start).toISOString() : '',
        end: resizedEvent.end ? new Date(resizedEvent.end).toISOString() : '',
        allDay: resizedEvent.allDay || false
      }
      dispatch(updateEventAsync({ id: resizedEvent.id, data: eventData }))
      dispatch(filterEvents())
    },

    // @ts-ignore
    ref: calendarRef,

    direction: theme.direction
  }

  return <FullCalendar {...calendarOptions} />
}

export default Calendar
