"use client";

import React, { useEffect, useState } from "react";
import style from "./page.module.scss";
import moment, { Moment, unitOfTime } from "moment";
import { FcGraduationCap } from "react-icons/fc";
import { TbArrowNarrowLeft, TbArrowNarrowRight } from "react-icons/tb";
import { MdOutlineArrowDropUp } from "react-icons/md";
import Cookies from "universal-cookie";
import { motion } from "framer-motion";
import promos from "../constants/promos";

const cookies = new Cookies();
moment.locale("fr");

const Days = Object.freeze(["Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi"]);
const Months: { [key: string]: string } = Object.freeze({
	January: "Janvier",
	February: "Février",
	March: "Mars",
	April: "Avril",
	May: "Mai",
	June: "Juin",
	July: "Juillet",
	August: "Août",
	September: "Septembre",
	October: "Octobre",
	November: "Novembre",
	December: "Décembre",
});

type Promo = {
	name: string;
	id: number;
	promoId: string;
	campus: 0 | 1;
};

type Week = {
	start: Moment;
	end: Moment;
	count: number;
};

type RawPlanningEvent = {
	title: string;
	room: string;
	teacher: string;
	start: string;
	end: string;
	color: string;
};

type PlanningEvent = {
	title: string;
	room: string;
	teacher: string;
	start: Moment;
	end: Moment;
	color: string;
};

export async function getGroups(promo: Promo): Promise<string[]> {
	const url = new URL("http://localhost:3000/api/groups");
	url.searchParams.append("promo", promo.id.toString());

	const res = await fetch(url.toString(), {
		next: { revalidate: 86400 },
	});

	return res.json();
}

export async function getPlanning(
	promo: Promo,
	group: string,
	start: Moment,
	end: Moment
): Promise<RawPlanningEvent[]> {
	const url = new URL("http://localhost:3000/api/planning");

	url.searchParams.append("start", start.toJSON());
	url.searchParams.append("end", end.toJSON());
	url.searchParams.append("grp", group);
	url.searchParams.append("promo", promo.id.toString());

	const res = await fetch(url.toString(), {
		next: { revalidate: 0 },
	});

	return res.json();
}

export default function Planning() {
	const [group, setGroup] = useState(null as string | null);
	const [promo, setPromo] = useState(null as Promo | null);
	const [groups, setGroups] = useState([] as string[]);
	const [groupsVisible, setGroupsVisible] = useState(false);
	const [promosVisible, setPromosVisible] = useState(false);
	const [mobile, setMobile] = useState(true);

	const [week, setWeek] = useState({
		start: moment().startOf("day"),
		end: moment().endOf("day"),
		count: 0,
	});

	const [days, setDays] = useState(
		new Array(1).fill(null).map(() => []) as PlanningEvent[][]
	);

	async function fetchPlanning(
		week: {
			start: Moment;
			end: Moment;
			count: number;
		},
		group: string,
		promo: Promo,
		isMobile: boolean
	) {
		const planning = await getPlanning(promo, group, week.start, week.end);
		const planningDays: PlanningEvent[][] = new Array(isMobile ? 1 : 5)
			.fill(null)
			.map(() => []);

		planning.forEach((event) => {
			const start = moment(event.start);
			const end = moment(event.end);
			const day = start.diff(week.start, "day") - 1;

			planningDays[isMobile ? 0 : day].push({
				title: event.title,
				room: event.room,
				teacher: event.teacher,
				start: start,
				end: end,
				color: event.color,
			});
		});

		setDays(planningDays);
	}

	function defineGroup(newGroup: string) {
		cookies.set("group", newGroup);
		setGroup(newGroup);
		setGroupsVisible(false);

		if (promo) fetchPlanning(week, newGroup, promo, mobile);
	}

	async function definePromo(newPromo: Promo) {
		cookies.set("promo", newPromo);
		setPromo(newPromo);
		setPromosVisible(false);

		const groups = await getGroups(newPromo);
		const defaultGroup = cookies.get("group") ?? groups[0];

		setGroups(groups);
		setGroup(defaultGroup);

		fetchPlanning(week, defaultGroup, newPromo, mobile);
	}

	function addWeek(addCount: number) {
		const unit: unitOfTime.DurationConstructor = mobile ? "day" : "week";

		if (addCount === 0) {
			var count = 0;
			var start = moment().startOf(unit);
			var end = moment().endOf(unit);
		} else {
			var count = week.count + addCount;
			var start = moment().startOf(unit).add(count, unit);
			var end = moment().endOf(unit).add(count, unit);
		}

		setWeek({ count, start, end });
		if (group && promo) {
			fetchPlanning({ count, start, end }, group, promo, mobile);
		}
	}

	function getDayStart(events: PlanningEvent[]): Moment {
		return events.reduce((pre: any, val) => {
			if (!pre) return val.start;
			if (val.start.diff(pre, "minutes") < 0) return val.start;
			return pre;
		}, null);
	}

	function getDayEnd(events: PlanningEvent[]): Moment {
		return events.reduce((pre: any, val) => {
			if (!pre) return val.end;
			if (val.end.diff(pre, "minutes") > 0) return val.end;
			return pre;
		}, null);
	}

	async function updatePlanning(week: Week, mobile: boolean) {
		const defaultPromo = cookies.get("promo") ?? promos[0];
		setPromo(defaultPromo);

		const groups = await getGroups(defaultPromo);
		const defaultGroup = cookies.get("group") ?? groups[0];

		setGroups(groups);
		setGroup(defaultGroup);

		fetchPlanning(week, defaultGroup, defaultPromo, mobile);
	}

	useEffect(() => {
		let isMobile = true;
		let currentWeek = week;

		function handleResize() {
			isMobile = window.innerWidth < 768;
			const unit: unitOfTime.DurationConstructor = isMobile ? "day" : "week";

			currentWeek = {
				start: moment().startOf(unit),
				end: moment().endOf(unit),
				count: 0,
			};

			setMobile(isMobile);
			setWeek(currentWeek);

			setDays(
				new Array(isMobile ? 1 : 5)
					.fill(null)
					.map(() => []) as PlanningEvent[][]
			);
		}

		window.addEventListener("resize", handleResize);
		handleResize();

		updatePlanning(currentWeek, isMobile);

		return () => window.removeEventListener("resize", handleResize);
	}, []);

	return (
		<React.Fragment>
			<motion.section
				initial={{ y: -50, opacity: 0 }}
				animate={{ y: 0, opacity: 1 }}
				exit={{ y: -50, opacity: 0 }}
				transition={{
					ease: "easeInOut",
					duration: 0.5,
				}}
				className={style.buttons}
			>
				<article className={style.selectionBar}>
					<button
						className={style.selected}
						onClick={() => setPromosVisible(!promosVisible)}
					>
						<p>{promo?.name}</p>
						<MdOutlineArrowDropUp
							className={
								promosVisible ? style.unfoldIconUp : style.unfoldIconDown
							}
						/>
					</button>
					<div
						className={style.choices}
						style={{
							pointerEvents: promosVisible ? "all" : "none",
							opacity: +promosVisible,
							animation: promosVisible
								? "showDown 200ms ease"
								: "hideUp 200ms ease",
						}}
					>
						{promos.map((p, i) => (
							<button
								className={style.choice}
								onClick={() => definePromo(p)}
								key={i}
							>
								{p.name}
							</button>
						))}
					</div>
				</article>
				{groups.length > 1 && (
					<article className={style.selectionBar}>
						<button
							className={style.selected}
							onClick={() => setGroupsVisible(!groupsVisible)}
						>
							<p>Groupe {group}</p>
							<MdOutlineArrowDropUp
								className={
									groupsVisible ? style.unfoldIconUp : style.unfoldIconDown
								}
							/>
						</button>
						<div
							className={style.choices}
							style={{
								pointerEvents: groupsVisible ? "all" : "none",
								opacity: +groupsVisible,
								animation: groupsVisible
									? "showDown 200ms ease"
									: "hideUp 200ms ease",
							}}
						>
							{groups.map((g, i) => (
								<button
									className={style.choice}
									onClick={() => defineGroup(g)}
									key={i}
								>
									Groupe {g}
								</button>
							))}
						</div>
					</article>
				)}
				<article className={style.directions}>
					<button onClick={() => addWeek(-1)} className={style.directionButton}>
						<TbArrowNarrowLeft />
						<p className={style.textContent}>Précédent</p>
					</button>
					<button onClick={() => addWeek(0)} className={style.directionButton}>
						<p className={style.textContent}>Aujourd'hui</p>
					</button>
					<button onClick={() => addWeek(1)} className={style.directionButton}>
						<p className={style.textContent}>Suivant</p>
						<TbArrowNarrowRight />
					</button>
				</article>
			</motion.section>
			<motion.section
				initial={{ y: 50, opacity: 0 }}
				animate={{ y: 0, opacity: 1 }}
				exit={{ y: 50, opacity: 0 }}
				transition={{
					ease: "easeInOut",
					duration: 0.5,
				}}
				className={style.planning}
			>
				{days.map((day, index) => (
					<article className={style.planningDay} key={index}>
						<div className={style.title}>
							{!mobile && <h3 className={style.content}>{Days[index]}</h3>}
							<p className={style.date}>
								{moment(week.start)
									.add(index + (mobile ? 0 : 1), "days")
									.format("DD MMMM")
									.replace(/([a-z]+)/gi, (m) => {
										return Months[m];
									})}
							</p>
							<p className={style.duration}>
								{getDayStart(day)?.format("HH:mm")} -{" "}
								{getDayEnd(day)?.format("HH:mm")}
							</p>
						</div>
						<div className={style.dayEvents}>
							<div className={style.events}>
								{day.map((event: PlanningEvent, i) => (
									<motion.div
										key={i}
										initial={{ y: 10, opacity: 0 }}
										animate={{ y: 0, opacity: 1 }}
										exit={{ y: 10, opacity: 0 }}
										transition={{
											ease: "easeInOut",
											duration: 0.4,
										}}
										className={style.event}
										data-room={event.room}
										style={{
											top: `${
												(event.start.get("hours") +
													event.start.get("minutes") / 60 -
													8) *
												75
											}px`,
											height: `${
												(event.end.diff(event.start, "minutes") / 60) * 75
											}px`,
											borderLeftColor: event.color,
										}}
									>
										<p className={style.interval}>
											{event.start.format("HH:mm")} -{" "}
											{event.end.format("HH:mm")}
										</p>
										<p className={style.title}>{event.title}</p>
										<div className={style.teacher}>
											<FcGraduationCap className={style.teacherIcon} />
											<p className={style.teacherContent}>{event.teacher}</p>
										</div>
									</motion.div>
								))}
							</div>
							<div className={style.hourPlaceholders}>
								{new Array(11).fill(null).map((_, j) => (
									<div
										key={j}
										className={style.dayEvent}
										data-start={8 + j}
										data-end={9 + j}
									/>
								))}
							</div>
						</div>
					</article>
				))}
			</motion.section>
		</React.Fragment>
	);
}