/* eslint-disable @next/next/no-img-element */
import Head from "next/head";
import {BsArrowClockwise, BsChevronLeft, BsChevronRight, BsFileEarmarkText, BsPencil, BsStar} from "react-icons/bs";
import {LinearScale, Chart as ChartJS, CategoryScale, PointElement, LineElement, Legend, Tooltip, LineController} from "chart.js";
import {withIronSessionSsr} from "iron-session/next";
import {sessionOptions} from "@/lib/session";
import {useEffect, useState} from "react";
import useFilterRecordsByUser from "@/hooks/useFilterRecordsByUser";
import {averageScore, totalExamsByModule, groupBySession, groupByModule, timestampToMoment, groupByDate} from "@/utils/stats";
import useUser from "@/hooks/useUser";
import {ToastContainer} from "react-toastify";
import {capitalize, Dictionary} from "lodash";
import {Module} from "@/interfaces";
import ProgressBar from "@/components/Low/ProgressBar";
import Layout from "@/components/High/Layout";
import {calculateAverageLevel, calculateBandScore} from "@/utils/score";
import {countExamModules, countFullExams, MODULE_ARRAY, sortByModule} from "@/utils/moduleUtils";
import {Chart} from "react-chartjs-2";
import useUsers from "@/hooks/useUsers";
import useGroups from "@/hooks/useGroups";
import DatePicker from "react-datepicker";
import {shouldRedirectHome} from "@/utils/navigation.disabled";
import ProfileSummary from "@/components/ProfileSummary";
import moment from "moment";
import {Group, Stat, User} from "@/interfaces/user";
import {Divider} from "primereact/divider";
import Badge from "@/components/Low/Badge";
import { mapBy, serialize } from "@/utils";
import { getEntitiesWithRoles } from "@/utils/entities.be";
import { checkAccess } from "@/utils/permissions";
import { getEntitiesUsers, getUsers } from "@/utils/users.be";
import { EntityWithRoles } from "@/interfaces/entity";
import { getGroups, getGroupsByEntities } from "@/utils/groups.be";
import Select from "@/components/Low/Select";

ChartJS.register(LinearScale, CategoryScale, PointElement, LineElement, LineController, Legend, Tooltip);

const COLORS = ["#1EB3FF", "#FF790A", "#3D9F11", "#EF5DA8", "#414288"];

export const getServerSideProps = withIronSessionSsr(async ({req, res}) => {
	const user = req.session.user as User;

	if (!user) {
		return {
			redirect: {
				destination: "/login",
				permanent: false,
			},
		};
	}

	if (shouldRedirectHome(user)) {
		return {
			redirect: {
				destination: "/",
				permanent: false,
			},
		};
	}

	const entityIDs = mapBy(user.entities, 'id')
	const entities = await getEntitiesWithRoles(checkAccess(user, ["admin", "developer"]) ? undefined : entityIDs)
	const users = await (checkAccess(user, ["admin", "developer"]) ? getUsers() : getEntitiesUsers(mapBy(entities, 'id')))
	const groups = await (checkAccess(user, ["admin", "developer"]) ? getGroups() : getGroupsByEntities(mapBy(entities, 'id')))

	return {
		props: serialize({user, entities, users, groups}),
	};
}, sessionOptions);

interface Props {
	user: User
	users: User[]
	entities: EntityWithRoles[]
	groups: Group[]
}

export default function Stats({ user, entities, users, groups }: Props) {
	const [statsUserId, setStatsUserId] = useState<string>(user.id);
	const [startDate, setStartDate] = useState<Date | null>(moment(new Date()).subtract(1, "weeks").toDate());
	const [endDate, setEndDate] = useState<Date | null>(new Date());
	const [initialStatDate, setInitialStatDate] = useState<Date>();

	const [monthlyOverallScoreDate, setMonthlyOverallScoreDate] = useState<Date | null>(new Date());
	const [monthlyModuleScoreDate, setMonthlyModuleScoreDate] = useState<Date | null>(new Date());

	const [dailyScoreDate, setDailyScoreDate] = useState<Date | null>(new Date());
	const [intervalDates, setIntervalDates] = useState<Date[]>([]);

	const {data: stats} = useFilterRecordsByUser<Stat[]>(statsUserId, !statsUserId);

	useEffect(() => {
		setInitialStatDate(
			stats
				.filter((s) => s.date)
				.sort((a, b) => timestampToMoment(a).diff(timestampToMoment(b)))
				.map(timestampToMoment)
				.shift()
				?.toDate(),
		);
	}, [stats]);

	const calculateModuleScore = (stats: Stat[]) => {
		const moduleStats = groupByModule(stats);
		return Object.keys(moduleStats).map((y) => {
			const correct = moduleStats[y].reduce((accumulator, current) => accumulator + current.score.correct, 0);
			const total = moduleStats[y].reduce((accumulator, current) => accumulator + current.score.total, 0);

			return {
				module: y as Module,
				score: calculateBandScore(correct, total, y as Module, user?.focus || "academic"),
			};
		});
	};

	const calculateModularScorePerSession = (stats: Stat[], module: Module) => {
		const groupedBySession = groupBySession(stats);
		const sessionAverage = Object.keys(groupedBySession).map((x: string) => {
			const session = groupedBySession[x];
			const moduleStats = groupByModule(session);
			if (!Object.keys(moduleStats).includes(module)) return null;
			const correct = moduleStats[module].reduce((acc, curr) => acc + curr.score.correct, 0);
			const total = moduleStats[module].reduce((acc, curr) => acc + curr.score.total, 0);

			return calculateBandScore(correct, total, module, user?.focus || "academic");
		});

		return sessionAverage;
	};

	const getListOfDateInInterval = (start: Date, end: Date) => {
		let currentDate = moment(start);
		const dates = [currentDate.toDate()];
		while (moment(end).diff(currentDate, "days") > 0) {
			currentDate = currentDate.add(1, "days");
			dates.push(currentDate.toDate());
		}

		return dates;
	};

	useEffect(() => {
		if (startDate && endDate) {
			setIntervalDates(getListOfDateInInterval(startDate, endDate));
		}
	}, [startDate, endDate]);

	const calculateTotalScore = (stats: Stat[], divisionFactor: number) => {
		const moduleScores = calculateModuleScore(stats);
		return moduleScores.reduce((acc, curr) => acc + curr.score, 0) / divisionFactor;
	};

	const calculateScorePerModule = (stats: Stat[], module: Module) => {
		const moduleScores = calculateModuleScore(stats);
		return moduleScores.find((x) => x.module === module)?.score || -1;
	};

	return (
		<>
			<Head>
				<title>Stats | EnCoach</title>
				<meta
					name="description"
					content="A training platform for the IELTS exam provided by the Muscat Training Institute and developed by eCrop."
				/>
				<meta name="viewport" content="width=device-width, initial-scale=1" />
				<link rel="icon" href="/favicon.ico" />
			</Head>
			<ToastContainer />
			{user && (
				<Layout user={user} className="gap-8">
					<ProfileSummary
						user={users.find((x) => x.id === statsUserId) || user}
						items={[
							{
								icon: <BsFileEarmarkText className="w-6 h-6 md:w-8 md:h-8 text-mti-red-light" />,
								value: countFullExams(stats),
								label: "Exams",
								tooltip: "Number of all conducted completed exams",
							},
							{
								icon: <BsPencil className="w-6 h-6 md:w-8 md:h-8 text-mti-red-light" />,
								value: countExamModules(stats),
								label: "Modules",
								tooltip: "Number of all exam modules performed including Level Test",
							},
							{
								icon: <BsStar className="w-6 h-6 md:w-8 md:h-8 text-mti-red-light" />,
								value: `${stats.length > 0 ? averageScore(stats) : 0}%`,
								label: "Average Score",
								tooltip: "Average success rate for questions responded",
							},
						]}
					/>

					<section className="flex flex-col gap-3">
						<div className="w-full flex justify-between gap-4 items-center">
							<>
								{(user.type === "developer" || user.type === "admin") && (
									<Select
										className="w-full"
										options={users.map((x) => ({value: x.id, label: `${x.name} - ${x.email}`}))}
										defaultValue={{value: user.id, label: `${user.name} - ${user.email}`}}
										onChange={(value) => setStatsUserId(value?.value || user.id)}
									/>
								)}
								{["corporate", "teacher", "mastercorporate"].includes(user.type) && groups.length > 0 && (
									<Select
										className="w-full"
										options={users
											.filter((x) => groups.flatMap((y) => y.participants).includes(x.id))
											.map((x) => ({value: x.id, label: `${x.name} - ${x.email}`}))}
										defaultValue={{value: user.id, label: `${user.name} - ${user.email}`}}
										onChange={(value) => setStatsUserId(value?.value || user.id)}
									/>
								)}
							</>
						</div>

						{stats.length > 0 && (
							<>
								<div className="flex -md:flex-col -md:items-center gap-4 flex-wrap">
									{/* Overall Level per Month */}
									<div className="flex flex-col items-center gap-4 border w-full h-[420px] overflow-y-scroll scrollbar-hide md:max-w-sm border-mti-gray-platinum p-4 pb-12 rounded-xl">
										<div className="flex flex-col gap-2 w-full">
											<span className="text-sm font-bold">Overall Level per Month</span>
											<div className="flex gap-2 items-center">
												{monthlyOverallScoreDate && (
													<button
														onClick={() =>
															setMonthlyOverallScoreDate((prev) => moment(prev).subtract(1, "months").toDate())
														}>
														<BsChevronLeft />
													</button>
												)}
												<DatePicker
													dateFormat="MMMM yyyy"
													className="border border-mti-gray-dim/40 px-2 py-1.5 rounded-lg text-center w-[200px]"
													minDate={initialStatDate}
													maxDate={new Date()}
													selected={monthlyOverallScoreDate}
													showMonthYearPicker
													onChange={setMonthlyOverallScoreDate}
												/>
												{monthlyOverallScoreDate && (
													<button
														disabled={moment(monthlyOverallScoreDate).add(1, "months").isAfter(moment())}
														onClick={() => setMonthlyOverallScoreDate((prev) => moment(prev).add(1, "months").toDate())}
														className="disabled:text-neutral-200">
														<BsChevronRight />
													</button>
												)}
												<button onClick={() => setMonthlyOverallScoreDate(new Date())}>
													<BsArrowClockwise />
												</button>
											</div>
										</div>
										<div className="w-full grid grid-cols-3 gap-4 items-center">
											{[...Array(31).keys()].map((day) => {
												const date = moment(
													`${(day + 1).toString().padStart(2, "0")}/${
														moment(monthlyOverallScoreDate).get("month") + 1
													}/${moment(monthlyOverallScoreDate).get("year")}`,
													"DD/MM/yyyy",
												);

												return date.isValid() && date.isSameOrBefore(moment()) ? (
													<div
														key={day}
														className="flex flex-col gap-1 items-start border border-mti-gray-smoke rounded-lg overflow-hidden">
														<span className="bg-mti-purple-ultralight w-full px-2 py-1 font-semibold">
															Day {(day + 1).toString().padStart(2, "0")}
														</span>
														<span className="px-2">
															Level{" "}
															{calculateTotalScore(
																stats.filter((s) => timestampToMoment(s).isBefore(date)),
																5,
															).toFixed(1)}
														</span>
													</div>
												) : null;
											})}
										</div>
									</div>

									{/* Overall Level per Month Graph */}
									<div className="w-full md:max-w-2xl border border-mti-gray-platinum p-4 pb-12 rounded-xl h-fit md:h-[420px]">
										<div className="flex flex-col gap-2 w-full">
											<span className="text-sm font-bold">Overall Level per Month</span>
											<div className="flex gap-2 items-center">
												{monthlyOverallScoreDate && (
													<button
														onClick={() =>
															setMonthlyOverallScoreDate((prev) => moment(prev).subtract(1, "months").toDate())
														}>
														<BsChevronLeft />
													</button>
												)}
												<DatePicker
													dateFormat="MMMM yyyy"
													className="border border-mti-gray-dim/40 px-2 py-1.5 rounded-lg text-center w-[200px]"
													minDate={initialStatDate}
													maxDate={new Date()}
													selected={monthlyOverallScoreDate}
													showMonthYearPicker
													onChange={setMonthlyOverallScoreDate}
												/>
												{monthlyOverallScoreDate && (
													<button
														disabled={moment(monthlyOverallScoreDate).add(1, "months").isAfter(moment())}
														onClick={() => setMonthlyOverallScoreDate((prev) => moment(prev).add(1, "months").toDate())}
														className="disabled:text-neutral-200">
														<BsChevronRight />
													</button>
												)}
												<button onClick={() => setMonthlyOverallScoreDate(new Date())}>
													<BsArrowClockwise />
												</button>
											</div>
										</div>
										<Chart
											type="line"
											options={{
												scales: {
													y: {
														min: 0,
														max: 9,
													},
												},
											}}
											data={{
												labels: [...Array(31).keys()]
													.map((day) => {
														const date = moment(
															`${(day + 1).toString().padStart(2, "0")}/${
																moment(monthlyOverallScoreDate).get("month") + 1
															}/${moment(monthlyOverallScoreDate).get("year")}`,
															"DD/MM/yyyy",
														);
														return date.isValid() ? (day + 1).toString().padStart(2, "0") : undefined;
													})
													.filter((x) => !!x),
												datasets: [
													{
														type: "line",
														label: "Total",
														fill: false,
														borderColor: "#6A5FB1",
														backgroundColor: "#7872BF",
														borderWidth: 2,
														spanGaps: true,
														data: [...Array(31).keys()]
															.map((day) => {
																const date = moment(
																	`${(day + 1).toString().padStart(2, "0")}/${
																		moment(monthlyOverallScoreDate).get("month") + 1
																	}/${moment(monthlyOverallScoreDate).get("year")}`,
																	"DD/MM/yyyy",
																);

																return date.isValid()
																	? calculateTotalScore(
																			stats.filter((s) => timestampToMoment(s).isBefore(date)),
																			5,
																	  ).toFixed(1)
																	: undefined;
															})
															.filter((x) => !!x),
													},
												],
											}}
										/>
									</div>

									{/* Module Level per Day */}
									<div className="flex flex-col gap-8 border w-full h-fit md:h-[420px] md:max-w-xs border-mti-gray-platinum p-4 pb-12 rounded-xl">
										<div className="flex flex-col gap-2 w-full">
											<span className="text-sm font-bold">Module Level per Day</span>
											<div className="flex gap-2 items-center">
												{monthlyModuleScoreDate && (
													<button
														onClick={() =>
															setMonthlyModuleScoreDate((prev) => moment(prev).subtract(1, "days").toDate())
														}>
														<BsChevronLeft />
													</button>
												)}
												<DatePicker
													dateFormat="dd MMMM yyyy"
													className="border border-mti-gray-dim/40 px-2 py-1.5 rounded-lg text-center w-[200px]"
													minDate={initialStatDate}
													maxDate={new Date()}
													selected={monthlyModuleScoreDate}
													onChange={setMonthlyModuleScoreDate}
												/>
												{monthlyModuleScoreDate && (
													<button
														disabled={moment(monthlyModuleScoreDate).add(1, "days").isAfter(moment())}
														onClick={() => setMonthlyModuleScoreDate((prev) => moment(prev).add(1, "days").toDate())}
														className="disabled:text-neutral-200">
														<BsChevronRight />
													</button>
												)}
												<button onClick={() => setMonthlyModuleScoreDate(new Date())}>
													<BsArrowClockwise />
												</button>
											</div>
										</div>
										<div className="flex flex-col gap-4">
											{calculateModuleScore(stats.filter((s) => timestampToMoment(s).isBefore(moment(monthlyModuleScoreDate))))
												.sort(sortByModule)
												.map(({module, score}) => (
													<div className="flex flex-col gap-2" key={module}>
														<div className="flex justify-between items-end">
															<span className="text-xs">
																<span className="font-medium">{score}</span> of <span className="font-medium">9</span>
															</span>
															<span className="text-xs">{capitalize(module)}</span>
														</div>
														<ProgressBar
															color={module as Module}
															percentage={(score * 100) / 9}
															label=""
															className="h-3"
														/>
													</div>
												))}
										</div>
									</div>
								</div>

								<Divider />

								<div className="flex -md:flex-col -md:items-center gap-4 flex-wrap">
									{/* Module Level per Exam */}
									<div className="flex flex-col items-center gap-4 border w-full h-[420px] overflow-y-scroll scrollbar-hide md:max-w-sm border-mti-gray-platinum p-4 pb-12 rounded-xl">
										<div className="flex flex-col gap-2 w-full">
											<span className="text-sm font-bold">Module Level per Exam</span>
											<div className="flex gap-2 items-center">
												{dailyScoreDate && (
													<button onClick={() => setDailyScoreDate((prev) => moment(prev).subtract(1, "days").toDate())}>
														<BsChevronLeft />
													</button>
												)}
												<DatePicker
													dateFormat="dd MMMM yyyy"
													className="border border-mti-gray-dim/40 px-2 py-1.5 rounded-lg text-center w-[200px]"
													minDate={initialStatDate}
													maxDate={new Date()}
													selected={dailyScoreDate}
													onChange={setDailyScoreDate}
												/>
												{dailyScoreDate && (
													<button
														disabled={moment(dailyScoreDate).add(1, "days").isAfter(moment())}
														onClick={() => setDailyScoreDate((prev) => moment(prev).add(1, "days").toDate())}
														className="disabled:text-neutral-200">
														<BsChevronRight />
													</button>
												)}
												<button onClick={() => setDailyScoreDate(new Date())}>
													<BsArrowClockwise />
												</button>
											</div>
										</div>

										<div className="w-full grid grid-cols-1 gap-6 items-center">
											{Object.keys(
												groupBySession(
													stats.filter(
														(s) =>
															Math.abs(timestampToMoment(s).diff(moment(dailyScoreDate), "days")) === 0 &&
															timestampToMoment(s).day() === moment(dailyScoreDate).day(),
													),
												),
											).length === 0 && <span className="font-semibold ml-1">No exams performed this day...</span>}

											{Object.keys(
												groupBySession(
													stats.filter(
														(s) =>
															Math.abs(timestampToMoment(s).diff(moment(dailyScoreDate), "days")) === 0 &&
															timestampToMoment(s).day() === moment(dailyScoreDate).day(),
													),
												),
											).map((session, index) => (
												<div key={index} className="flex flex-col gap-2 items-start rounded-lg overflow-hidden">
													<span className="bg-mti-purple-ultralight w-full px-2 py-1 font-semibold">
														Exam {(index + 1).toString().padStart(2, "0")}
													</span>
													<div className="flex justify-between w-full">
														{MODULE_ARRAY.map((module) => {
															const score = calculateScorePerModule(
																groupBySession(
																	stats.filter(
																		(s) =>
																			Math.abs(timestampToMoment(s).diff(moment(dailyScoreDate), "days")) === 0,
																	),
																)[session],
																module,
															);

															return score === -1 ? null : <Badge module={module}>{score.toFixed(1)}</Badge>;
														}).filter((m) => !!m)}
													</div>
												</div>
											))}
										</div>
									</div>

									<div className="w-full md:max-w-2xl border border-mti-gray-platinum p-4 pb-12 rounded-xl h-[420px]">
										<div className="flex flex-col gap-2 w-full mb-2">
											<span className="text-sm font-bold">Module Level per Exam</span>
											<div className="flex gap-2 items-center">
												{dailyScoreDate && (
													<button onClick={() => setDailyScoreDate((prev) => moment(prev).subtract(1, "days").toDate())}>
														<BsChevronLeft />
													</button>
												)}
												<DatePicker
													dateFormat="dd MMMM yyyy"
													className="border border-mti-gray-dim/40 px-2 py-1.5 rounded-lg text-center w-[200px]"
													minDate={initialStatDate}
													maxDate={new Date()}
													selected={dailyScoreDate}
													onChange={setDailyScoreDate}
												/>
												{dailyScoreDate && (
													<button
														disabled={moment(dailyScoreDate).add(1, "days").isAfter(moment())}
														onClick={() => setDailyScoreDate((prev) => moment(prev).add(1, "days").toDate())}
														className="disabled:text-neutral-200">
														<BsChevronRight />
													</button>
												)}
												<button onClick={() => setDailyScoreDate(new Date())}>
													<BsArrowClockwise />
												</button>
											</div>
										</div>
										<Chart
											type="line"
											options={{
												scales: {
													y: {
														min: 0,
														max: 9,
													},
												},
											}}
											data={{
												labels: Object.keys(
													groupBySession(
														stats.filter(
															(s) =>
																Math.abs(timestampToMoment(s).diff(moment(dailyScoreDate), "days")) === 0 &&
																timestampToMoment(s).day() === moment(dailyScoreDate).day(),
														),
													),
												).map((_, index) => `Exam ${(index + 1).toString().padStart(2, "0")}`),
												datasets: [
													...MODULE_ARRAY.map((module, index) => ({
														type: "line" as const,
														label: capitalize(module),
														borderColor: COLORS[index],
														backgroundColor: COLORS[index],
														borderWidth: 2,
														data: calculateModularScorePerSession(
															stats.filter(
																(s) =>
																	Math.abs(timestampToMoment(s).diff(moment(dailyScoreDate), "days")) === 0 &&
																	timestampToMoment(s).day() === moment(dailyScoreDate).day(),
															),
															module,
														),
													})),
												],
											}}
										/>
									</div>
								</div>

								<Divider />

								<div className="w-full flex flex-col gap-4">
									<DatePicker
										dateFormat="dd/MM/yyyy"
										className="border border-mti-gray-dim/40 px-4 py-2 rounded-lg text-center w-80"
										startDate={startDate}
										endDate={endDate}
										selectsRange
										showMonthDropdown
										filterDate={(date) => moment(date).isSameOrBefore(moment(new Date()))}
										onChange={([initialDate, finalDate]) => {
											setStartDate(initialDate);
											setEndDate(finalDate);
										}}
									/>
									<div className="flex -md:flex-col -md:items-center gap-4 flex-wrap">
										{/* Module Score Band in Interval */}
										{MODULE_ARRAY.map((module, index) => (
											<div
												className="w-full md:max-w-2xl border border-mti-gray-platinum p-4 pb-12 rounded-xl h-fit md:h-96"
												key={module}>
												<span className="text-sm font-bold">{capitalize(module)} Score Band in Interval</span>
												<Chart
													options={{
														scales: {
															y: {
																min: 0,
																max: 9,
															},
														},
													}}
													type="line"
													data={{
														labels: intervalDates.map((date) => moment(date).format("DD/MM/YYYY")),
														datasets: [
															{
																type: "line",
																label: capitalize(module),
																fill: false,
																borderColor: COLORS[index],
																backgroundColor: COLORS[index],
																borderWidth: 2,
																spanGaps: true,
																data: intervalDates.map((date) => {
																	return calculateTotalScore(
																		stats.filter(
																			(s) => timestampToMoment(s).isBefore(date) && s.module === module,
																		),
																		1,
																	).toFixed(1);
																}),
															},
														],
													}}
												/>
											</div>
										))}
									</div>
								</div>
							</>
						)}
					</section>
					{stats.length === 0 && (
						<section className="flex flex-col gap-3">
							<span className="font-semibold ml-1">No stats to display...</span>
						</section>
					)}
				</Layout>
			)}
		</>
	);
}
