import React from "react";
import { BsClock, BsXCircle } from "react-icons/bs";
import clsx from "clsx";
import { Stat, User } from "@/interfaces/user";
import { Grading, Module, Step } from "@/interfaces";
import ai_usage from "@/utils/ai.detection";
import { calculateBandScore, getGradingLabel } from "@/utils/score";
import moment from "moment";
import { Assignment } from "@/interfaces/results";
import { uuidv4 } from "@firebase/util";
import { useRouter } from "next/router";
import { uniqBy } from "lodash";
import { sortByModule } from "@/utils/moduleUtils";
import { getExamById } from "@/utils/exams";
import ModuleBadge from "../ModuleBadge";
import useExamStore from "@/stores/exam";
import { findBy } from "@/utils";

const formatTimestamp = (timestamp: string | number) => {
	const time = typeof timestamp === "string" ? parseInt(timestamp) : timestamp;
	const date = moment(time);
	const formatter = "YYYY/MM/DD - HH:mm";
	return date.format(formatter);
};

const aggregateScoresByModule = (stats: Stat[]): { module: Module; total: number; missing: number; correct: number }[] => {
	const scores: {
		[key in Module]: { total: number; missing: number; correct: number };
	} = {
		reading: {
			total: 0,
			correct: 0,
			missing: 0,
		},
		listening: {
			total: 0,
			correct: 0,
			missing: 0,
		},
		writing: {
			total: 0,
			correct: 0,
			missing: 0,
		},
		speaking: {
			total: 0,
			correct: 0,
			missing: 0,
		},
		level: {
			total: 0,
			correct: 0,
			missing: 0,
		},
	};

	stats.filter(x => !x.isPractice).forEach((x) => {
		scores[x.module!] = {
			total: scores[x.module!].total + x.score.total,
			correct: scores[x.module!].correct + x.score.correct,
			missing: scores[x.module!].missing + x.score.missing,
		};
	});

	return Object.keys(scores)
		.filter((x) => scores[x as Module].total > 0)
		.map((x) => ({ module: x as Module, ...scores[x as Module] }));
};

interface StatsGridItemProps {
	width?: string | undefined;
	height?: string | undefined;
	gradingSystems: Grading[]
	examNumber?: number | undefined;
	stats: Stat[];
	timestamp: string | number;
	user: User;
	assignments: Assignment[];
	users: User[];
	training?: boolean;
	selectedTrainingExams?: string[];
	maxTrainingExams?: number;
	setSelectedTrainingExams?: React.Dispatch<React.SetStateAction<string[]>>;
	renderPdfIcon?: (session: string, color: string, textColor: string) => React.ReactNode;
}

const StatsGridItem: React.FC<StatsGridItemProps> = ({
	stats,
	timestamp,
	user,
	assignments,
	gradingSystems,
	users,
	training,
	selectedTrainingExams,
	setSelectedTrainingExams,
	renderPdfIcon,
	width = undefined,
	height = undefined,
	examNumber = undefined,
	maxTrainingExams = undefined,
}) => {
	const router = useRouter();
	const dispatch = useExamStore((s) => s.dispatch);

	const correct = stats.reduce((accumulator, current) => accumulator + current.score.correct, 0);
	const total = stats.reduce((accumulator, current) => accumulator + current.score.total, 0);
	const aggregatedScores = aggregateScoresByModule(stats).filter((x) => x.total > 0);
	const assignmentID = stats.reduce((_, current) => current.assignment as any, "");
	const assignment = assignments.find((a) => a.id === assignmentID);
	const isDisabled = stats.some((x) => x.isDisabled);

	const aiUsage = Math.round(ai_usage(stats) * 100);

	const aggregatedLevels = aggregatedScores.map((x) => ({
		module: x.module,
		level: calculateBandScore(x.correct, x.total, x.module, user.focus),
	}));

	const textColor = clsx(
		correct / total >= 0.7 && "text-mti-purple",
		correct / total >= 0.3 && correct / total < 0.7 && "text-mti-red",
		correct / total < 0.3 && "text-mti-rose",
	);

	const { timeSpent, inactivity, session } = stats[0];

	const selectExam = () => {
		if (
			training &&
			!isDisabled &&
			typeof maxTrainingExams !== "undefined" &&
			typeof setSelectedTrainingExams !== "undefined" &&
			typeof timestamp == "string"
		) {
			setSelectedTrainingExams((prevExams) => {
				const uniqueExams = [...new Set(stats.map((stat) => `${stat.module}-${stat.date}`))];
				const indexes = uniqueExams.map((exam) => prevExams.indexOf(exam)).filter((index) => index !== -1);
				if (indexes.length > 0) {
					const newExams = [...prevExams];
					indexes
						.sort((a, b) => b - a)
						.forEach((index) => {
							newExams.splice(index, 1);
						});
					return newExams;
				} else {
					if (prevExams.length + uniqueExams.length <= maxTrainingExams) {
						return [...prevExams, ...uniqueExams];
					} else {
						return prevExams;
					}
				}
			});
		} else {
			const examPromises = uniqBy(stats, "exam").map((stat) => {
				return getExamById(stat.module, stat.exam);
			});

			if (isDisabled) return;

			Promise.all(examPromises).then((exams) => {
				if (exams.every((x) => !!x)) {
					dispatch({
						type: 'INIT_SOLUTIONS', payload: {
							exams: exams.map((x) => x!).sort(sortByModule),
							modules: exams
								.map((x) => x!)
								.sort(sortByModule)
								.map((x) => x!.module),
							stats,
							timeSpent,
							inactivity
						}
					});
					router.push("/exam");
				}
			});
		}
	};

	const shouldRenderPDFIcon = () => {
		if (assignment) return assignment.released;
		return true;
	};

	const levelAverage = () =>
		aggregatedLevels.reduce((accumulator, current) => accumulator + current.level, 0) / aggregatedLevels.length

	const renderLevelScore = () => {
		const defaultLevelScore = levelAverage().toFixed(1)
		if (!stats.every(s => s.module === "level")) return defaultLevelScore
		if (gradingSystems.length === 0) return defaultLevelScore

		const score = {
			correct: stats.reduce((acc, curr) => acc + curr.score.correct, 0),
			total: stats.reduce((acc, curr) => acc + curr.score.total, 0)
		}

		const level: number = calculateBandScore(score.correct, score.total, "level", user.focus);

		if (!!assignment) {
			const gradingSystem = findBy(gradingSystems, 'entity', assignment.entity)
			if (!gradingSystem) return defaultLevelScore

			return getGradingLabel(level, gradingSystem.steps)
		}

		return getGradingLabel(level, gradingSystems[0].steps)
	}

	const content = (
		<>
			<div className="w-full flex justify-between">
				<div className="flex flex-col md:gap-1 -md:gap-2 2xl:gap-2">
					<span className="font-medium">{formatTimestamp(timestamp)}</span>
					<div className="flex items-center gap-2">
						{!!timeSpent && (
							<span className="text-sm flex gap-2 items-center tooltip" data-tip="Time Spent">
								<BsClock /> {Math.floor(timeSpent / 60)} minutes
							</span>
						)}
						{!!inactivity && (
							<span className="text-sm flex gap-2 items-center tooltip" data-tip="Inactivity">
								<BsXCircle /> {Math.floor(inactivity / 60)} minutes
							</span>
						)}
					</div>
				</div>
				<div className="flex flex-col gap-2">
					<div className="flex flex-row gap-2">
						{((!!assignment && (assignment.released || assignment.released === undefined)) || !assignment) && (
							<span className={textColor}>
								Level{' '}
								{renderLevelScore()}
							</span>
						)}
						{shouldRenderPDFIcon() && renderPdfIcon && renderPdfIcon(session, textColor, textColor)}
					</div>
					{examNumber === undefined ? (
						<>
							{aiUsage >= 50 && user.type !== "student" && (
								<div
									className={clsx("ml-auto border px-1 rounded w-fit mr-1", {
										"bg-orange-100 border-orange-400 text-orange-700": aiUsage < 80,
										"bg-red-100 border-red-400 text-red-700": aiUsage >= 80,
									})}>
									<span className="text-xs">AI Usage</span>
								</div>
							)}
						</>
					) : (
						<div className="flex justify-end">
							<span className="font-semibold bg-gray-200 text-gray-800 px-2.5 py-0.5 rounded-full mt-0.5">{examNumber}</span>
						</div>
					)}
				</div>
			</div>

			<div className="w-full flex flex-col gap-1">
				<div className={clsx("grid grid-cols-4 gap-2 place-items-start w-full -md:mt-2", examNumber !== undefined && "pr-10")}>
					{aggregatedLevels.map(({ module, level }) =>
						<ModuleBadge
							key={module}
							module={module}
							level={(!!assignment && (assignment.released || assignment.released === undefined)) || !assignment ? level : undefined}
						/>
					)}
				</div>

				{assignment && (
					<span className="font-light text-sm">
						Assignment: {assignment.name}, Teacher: {users.find((u) => u.id === assignment.assigner)?.name}
					</span>
				)}
			</div>
		</>
	);

	return (
		<>
			<div
				key={uuidv4()}
				className={clsx(
					"flex flex-col justify-between gap-4 border border-mti-gray-platinum p-4 cursor-pointer rounded-xl transition ease-in-out duration-300 -md:hidden",
					(isDisabled || (!!assignment && !assignment.released)) && "grayscale tooltip",
					correct / total >= 0.7 && "hover:border-mti-purple",
					correct / total >= 0.3 && correct / total < 0.7 && "hover:border-mti-red",
					correct / total < 0.3 && "hover:border-mti-rose",
					typeof selectedTrainingExams !== "undefined" &&
					typeof timestamp === "string" &&
					selectedTrainingExams.some((exam) => exam.includes(timestamp)) &&
					"border-2 border-slate-600",
				)}
				onClick={() => {
					if (!!assignment && !assignment.released) return;
					if (examNumber === undefined) return selectExam();
					return;
				}}
				style={{
					...(width !== undefined && { width }),
					...(height !== undefined && { height }),
				}}
				data-tip={isDisabled ? "This exam is still being evaluated..." : "This exam is still locked by its assigner..."}
				role="button">
				{content}
			</div>
			<div
				key={uuidv4()}
				className={clsx(
					"flex flex-col gap-4 border border-mti-gray-platinum p-4 cursor-pointer rounded-xl transition ease-in-out duration-300 -md:tooltip md:hidden",
					correct / total >= 0.7 && "hover:border-mti-purple",
					correct / total >= 0.3 && correct / total < 0.7 && "hover:border-mti-red",
					correct / total < 0.3 && "hover:border-mti-rose",
				)}
				data-tip="Your screen size is too small to view previous exams."
				style={{
					...(width !== undefined && { width }),
					...(height !== undefined && { height }),
				}}
				role="button">
				{content}
			</div>
		</>
	);
};

export default StatsGridItem;
