import { Head, Link, router } from '@inertiajs/react';
import { CalendarPlus } from 'lucide-react';
import { useCallback, useEffect, useMemo, useState } from 'react';
import AgendaList from '@/components/calendar/agenda-list';
import CalendarToolbar, {
    allClassrooms,
} from '@/components/calendar/calendar-toolbar';
import MonthGrid from '@/components/calendar/month-grid';
import WeekView from '@/components/calendar/week-view';
import { Button } from '@/components/ui/button';
import { useIsNarrow } from '@/hooks/use-narrow';
import {
    addDays,
    addMonths,
    dayOfInstant,
    eachDay,
    formatDayRange,
    formatMonthYear,
    todayIso,
} from '@/lib/date';
import { index as calendarIndex } from '@/routes/calendar';
import { create as classroomsCreate } from '@/routes/classrooms';
import type { CalendarMeeting, CalendarPageProps, CalendarView } from '@/types';

type Props = Partial<CalendarPageProps>;

const partialProps = ['meetings', 'range', 'view', 'date', 'filters'];

export default function CalendarIndex({
    view = 'month',
    date,
    range,
    meetings = [],
    classrooms = [],
    filters,
}: Props) {
    const today = todayIso();
    const anchor = date ?? today;
    const period = range ?? { from: anchor, to: anchor };
    const classroomFilter = filters?.classroom ?? allClassrooms;

    const [loading, setLoading] = useState(false);
    const isNarrow = useIsNarrow();

    useEffect(() => {
        const stopStart = router.on('start', () => setLoading(true));
        const stopFinish = router.on('finish', () => setLoading(false));

        return () => {
            stopStart();
            stopFinish();
        };
    }, []);

    const meetingsByDay = useMemo(() => {
        const byDay = new Map<string, CalendarMeeting[]>();

        for (const meeting of [...meetings].sort((left, right) =>
            left.scheduled_at.localeCompare(right.scheduled_at),
        )) {
            const day = dayOfInstant(meeting.scheduled_at);
            const bucket = byDay.get(day);

            if (bucket) {
                bucket.push(meeting);
            } else {
                byDay.set(day, [meeting]);
            }
        }

        return byDay;
    }, [meetings]);

    const go = useCallback(
        (next: { view?: CalendarView; date?: string; classroom?: string }) => {
            const nextClassroom = next.classroom ?? classroomFilter;

            router.get(
                calendarIndex({
                    query: {
                        view: next.view ?? view,
                        date: next.date ?? anchor,
                        classroom:
                            nextClassroom === allClassrooms
                                ? undefined
                                : nextClassroom,
                    },
                }).url,
                {},
                {
                    preserveState: true,
                    preserveScroll: true,
                    replace: true,
                    only: partialProps,
                },
            );
        },
        [anchor, classroomFilter, view],
    );

    // Step by whole months for a month-sized range, otherwise by the range itself,
    // so navigation stays correct whichever span the server decides to send.
    const step = useCallback(
        (direction: 1 | -1) => {
            const length = eachDay(period.from, period.to).length;

            go({
                date:
                    length >= 28
                        ? addMonths(anchor, direction)
                        : addDays(anchor, direction * Math.max(length, 1)),
            });
        },
        [anchor, go, period.from, period.to],
    );

    useEffect(() => {
        function onKeyDown(event: KeyboardEvent) {
            if (event.metaKey || event.ctrlKey || event.altKey) {
                return;
            }

            const target = event.target as HTMLElement | null;

            if (
                target?.isContentEditable ||
                ['INPUT', 'TEXTAREA', 'SELECT'].includes(target?.tagName ?? '')
            ) {
                return;
            }

            if (event.key === 'ArrowLeft') {
                event.preventDefault();
                step(-1);
            } else if (event.key === 'ArrowRight') {
                event.preventDefault();
                step(1);
            } else if (event.key === 't' || event.key === 'T') {
                event.preventDefault();
                go({ date: today });
            }
        }

        window.addEventListener('keydown', onKeyDown);

        return () => window.removeEventListener('keydown', onKeyDown);
    }, [go, step, today]);

    const title =
        view === 'month'
            ? formatMonthYear(anchor)
            : formatDayRange(period.from, period.to);

    // A month grid is unreadable on a phone, so it falls back to the same data as a list.
    const shownView: CalendarView =
        isNarrow && view === 'month' ? 'agenda' : view;

    const noClassrooms = classrooms.length === 0;

    const emptyState = noClassrooms ? (
        <div className="mx-auto max-w-sm text-center">
            <h2 className="text-sm font-medium">Nessuna aula da pianificare</h2>
            <p className="text-muted-foreground mt-1 text-sm">
                Crea un&apos;aula e programma la prima lezione: comparirà qui.
            </p>
            <Button className="mt-4" asChild>
                <Link href={classroomsCreate()}>
                    <CalendarPlus />
                    Crea aula
                </Link>
            </Button>
        </div>
    ) : (
        <div className="mx-auto max-w-sm text-center">
            <h2 className="text-sm font-medium">
                Nessuna lezione in questo periodo
            </h2>
            <p className="text-muted-foreground mt-1 text-sm">
                Cambia periodo con le frecce, oppure programma una lezione dalla
                pagina dell&apos;aula.
            </p>
        </div>
    );

    return (
        <>
            <Head title="Calendario" />

            <div className="flex h-full flex-1 flex-col p-4">
                <div className="bg-card flex min-h-0 flex-1 flex-col overflow-hidden rounded-xl border">
                    <CalendarToolbar
                        view={view}
                        title={title}
                        loading={loading}
                        classrooms={classrooms}
                        selectedClassroom={classroomFilter}
                        onPrevious={() => step(-1)}
                        onNext={() => step(1)}
                        onToday={() => go({ date: today })}
                        onViewChange={(next) => go({ view: next })}
                        onClassroomChange={(uuid) => go({ classroom: uuid })}
                    />

                    {noClassrooms ? (
                        <div className="flex flex-1 items-center justify-center p-8">
                            {emptyState}
                        </div>
                    ) : (
                        <>
                            {shownView === 'month' && (
                                <MonthGrid
                                    range={period}
                                    anchor={anchor}
                                    meetingsByDay={meetingsByDay}
                                    onOpenDay={(day) =>
                                        go({ view: 'agenda', date: day })
                                    }
                                />
                            )}

                            {shownView === 'week' && (
                                <WeekView
                                    range={period}
                                    meetingsByDay={meetingsByDay}
                                />
                            )}

                            {shownView === 'agenda' && (
                                <AgendaList
                                    range={period}
                                    meetingsByDay={meetingsByDay}
                                    emptyState={emptyState}
                                />
                            )}
                        </>
                    )}
                </div>
            </div>
        </>
    );
}

CalendarIndex.layout = {
    breadcrumbs: [
        {
            title: 'Calendario',
            href: calendarIndex(),
        },
    ],
};
