import React, {useEffect, useMemo, useState} from "react";
import {CorporateUser, StudentUser, User} from "@/interfaces/user";
import {BsFileExcel, BsBank, BsPersonFill} from "react-icons/bs";
import IconCard from "../IconCard";

import useAssignmentsCorporates from "@/hooks/useAssignmentCorporates";
import ReactDatePicker from "react-datepicker";

import moment from "moment";
import {AssignmentWithCorporateId} from "@/interfaces/results";
import {flexRender, createColumnHelper, getCoreRowModel, useReactTable} from "@tanstack/react-table";
import Checkbox from "@/components/Low/Checkbox";
import {useListSearch} from "@/hooks/useListSearch";
import axios from "axios";
import {toast} from "react-toastify";
import Button from "@/components/Low/Button";
import {getUserName} from "@/utils/users";

interface GroupedCorporateUsers {
	// list of user Ids
	[key: string]: string[];
}

interface Props {
	corporateUsers: GroupedCorporateUsers;
	users: User[];
	displaySelection?: boolean;
}

interface TableData {
	user: User | undefined;
	email: string;
	correct: number;
	corporate: string;
	submitted: boolean;
	date: moment.Moment;
	assignment: string;
	corporateId: string;
}

interface UserCount {
	userCount: number;
	maxUserCount: number;
}

const searchFilters = [["email"], ["user"], ["userId"], ["exams"], ["assignment"]];

const SIZE = 16;

const MasterStatistical = (props: Props) => {
	const {users, corporateUsers, displaySelection = true} = props;
	const [page, setPage] = useState(0);

	// const corporateRelevantUsers = React.useMemo(
	//   () => corporateUsers.filter((x) => x.type !== "student") as CorporateUser[],
	//   [corporateUsers]
	// );

	const corporates = React.useMemo(() => Object.values(corporateUsers).flat(), [corporateUsers]);

	const [selectedCorporates, setSelectedCorporates] = React.useState<string[]>(corporates);
	const [startDate, setStartDate] = React.useState<Date | null>(moment("01/01/2023").toDate());
	const [endDate, setEndDate] = React.useState<Date | null>(moment().endOf("year").toDate());

	const {assignments, isLoading} = useAssignmentsCorporates({
		corporates: selectedCorporates,
		startDate,
		endDate,
	});

	const [downloading, setDownloading] = React.useState<boolean>(false);

	const tableResults = React.useMemo(
		() =>
			assignments.reduce((accmA: TableData[], a: AssignmentWithCorporateId) => {
				const userResults = a.assignees.map((assignee) => {
					const userData = users.find((u) => u.id === assignee);
					const userStats = a.results.find((r) => r.user === assignee)?.stats || [];
					const corporate = getUserName(users.find((u) => u.id === a.assigner));
					const commonData = {
						user: userData,
						email: userData?.email || "N/A",
						userId: assignee,
						corporateId: a.corporateId,
						exams: a.exams.map((x) => x.id).join(", "),
						corporate,
						assignment: a.name,
					};

					if (userStats.length === 0) {
						return {
							...commonData,
							correct: 0,
							submitted: false,
							date: null,
						};
					}

					return {
						...commonData,
						correct: userStats.reduce((n, e) => n + e.score.correct, 0),
						submitted: true,
						date: moment.max(userStats.map((e) => moment(e.date))),
					};
				}) as TableData[];

				return [...accmA, ...userResults];
			}, []),
		[assignments, users],
	);

	const getCorporateScores = (corporateId: string): UserCount => {
		const corporateAssignmentsUsers = assignments.filter((a) => a.corporateId === corporateId).reduce((acc, a) => acc + a.assignees.length, 0);

		const corporateResults = tableResults.filter((r) => r.corporateId === corporateId).length;

		return {
			maxUserCount: corporateAssignmentsUsers,
			userCount: corporateResults,
		};
	};

	const getCorporatesScoresHash = (data: string[]) =>
		data.reduce(
			(accm, id) => ({
				...accm,
				[id]: getCorporateScores(id),
			}),
			{},
		) as Record<string, UserCount>;

	const getConsolidateScore = (data: Record<string, UserCount>) =>
		Object.values(data).reduce(
			(acc: UserCount, {userCount, maxUserCount}: UserCount) => ({
				userCount: acc.userCount + userCount,
				maxUserCount: acc.maxUserCount + maxUserCount,
			}),
			{userCount: 0, maxUserCount: 0},
		);

	const corporateScores = getCorporatesScoresHash(corporates);
	const consolidateScore = getConsolidateScore(corporateScores);

	const getConsolidateScoreStr = (data: UserCount) => `${data.userCount}/${data.maxUserCount}`;

	const columnHelper = createColumnHelper<TableData>();

	const defaultColumns = [
		columnHelper.accessor("user", {
			header: "User",
			id: "user",
			cell: (info) => {
				return <span>{info.getValue()?.name || "N/A"}</span>;
			},
		}),
		columnHelper.accessor("email", {
			header: "Email",
			id: "email",
			cell: (info) => {
				return <span>{info.getValue()}</span>;
			},
		}),
		columnHelper.accessor("user", {
			header: "Student ID",
			id: "studentID",
			cell: (info) => {
				return <span>{(info.getValue() as StudentUser)?.studentID || "N/A"}</span>;
			},
		}),
		...(displaySelection
			? [
					columnHelper.accessor("corporate", {
						header: "Corporate",
						id: "corporate",
						cell: (info) => {
							return <span>{info.getValue()}</span>;
						},
					}),
			  ]
			: []),
		columnHelper.accessor("assignment", {
			header: "Assignment",
			id: "assignment",
			cell: (info) => {
				return <span>{info.getValue()}</span>;
			},
		}),
		columnHelper.accessor("submitted", {
			header: "Submitted",
			id: "submitted",
			cell: (info) => {
				return (
					<Checkbox isChecked={info.getValue()} disabled onChange={() => {}}>
						<span></span>
					</Checkbox>
				);
			},
		}),
		columnHelper.accessor("correct", {
			header: "Score",
			id: "correct",
			cell: (info) => {
				return <span>{info.getValue()}</span>;
			},
		}),
		columnHelper.accessor("date", {
			header: "Date",
			id: "date",
			cell: (info) => {
				const date = info.getValue();
				if (date) {
					return <span>{!!date ? date.format("DD/MM/YYYY") : "N/A"}</span>;
				}

				return <span>{""}</span>;
			},
		}),
	];

	const {rows: filteredRows, renderSearch, text: searchText} = useListSearch(searchFilters, tableResults);

	useEffect(() => setPage(0), [searchText]);
	const rows = useMemo(
		() => filteredRows.slice(page * SIZE, (page + 1) * SIZE > filteredRows.length ? filteredRows.length : (page + 1) * SIZE),
		[filteredRows, page],
	);

	const table = useReactTable({
		data: rows,
		columns: defaultColumns,
		getCoreRowModel: getCoreRowModel(),
	});

	const areAllSelected = selectedCorporates.length === corporates.length;

	const getStudentsConsolidateScore = () => {
		if (tableResults.length === 0) {
			return {highest: null, lowest: null};
		}

		// Find the student with the highest and lowest score
		return tableResults.reduce(
			(acc, curr) => {
				if (curr.correct > acc.highest.correct) {
					acc.highest = curr;
				}
				if (curr.correct < acc.lowest.correct) {
					acc.lowest = curr;
				}
				return acc;
			},
			{highest: tableResults[0], lowest: tableResults[0]},
		);
	};

	const triggerDownload = async () => {
		try {
			setDownloading(true);
			const res = await axios.post("/api/assignments/statistical/excel", {
				ids: selectedCorporates,
				...(startDate ? {startDate: startDate.toISOString()} : {}),
				...(endDate ? {endDate: endDate.toISOString()} : {}),
				searchText,
			});
			toast.success("Report ready!");
			const link = document.createElement("a");
			link.href = res.data;
			// download should have worked but there are some CORS issues
			// https://firebase.google.com/docs/storage/web/download-files#cors_configuration
			// link.download="report.pdf";
			link.target = "_blank";
			link.rel = "noreferrer";
			link.click();
			setDownloading(false);
		} catch (err) {
			toast.error("Failed to display the report!");
			console.error(err);
			setDownloading(false);
		}
	};

	const consolidateResults = getStudentsConsolidateScore();
	return (
		<>
			{displaySelection && (
				<div className="flex flex-wrap gap-2 items-center text-center">
					<IconCard
						Icon={BsBank}
						label="Consolidate"
						isLoading={isLoading}
						value={getConsolidateScoreStr(consolidateScore)}
						color="purple"
						onClick={() => {
							if (areAllSelected) {
								setSelectedCorporates([]);
								return;
							}
							setSelectedCorporates(corporates);
						}}
						isSelected={areAllSelected}
					/>
					{Object.keys(corporateUsers).map((corporateName) => {
						const group = corporateUsers[corporateName];
						const isSelected = group.every((id) => selectedCorporates.includes(id));

						const valueHash = getCorporatesScoresHash(group);
						const value = getConsolidateScoreStr(getConsolidateScore(valueHash));
						return (
							<IconCard
								key={corporateName}
								Icon={BsBank}
								isLoading={isLoading}
								label={corporateName}
								value={value}
								color="purple"
								onClick={() => {
									if (isSelected) {
										setSelectedCorporates((prev) => prev.filter((x) => !group.includes(x)));
										return;
									}
									setSelectedCorporates((prev) => [...new Set([...prev, ...group])]);
								}}
								isSelected={isSelected}
							/>
						);
					})}
				</div>
			)}
			<div className="flex gap-3 w-full">
				<div className="flex flex-col gap-3">
					<label className="font-normal text-base text-mti-gray-dim">Date</label>
					<ReactDatePicker
						dateFormat="dd/MM/yyyy"
						className="px-4 py-6 w-52 text-sm text-center font-normal placeholder:text-mti-gray-cool disabled:bg-mti-gray-platinum/40 disabled:text-mti-gray-dim disabled:cursor-not-allowed rounded-full border border-mti-gray-platinum focus:outline-none"
						selected={startDate}
						startDate={startDate}
						endDate={endDate}
						selectsRange
						showMonthDropdown
						onChange={([initialDate, finalDate]: [Date, Date]) => {
							setStartDate(initialDate ?? moment("01/01/2023").toDate());
							if (finalDate) {
								// basicly selecting a final day works as if I'm selecting the first
								// minute of that day. this way it covers the whole day
								setEndDate(moment(finalDate).endOf("day").toDate());
								return;
							}
							setEndDate(null);
						}}
					/>
				</div>
				{renderSearch()}
				<div className="flex flex-col gap-3 justify-end">
					<Button className="w-[200px] h-[70px]" variant="outline" isLoading={downloading} onClick={triggerDownload}>
						Download
					</Button>
				</div>
			</div>

			<div className="w-full h-full flex flex-col gap-4">
				<div className="w-full flex gap-2 justify-between">
					<Button className="w-full max-w-[200px]" disabled={page === 0} onClick={() => setPage((prev) => prev - 1)}>
						Previous Page
					</Button>
					<div className="flex items-center gap-4 w-fit">
						<span className="opacity-80">
							{page * SIZE + 1} - {(page + 1) * SIZE > filteredRows.length ? filteredRows.length : (page + 1) * SIZE} /{" "}
							{filteredRows.length}
						</span>
						<Button className="w-[200px]" disabled={(page + 1) * SIZE >= filteredRows.length} onClick={() => setPage((prev) => prev + 1)}>
							Next Page
						</Button>
					</div>
				</div>

				<table className="rounded-xl h-full bg-mti-purple-ultralight/40 w-full">
					<thead>
						{table.getHeaderGroups().map((headerGroup) => (
							<tr key={headerGroup.id}>
								{headerGroup.headers.map((header) => (
									<th className="p-4 text-left" key={header.id}>
										{header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
									</th>
								))}
							</tr>
						))}
					</thead>
					<tbody className="px-2">
						{table.getRowModel().rows.map((row) => (
							<tr className="odd:bg-white even:bg-mti-purple-ultralight/40 rounded-lg py-2" key={row.id}>
								{row.getVisibleCells().map((cell) => (
									<td className="px-4 py-2" key={cell.id}>
										{flexRender(cell.column.columnDef.cell, cell.getContext())}
									</td>
								))}
							</tr>
						))}
					</tbody>
				</table>
			</div>
			<div className="flex flex-wrap gap-2 items-center text-center">
				{consolidateResults.highest && (
					<IconCard onClick={() => {}} Icon={BsPersonFill} label={`Highest result: ${consolidateResults.highest.user}`} color="purple" />
				)}
				{consolidateResults.lowest && (
					<IconCard onClick={() => {}} Icon={BsPersonFill} label={`Lowest result: ${consolidateResults.lowest.user}`} color="purple" />
				)}
			</div>
		</>
	);
};

export default MasterStatistical;
