/* eslint-disable @next/next/no-img-element */
import Layout from "@/components/High/Layout";
import Table from "@/components/High/Table";
import Checkbox from "@/components/Low/Checkbox";
import Separator from "@/components/Low/Separator";
import { useAllowedEntities } from "@/hooks/useEntityPermissions";
import { Session } from "@/hooks/useSessions";
import { Entity, EntityWithRoles } from "@/interfaces/entity";
import { Exam } from "@/interfaces/exam";
import { Assignment, AssignmentResult } from "@/interfaces/results";
import { Group, Stat, StudentUser, User } from "@/interfaces/user";
import { sessionOptions } from "@/lib/session";
import { filterBy, findBy, mapBy, redirect, serialize } from "@/utils";
import { requestUser } from "@/utils/api";
import { getEntitiesAssignments } from "@/utils/assignments.be";
import { getEntitiesWithRoles } from "@/utils/entities.be";
import { getExamsByIds } from "@/utils/exams.be";
import { checkAccess, findAllowedEntities } from "@/utils/permissions";
import { getSessionsByAssignments, getSessionsByUser } from "@/utils/sessions.be";
import { getStatsByUsers } from "@/utils/stats.be";
import { isAdmin } from "@/utils/users";
import { getEntitiesUsers } from "@/utils/users.be";
import { createColumnHelper } from "@tanstack/react-table";
import axios from "axios";
import { clsx } from "clsx";
import { withIronSessionSsr } from "iron-session/next";
import { capitalize, orderBy } from "lodash";
import moment from "moment";
import Head from "next/head";
import Link from "next/link";
import { useEffect, useMemo, useState } from "react";
import ReactDatePicker from "react-datepicker";
import {
	BsBank,
	BsChevronLeft,
	BsX,
} from "react-icons/bs";

interface Props {
	user: User;
	students: StudentUser[];
	entities: EntityWithRoles[];
	assignments: Assignment[];
	sessions: Session[]
	exams: Exam[]
}

export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => {
	const user = await requestUser(req, res)
	if (!user) return redirect("/login")

	if (!checkAccess(user, ["admin", "developer", "mastercorporate"]))
		return redirect("/")

	const entityIDS = mapBy(user.entities, "id") || [];
	const entities = await getEntitiesWithRoles(isAdmin(user) ? undefined : entityIDS);
	const allowedEntities = findAllowedEntities(user, entities, 'view_entity_statistics')

	if (allowedEntities.length === 0) return redirect("/")

	const studentsAllowedEntities = findAllowedEntities(user, entities, 'view_students')
	const students = await getEntitiesUsers(mapBy(studentsAllowedEntities, 'id'), { type: "student" })

	const assignments = await getEntitiesAssignments(mapBy(entities, "id"));
	const sessions = await getSessionsByAssignments(mapBy(assignments, 'id'))
	const exams = await getExamsByIds(assignments.flatMap(a => a.exams))

	return { props: serialize({ user, students, entities: allowedEntities, assignments, sessions, exams }) };
}, sessionOptions);

interface Item {
	student: StudentUser
	result?: AssignmentResult
	assignment: Assignment
	exams: Exam[]
	entity: Entity
	session?: Session
}

const columnHelper = createColumnHelper<Item>();

export default function Statistical({ user, students, entities, assignments, sessions, exams }: Props) {
	const [startDate, setStartDate] = useState<Date>(new Date());
	const [endDate, setEndDate] = useState<Date | null>(moment().add(1, 'month').toDate());
	const [selectedEntities, setSelectedEntities] = useState<string[]>([])
	const [isDownloading, setIsDownloading] = useState(false)

	const entitiesAllowDownload = useAllowedEntities(user, entities, 'download_statistics_report')

	const resetDateRange = () => {
		const orderedAssignments = orderBy(assignments, ['startDate'], ['asc'])
		setStartDate(moment(orderedAssignments.shift()?.startDate || "2024-01-01T00:00:01.986Z").toDate())
		setEndDate(moment().add(1, 'month').toDate())
	}

	useEffect(resetDateRange, [assignments])

	const updateDateRange = (dates: [Date, Date | null]) => {
		const [start, end] = dates;
		setStartDate(start!);
		setEndDate(end);
	};

	const toggleEntity = (id: string) => setSelectedEntities(prev => prev.includes(id) ? prev.filter(x => x !== id) : [...prev, id])

	const renderAssignmentResolution = (entityID: string) => {
		const entityAssignments = filterBy(assignments, 'entity', entityID)
		const total = entityAssignments.reduce((acc, curr) => acc + curr.assignees.length, 0)
		const results = entityAssignments.reduce((acc, curr) => acc + curr.results.length, 0)

		return `${results}/${total}`
	}

	const totalAssignmentResolution = useMemo(() => {
		const total = assignments.reduce((acc, curr) => acc + curr.assignees.length, 0)
		const results = assignments.reduce((acc, curr) => acc + curr.results.length, 0)

		return { results, total }
	}, [assignments])

	const filteredAssignments = useMemo(() => {
		if (!startDate && !endDate) return assignments
		const startDateFiltered = startDate ? assignments.filter(a => moment(a.startDate).isSameOrAfter(moment(startDate))) : assignments
		return endDate ? startDateFiltered.filter(a => moment(a.endDate).isSameOrBefore(moment(endDate))) : startDateFiltered
	}, [startDate, endDate, assignments])

	const data: Item[] = useMemo(() =>
		filteredAssignments.filter(a => selectedEntities.includes(a.entity || "")).flatMap(a => a.assignees.map(x => {
			const result = findBy(a.results, 'user', x)
			const student = findBy(students, 'id', x)
			const entity = findBy(entities, 'id', a.entity)
			const assignmentExams = exams.filter(e => a.exams.map(x => `${x.id}_${x.module}`).includes(`${e.id}_${e.module}`))
			const session = sessions.find(s => s.assignment?.id === a.id && s.user === x)

			if (!student) return undefined
			return { student, result, assignment: a, exams: assignmentExams, session, entity }
		})).filter(x => !!x) as Item[],
		[students, selectedEntities, filteredAssignments, exams, sessions, entities]
	)

	const sortedData: Item[] = useMemo(() => data.sort((a, b) => {
		const aTotalScore = a.result?.stats.filter(x => !x.isPractice).reduce((acc, curr) => acc + curr.score.correct, 0) || 0
		const bTotalScore = b.result?.stats.filter(x => !x.isPractice).reduce((acc, curr) => acc + curr.score.correct, 0) || 0

		return bTotalScore - aTotalScore
	}), [data])

	const downloadExcel = async () => {
		setIsDownloading(true)

		const request = await axios.post("/api/statistical", {
			entities: entities.filter(e => selectedEntities.includes(e.id)),
			items: data,
			assignments: filteredAssignments,
			startDate,
			endDate
		}, {
			responseType: 'blob'
		})

		const href = URL.createObjectURL(request.data)
		const link = document.createElement('a');
		link.href = href;
		link.setAttribute('download', `statistical_${new Date().toISOString()}.xlsx`);
		document.body.appendChild(link);
		link.click();

		document.body.removeChild(link);
		URL.revokeObjectURL(href);

		setIsDownloading(false)
	}

	const columns = [
		columnHelper.accessor("student.name", {
			header: "Student",
			cell: (info) => info.getValue(),
		}),
		columnHelper.accessor("student.studentID", {
			header: "Student ID",
			cell: (info) => info.getValue(),
		}),
		columnHelper.accessor("student.email", {
			header: "E-mail",
			cell: (info) => info.getValue(),
		}),
		columnHelper.accessor("entity.label", {
			header: "Entity",
			cell: (info) => info.getValue(),
		}),
		columnHelper.accessor("assignment.name", {
			header: "Assignment",
			cell: (info) => info.getValue(),
		}),
		columnHelper.accessor("assignment.startDate", {
			header: "Date",
			cell: (info) => moment(info.getValue()).format("DD/MM/YYYY"),
		}),
		columnHelper.accessor("result", {
			header: "Progress",
			cell: (info) => {
				const student = info.row.original.student
				const session = info.row.original.session

				if (!student.lastLogin) return <span className="text-mti-red-dark">Never logged in</span>
				if (info.getValue()) return <span className="text-mti-green font-semibold">Submitted</span>
				if (!session) return <span className="text-mti-rose">Not started</span>

				return <span className="font-semibold">
					{capitalize(session.exam?.module || "")} Module, Part {session.partIndex + 1}, Exercise {session.exerciseIndex + 1}
				</span>
			},
		})
	]

	return (
		<>
			<Head>
				<title>Statistical | 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>

			<Layout user={user}>
				<div className="flex flex-col gap-4">
					<div className="flex items-center justify-between w-full">
						<div className="flex items-center gap-2">
							<Link href="/dashboard" className="text-mti-purple hover:text-mti-purple-dark transition ease-in-out duration-300 text-xl">
								<BsChevronLeft />
							</Link>
							<h2 className="font-bold text-2xl">Statistical</h2>
						</div>
						<Checkbox
							onChange={value => setSelectedEntities(value ? mapBy(entities, 'id') : [])}
							isChecked={selectedEntities.length === entities.length}
						>
							Select All
						</Checkbox>
					</div>
					<Separator />
				</div>

				<section className="flex flex-col gap-3">
					<div className="w-full flex items-center justify-between gap-4 flex-wrap">
						{entities.map(entity => (
							<button
								onClick={() => toggleEntity(entity.id)}
								className={clsx(
									"flex flex-col items-center justify-between gap-3 border-2 drop-shadow rounded-xl bg-white p-8 px-2 w-48 h-52",
									"transition ease-in-out duration-300 hover:shadow-xl hover:border-mti-purple",
									selectedEntities.includes(entity.id) && "border-mti-purple text-mti-purple"
								)}
								key={entity.id}
							>
								<BsBank size={48} />
								<div className="flex flex-col gap-1">
									<span>{entity.label}</span>
									<span className={clsx("font-semibold")}>
										{renderAssignmentResolution(entity.id)}
									</span>
								</div>
							</button>
						))}
					</div>

					<div className="flex items-center justify-between">
						<div className="flex items-center gap-2">
							<ReactDatePicker
								className={clsx(
									"p-6 px-12 w-full flex justify-center text-sm font-normal rounded-full border focus:outline-none cursor-pointer",
									"hover:border-mti-purple tooltip",
									"transition duration-300 ease-in-out",
								)}
								dateFormat="dd/MM/yyyy"
								selectsRange
								selected={startDate}
								onChange={updateDateRange}
								startDate={startDate}
								endDate={endDate}
							/>
							{startDate !== null && endDate !== null && (
								<button onClick={resetDateRange} className="transition ease-in-out duration-300 rounded-full p-2 hover:bg-mti-gray-cool/10">
									<BsX size={24} />
								</button>
							)}
						</div>
						<span className="font-semibold text-lg pr-1">
							Total: {totalAssignmentResolution.results} / {totalAssignmentResolution.total}
						</span>
					</div>
				</section>

				{selectedEntities.length > 0 && (
					<Table
						columns={columns}
						data={sortedData}
						searchFields={[["student", "name"], ["student", "email"], ["student", "studentID"], ["exams", "id"], ["assignment", "name"]]}
						searchPlaceholder="Search by student, assignment or exam..."
						onDownload={entitiesAllowDownload.length > 0 ? downloadExcel : undefined}
						isDownloadLoading={isDownloading}
					/>
				)}
			</Layout>
		</>
	)
}
