import { Calendar } from '@fullcalendar/core'
import interactionPlugin from '@fullcalendar/interaction'
import dayGridPlugin from '@fullcalendar/daygrid'
import timeGridPlugin from '@fullcalendar/timegrid'
import listPlugin from '@fullcalendar/list'
import multiMonthPlugin from '@fullcalendar/multimonth'
import scrollGridPlugin from '@fullcalendar/scrollgrid'
import timelinePlugin from '@fullcalendar/timeline'
import adaptivePlugin from '@fullcalendar/adaptive'
import resourcePlugin from '@fullcalendar/resource'
import resourceDayGridPlugin from '@fullcalendar/resource-daygrid'
import resourceTimelinePlugin from '@fullcalendar/resource-timeline'
import resourceTimeGridPlugin from '@fullcalendar/resource-timegrid'
import rrulePlugin from '@fullcalendar/rrule'
import momentPlugin from '@fullcalendar/moment'
import momentTimezonePlugin from '@fullcalendar/moment-timezone'
import locales from '@fullcalendar/core/locales-all'

export default function fullCalendar({
    locale,
    plugins,
    timeZone,
    config,
    editable,
    selectable,
    eventClassNames,
    eventContent,
    eventDidMount,
    eventWillUnmount,
}) {
    return {
        init() {
            const isSelectable = Boolean(selectable || config?.selectable)

            const calendarOptions = {
                headerToolbar: {
                    'left': 'prev,next today',
                    'center': 'title',
                    'right': 'dayGridMonth,dayGridWeek,dayGridDay',
                },
                plugins: plugins.map(plugin => availablePlugins[plugin]),
                locale,
                timeZone,
                editable,
                selectable,
                ...config,
                locales,
                events: (info, successCallback, failureCallback) => {
                    this.$wire.fetchEvents({ start: info.startStr, end: info.endStr, timezone: info.timeZone })
                        .then(successCallback)
                        .catch(failureCallback)
                },
                eventClick: ({ event, jsEvent }) => {
                    jsEvent.preventDefault()

                    if (event.url) {
                        const isNotPlainLeftClick = e => (e.which > 1) || (e.altKey) || (e.ctrlKey) || (e.metaKey) || (e.shiftKey)
                        return window.open(event.url, (event.extendedProps.shouldOpenUrlInNewTab || isNotPlainLeftClick(jsEvent)) ? '_blank' : '_self')
                    }

                    this.$wire.onEventClick(event)
                },
                eventDrop: async ({ event, oldEvent, relatedEvents, delta, oldResource, newResource, revert }) => {
                    const shouldRevert = await this.$wire.onEventDrop(event, oldEvent, relatedEvents, delta, oldResource, newResource)

                    if (typeof shouldRevert === 'boolean' && shouldRevert) {
                        revert()
                    }
                },
                eventResize: async ({ event, oldEvent, relatedEvents, startDelta, endDelta, revert }) => {
                    const shouldRevert = await this.$wire.onEventResize(event, oldEvent, relatedEvents, startDelta, endDelta)

                    if (typeof shouldRevert === 'boolean' && shouldRevert) {
                        revert()
                    }
                },
                dateClick: ({ dateStr, allDay, view, resource }) => {
                    if (! isSelectable) {
                        return;
                    }

                    const safeView = view ? {
                        type: view.type,
                    } : null

                    const safeResource = resource ? {
                        id: resource.id,
                        title: resource.title,
                    } : null

                    this.$wire.onDateSelect(dateStr, null, allDay, safeView, safeResource)
                },
                select: ({ startStr, endStr, allDay, view, resource }) => {
                    if (! isSelectable) {
                        return;
                    }

                    const safeView = view ? {
                        type: view.type,
                    } : null

                    const safeResource = resource ? {
                        id: resource.id,
                        title: resource.title,
                    } : null

                    this.$wire.onDateSelect(startStr, endStr, allDay, safeView, safeResource)
                },
            }

            if (eventClassNames !== null && eventClassNames !== undefined) {
                calendarOptions.eventClassNames = eventClassNames
            }

            if (typeof eventContent === 'function') {
                calendarOptions.eventContent = eventContent
            }

            if (typeof eventDidMount === 'function') {
                calendarOptions.eventDidMount = eventDidMount
            }

            if (typeof eventWillUnmount === 'function') {
                calendarOptions.eventWillUnmount = eventWillUnmount
            }

            const calendar = new Calendar(this.$el, calendarOptions)

            calendar.render()

            window.addEventListener('full-calendar--refresh', () => calendar.refetchEvents())
            window.addEventListener('full-calendar--prev', () => calendar.prev())
            window.addEventListener('full-calendar--next', () => calendar.next())
            window.addEventListener('full-calendar--today', () => calendar.today())
            window.addEventListener('full-calendar--goto', (event) => calendar.gotoDate(event.detail.date))
        },
    }
}

const availablePlugins = {
    'interaction': interactionPlugin,
    'dayGrid': dayGridPlugin,
    'timeGrid': timeGridPlugin,
    'list': listPlugin,
    'multiMonth': multiMonthPlugin,
    'scrollGrid': scrollGridPlugin,
    'timeline': timelinePlugin,
    'adaptive': adaptivePlugin,
    'resource': resourcePlugin,
    'resourceDayGrid': resourceDayGridPlugin,
    'resourceTimeline': resourceTimelinePlugin,
    'resourceTimeGrid': resourceTimeGridPlugin,
    'rrule': rrulePlugin,
    'moment': momentPlugin,
    'momentTimezone': momentTimezonePlugin,
}
