"use client";

import { ApiResponse } from "@/types/types";
import request from "@/lib/request";
import "@schedule-x/theme-shadcn/dist/index.css";
import { useNextCalendarApp, ScheduleXCalendar } from "@schedule-x/react";
import { createEventsServicePlugin } from "@schedule-x/events-service";
import { createDragAndDropPlugin } from "@schedule-x/drag-and-drop";
import { createResizePlugin } from "@schedule-x/resize";
import { createEventRecurrencePlugin } from "@schedule-x/event-recurrence";
import { createEventModalPlugin } from "@schedule-x/event-modal";
import {
	createViewDay,
	createViewWeek,
	PluginBase,
} from "@schedule-x/calendar";
import { useEffect, useState } from "react";
import { format } from "date-fns";
import { Dialog, DialogProps } from "@radix-ui/react-dialog";
import {
	DialogContent,
	DialogDescription,
	DialogFooter,
	DialogHeader,
	DialogTitle,
} from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { KeyedMutator } from "swr";
import { getCookie } from "cookies-next";
import { useTheme } from "next-themes";
import { EventForm, EventFormValues } from "./event-dialog";
import ICalendarEvent from "@/interfaces/ICalendarEvent";
import { UseFormReturn } from "react-hook-form";
import mapFrequencyToRrule from "@/lib/mapFrequencyToRrule";
import { useToast } from "@/hooks/use-toast";

type Plugins = PluginBase<string>[];

const Planning: React.FC<{
	events: ICalendarEvent[];
	mutate?: KeyedMutator<ApiResponse<ICalendarEvent[]>>;
	modifiable?: boolean;
}> = ({ events, mutate, modifiable = false }) => {
	const { toast } = useToast();
	const { resolvedTheme } = useTheme();
	const isConnected = getCookie("auth_token");
	const plugins: Plugins = [
		createEventsServicePlugin(),
		createEventRecurrencePlugin(),
	];
	if (isConnected && modifiable) {
		plugins.push(createDragAndDropPlugin());
		plugins.push(createResizePlugin());
	} else if (isConnected || !isConnected) {
		plugins.push(createEventModalPlugin());
	}
	const [eventSelected, setEventSelected] = useState<ICalendarEvent | null>(
		null,
	);
	const [newEvent, setNewEvent] = useState<Omit<ICalendarEvent, "id"> | null>(
		null,
	);

	const handleEventUpdate = async (
		eventSelected: ICalendarEvent | Omit<ICalendarEvent, "id">,
		willMutate: boolean = false,
	) => {
		if (!isConnected || !modifiable) return;
		const event = {
			...eventSelected,
			start: `${new Date(eventSelected.start).toISOString()}`,
			end: `${new Date(eventSelected.end).toISOString()}`,
		} as ICalendarEvent;
		try {
			const res = await request<undefined>(`/events/${event.id}/update`, {
				method: "PATCH",
				body: event,
				requiresAuth: true,
				csrfToken: false,
			});
			if (res.status === "Error") {
				toast({
					title: "Une erreur est survenue.",
					description: res.message,
				});
			} else {
				willMutate && mutate?.();
			}
		} catch (e) {
			if (e instanceof Error)
				toast({
					title: "Une erreur est survenue.",
					description: e.message,
				});
		}
	};

	const calendar = useNextCalendarApp(
		{
			theme: "shadcn",
			views: [createViewDay(), createViewWeek()],
			defaultView: "week",
			isDark: resolvedTheme === "dark" ? true : false,
			isResponsive: true,
			locale: "fr-FR",
			dayBoundaries: {
				start: "06:00",
				end: "00:00",
			},
			events: events.map((event) => ({
				...event,
				start: format(new Date(event.start), "yyyy-MM-dd HH:mm"),
				end: format(new Date(event.end), "yyyy-MM-dd HH:mm"),
			})),
			callbacks: {
				onEventClick(event, e) {
					setEventSelected(event as ICalendarEvent);
				},
				async onEventUpdate(newEvent) {
					await handleEventUpdate(newEvent as ICalendarEvent);
				},
			},
		},
		plugins,
	);

	useEffect(() => {
		calendar?.events.getAll();
	}, []);

	useEffect(() => {
		calendar?.setTheme(resolvedTheme === "dark" ? "dark" : "light");
	}, [resolvedTheme]);

	const AddButton: React.FC = () => {
		if (!isConnected || !modifiable) return <></>;
		return (
			<Button onClick={() => setNewEvent({})} variant="outline">
				Nouveau
			</Button>
		);
	};

	return (
		<div>
			<div className="m-8">
				<AddButton />
				<ScheduleXCalendar calendarApp={calendar} />
			</div>
			{newEvent && isConnected && modifiable && (
				<EventDialog
					open={newEvent !== null || false}
					onOpenChange={(open) => {
						setNewEvent((e) => (open ? e : null));
					}}
					onAdd={async (formValues) => {
						if (!isConnected || !modifiable) return;
						const rrule = mapFrequencyToRrule(
							formValues.frequency,
							formValues.frequencyEndDate,
						);
						const [sHours, sMinutes] =
							formValues.startTime.split(":");
						formValues.startDate.setHours(
							parseInt(sHours),
							parseInt(sMinutes),
						);
						const [eHours, eMinutes] =
							formValues.endTime.split(":");
						formValues.endDate.setHours(
							parseInt(eHours),
							parseInt(eMinutes),
						);
						console.log(formValues.endDate);
						const event: Omit<ICalendarEvent, "id"> = {
							...newEvent,
							start: formValues.startDate.toISOString(),
							end: formValues.endDate.toISOString(),
							title: `${formValues.title}`,
							fullday: formValues.fullDay,
							rrule: rrule,
							isVisible: formValues.isVisible,
							description: formValues.description,
							location: formValues.location,
						};
						const res = await request<undefined>(`/events/new`, {
							method: "POST",
							body: event,
							requiresAuth: true,
							csrfToken: false,
						});
						if (res.status === "Error") {
							toast({
								title: "Une erreur est survenue.",
								description: res.message,
							});
						}
						if (res.status === "Success") {
							mutate?.();
							console.log("Success");
						}
					}}
					event={newEvent}
				/>
			)}
			{eventSelected && modifiable && isConnected && (
				<EventDialog
					open={eventSelected !== null || false}
					onOpenChange={(open) => {
						setEventSelected((e) => (open ? e : null));
					}}
					event={eventSelected}
					onDelete={async (id) => {
						if (!isConnected || !modifiable) return;
						calendar?.events?.remove(id);
						try {
							const res = await request<undefined>(
								`/events/${id}/delete`,
								{
									method: "DELETE",
									requiresAuth: true,
									csrfToken: false,
								},
							);
							if (res.status === "Error") {
								toast({
									title: "Une erreur est survenue.",
									description: res.message,
								});
							}
							if (res.status === "Success") {
								console.log("Success");
							}
						} catch (e: unknown) {
							if (e instanceof Error)
								toast({
									title: "Une erreur est survenue.",
									description: e.message,
								});
						}
						setEventSelected(null);
					}}
					onUpdate={async (formValues) => {
						if (!isConnected || !modifiable) return;
						const rrule = mapFrequencyToRrule(
							formValues.frequency,
							formValues.frequencyEndDate,
						);
						const [sHours, sMinutes] =
							formValues.startTime.split(":");
						formValues.startDate.setHours(
							parseInt(sHours),
							parseInt(sMinutes),
						);
						const [eHours, eMinutes] =
							formValues.endTime.split(":");
						formValues.endDate.setHours(
							parseInt(eHours),
							parseInt(eMinutes),
						);
						const event: ICalendarEvent = {
							...eventSelected,
							start: formValues.startDate.toISOString(),
							end: formValues.endDate.toISOString(),
							title: `${formValues.title}`,
							fullday: formValues.fullDay,
							rrule: rrule,
							isVisible: formValues.isVisible,
							description: formValues.description,
							location: formValues.location,
						};
						await handleEventUpdate(event, true);
						setEventSelected(null);
					}}
				/>
			)}
		</div>
	);
};

const EventDialog: React.FC<
	{
		onDelete?: (id: string) => void;
		onUpdate?: (formValues: EventFormValues) => void;
		onAdd?: (formValues: EventFormValues) => void;
		event: ICalendarEvent | Omit<ICalendarEvent, "id">;
	} & DialogProps
> = ({ open, onOpenChange, onDelete, onUpdate, onAdd, event }) => {
	const [form, setForm] = useState<UseFormReturn<EventFormValues>>();

	const submitForm = (event: "add" | "update") => {
		const callback = event === "add" ? onAdd : onUpdate;
		if (callback) form?.handleSubmit(callback)();
	};

	return (
		<Dialog open={open} onOpenChange={onOpenChange}>
			<DialogContent className="sm:max-w-md">
				<DialogHeader>
					<DialogTitle>{event.title}</DialogTitle>
					<DialogDescription>{event.description}</DialogDescription>
				</DialogHeader>
				<EventForm event={event} setForm={setForm} />
				<DialogFooter className="flex flex-row justify-end">
					{onUpdate && (
						<Button
							variant="outline"
							onClick={() => submitForm("update")}
							type="submit"
						>
							Actualiser
						</Button>
					)}
					{onDelete && (
						<Button
							variant="destructive"
							onClick={() => onDelete(event.id)}
							type="submit"
						>
							Supprimer
						</Button>
					)}
					{onAdd && !onUpdate && !onDelete && (
						<Button onClick={() => submitForm("add")} type="submit">
							Créer
						</Button>
					)}
				</DialogFooter>
			</DialogContent>
		</Dialog>
	);
};

export default Planning;
