import { Calendar, EventSourceInput } from '@fullcalendar/core';
import dayGridPlugin from '@fullcalendar/daygrid';
import multiMonthPlugin from '@fullcalendar/multimonth';
import { action, computed, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { dateRangeStrToDates } from '../../../../ClientUtils';
import { Doc, DocListCast } from '../../../../fields/Doc';
import { NumCast, StrCast } from '../../../../fields/Types';
import { DocumentType } from '../../../documents/DocumentTypes';
import { Docs } from '../../../documents/Documents';
import { ViewBoxBaseComponent } from '../../DocComponent';
import { FieldView, FieldViewProps } from '../FieldView';
import './CalendarBox.scss';
import { CollectionSubView, SubCollectionViewProps } from '../../collections/CollectionSubView';

type CalendarView = 'month' | 'multi-month' | 'week';

@observer
export class CalendarBox extends CollectionSubView() {
    _calendarRef: HTMLDivElement | null = null;
    _calendar: Calendar | undefined;
    _oldWheel: HTMLElement | null = null;
    _observer: ResizeObserver | undefined;

    constructor(props: SubCollectionViewProps) {
        super(props);
        makeObservable(this);
    }

    @observable _multiMonth = 0;

    componentDidMount(): void {
        reaction(
            () => this.calendarEvents,
            events => {
                this._calendar?.setOption('events', events);
            },
            { fireImmediately: true }
        );
    }

    @computed get calendarEvents(): EventSourceInput | undefined {
        return this.childDocs.map(doc => {
            const { startDate: start, endDate: end } = dateRangeStrToDates(StrCast(doc.date_range));

            return {
                title: StrCast(doc.title),
                start,
                end,
                startEditable: true,
                endEditable: true,
                allDay: false,
                classNames: ['mother'], // will determine the style
                editable: true, // subject to change in the future
                backgroundColor: this.eventToColor(doc),
                color: 'white',
                extendedProps: {
                    description: StrCast(doc.description),
                },
            };
        });
    }

    @computed get dateRangeStrDates() {
        return dateRangeStrToDates(StrCast(this.Document.date_range));
    }

    // Choose a calendar view based on the date range
    @computed get calendarViewType(): CalendarView {
        const { startDate, endDate } = this.dateRangeStrDates;

        if (startDate.getFullYear() !== endDate.getFullYear() || startDate.getMonth() !== endDate.getMonth()) return 'multi-month';

        if (Math.abs(startDate.getDay() - endDate.getDay()) > 7) return 'month';
        return 'week';
    }

    eventToColor(event: Doc): string {
        // TODO: Return a different color based on the event type
        console.log(event.title);
        return 'blue';
    }

    handleEventClick = (/* arg: EventClickArg */) => {
        // TODO: open popover with event description, option to open CalendarManager and change event date, delete event, etc.
    };

    // https://fullcalendar.io
    renderCalendar = () => {
        const cal = !this._calendarRef
            ? null
            : (this._calendar = new Calendar(this._calendarRef, {
                  plugins: [multiMonthPlugin, dayGridPlugin],
                  headerToolbar: {
                      left: 'prev,next today',
                      center: 'title',
                      right: 'dayGridMonth multiMonth', // timeGridWeek timeGridDay listWeek',
                  },
                  initialDate: this.dateRangeStrDates.startDate,
                  navLinks: true,
                  editable: false,
                  displayEventTime: false,
                  displayEventEnd: false,
                  aspectRatio: NumCast(this.Document.width) / NumCast(this.Document.height),
                  events: this.calendarEvents,
                  eventClick: this.handleEventClick,
              }));
        cal?.render();
    };

    onPassiveWheel = (e: WheelEvent) => e.stopPropagation();
    render() {
        this._multiMonth;
        return (
            <div
                key={this._multiMonth}
                className="calendarBox"
                onPointerDown={action(e =>
                    setTimeout(() => {
                        if ((e.nativeEvent.target as HTMLButtonElement)?.className?.includes('multiMonth-button')) {
                            this._multiMonth = this._multiMonth + 1;
                        }
                    })
                )}
                ref={r => {
                    this._oldWheel?.removeEventListener('wheel', this.onPassiveWheel);
                    this._oldWheel = r;
                    // prevent wheel events from passively propagating up through containers and prevents containers from preventDefault which would block scrolling
                    r?.addEventListener('wheel', this.onPassiveWheel, { passive: false });

                    if (r) {
                        this._observer?.disconnect();
                        (this._observer = new ResizeObserver(() => {
                            this._calendar?.setOption('aspectRatio', NumCast(this.Document.width) / NumCast(this.Document.height));
                            this._calendar?.updateSize();
                        })).observe(r);
                        this.renderCalendar();
                    }
                }}>
                <div className="calendarBox-wrapper" ref={r => (this._calendarRef = r)} />
            </div>
        );
    }
}
