import QuestionsModal from "@/components/QuestionsModal";
import { renderExercise } from "@/components/Exercises";
import Button from "@/components/Low/Button";
import ModuleTitle from "@/components/Medium/ModuleTitle";
import { renderSolution } from "@/components/Solutions";
import { Module } from "@/interfaces";
import { Exercise, FillBlanksMCOption, LevelExam, MultipleChoiceExercise, UserSolution } from "@/interfaces/exam";
import useExamStore, { usePersistentExamStore } from "@/stores/exam";
import { countExercises } from "@/utils/moduleUtils";
import clsx from "clsx";
import { use, useCallback, useEffect, useMemo, useRef, useState } from "react";
import TextComponent from "./TextComponent";
import PartDivider from "../Navigation/SectionDivider";
import Timer from "@/components/Medium/Timer";
import shuffleExamExercise from "./Shuffle";
import { Tab } from "@headlessui/react";
import Modal from "@/components/Modal";
import { typeCheckWordsMC } from "@/utils/type.check";
import SectionNavbar from "../Navigation/SectionNavbar";
import AudioPlayer from "@/components/Low/AudioPlayer";
import { ExamProps } from "../types";
import { answeredEveryQuestionInPart } from "../utils/answeredEveryQuestion";
import useExamTimer from "@/hooks/useExamTimer";
import ProgressButtons from "../components/ProgressButtons";
import useExamNavigation from "../Navigation/useExamNavigation";
import { calculateExerciseIndex } from "../utils/calculateExerciseIndex";
import { defaultExamUserSolutions } from "@/utils/exams";
import PracticeModal from "@/components/PracticeModal";


const Level: React.FC<ExamProps<LevelExam>> = ({ exam, showSolutions = false, preview = false }) => {
	const updateTimers = useExamTimer(exam.module, preview || showSolutions);
	const userSolutionRef = useRef<(() => UserSolution) | null>(null);
	const [solutionWasUpdated, setSolutionWasUpdated] = useState(false);

	const examState = useExamStore((state) => state);
	const persistentExamState = usePersistentExamStore((state) => state);

	const {
		userSolutions,
		partIndex,
		exerciseIndex,
		questionIndex,
		shuffles,
		setTimeIsUp,
		setBgColor,
		setUserSolutions,
		setPartIndex,
		setExerciseIndex,
		setQuestionIndex,
		setShuffles,
		flags,
		timeSpentCurrentModule,
		dispatch,
	} = !preview ? examState : persistentExamState;

	const { finalizeModule, timeIsUp } = flags;

	const timer = useRef(exam.minTimer - timeSpentCurrentModule / 60);

	// In case client want to switch back
	const textRenderDisabled = true;

	const [timesListened, setTimesListened] = useState(0);
	const [showSubmissionModal, setShowSubmissionModal] = useState(false);
	const [showQuestionsModal, setShowQuestionsModal] = useState(false);
	const [continueAnyways, setContinueAnyways] = useState(false);
	const [textRender, setTextRender] = useState(false);

	const [questionModalKwargs, setQuestionModalKwargs] = useState<{
		type?: "module" | "blankQuestions" | "submit"; unanswered?: boolean | undefined; onClose: (next?: boolean) => void | undefined;
	}>({
		type: "blankQuestions",
		onClose: function (x: boolean | undefined) { if (x) { setShowQuestionsModal(false); nextExercise(); } else { setShowQuestionsModal(false) } }
	});

	const modalKwargs = () => {
		const kwargs: { type: "module" | "blankQuestions" | "submit", unanswered: boolean, onClose: (next?: boolean) => void; } = {
			type: "blankQuestions",
			unanswered: false,
			onClose: function (x: boolean | undefined) { if (x) { setContinueAnyways(true); setShowQuestionsModal(false); } else { setShowQuestionsModal(false) } }
		};

		if (partIndex === exam.parts.length - 1) {
			kwargs.type = "submit"
			kwargs.unanswered = !exam.parts.every((_, partIndex) => answeredEveryQuestionInPart(exam, partIndex, userSolutions));
			kwargs.onClose = function (x: boolean | undefined) { if (x) { setShowSubmissionModal(true); setShowQuestionsModal(false); } else { setShowQuestionsModal(false) } };
		}
		setQuestionModalKwargs(kwargs);
	}

	const {
		nextExercise, previousExercise,
		showPartDivider, setShowPartDivider,
		seenParts, setSeenParts, startNow, setStartNow
	} = useExamNavigation(
		{
			exam, module: "level", showBlankModal: showQuestionsModal,
			setShowBlankModal: setShowQuestionsModal, showSolutions,
			preview, disableBetweenParts: true, modalBetweenParts: true, modalKwargs
		}
	);

	const hasPractice = useMemo(() => {
		if (partIndex > -1 && partIndex < exam.parts.length && !showPartDivider) {
			return exam.parts[partIndex].exercises.some(e => e.isPractice)
		}
		return false
	}, [partIndex, showPartDivider, exam.parts])

	const registerSolution = useCallback((updateSolution: () => UserSolution) => {
		userSolutionRef.current = updateSolution;
		setSolutionWasUpdated(true);
	}, []);

	useEffect(() => {
		if (preview) {
			setUserSolutions(defaultExamUserSolutions(exam));
		}
		// eslint-disable-next-line react-hooks/exhaustive-deps
	}, [])

	const [contextWords, setContextWords] = useState<{ match: string, originalLine: string }[] | undefined>(undefined);
	const [contextWordLines, setContextWordLines] = useState<number[] | undefined>(undefined);
	const [totalLines, setTotalLines] = useState<number>(0);

	useEffect(() => {
		if (showSolutions) {
			const solutionShuffles = userSolutions.map(solution => ({
				exerciseID: solution.exercise,
				shuffles: solution.shuffleMaps || []
			}));
			setShuffles(solutionShuffles);
		}
		// eslint-disable-next-line react-hooks/exhaustive-deps
	}, []);


	const currentExercise = useMemo<Exercise>(() => {
		let exercise = exam.parts[partIndex].exercises[exerciseIndex];
		exercise = {
			...exercise,
			userSolutions: userSolutions.find((x) => x.exercise === exercise.id)?.solutions || [],
		};
		exercise = shuffleExamExercise(exam.shuffle, exercise, showSolutions, userSolutions, shuffles, setShuffles);
		return exercise;
		// eslint-disable-next-line react-hooks/exhaustive-deps
	}, [partIndex, exerciseIndex]);


	useEffect(() => {
		if (solutionWasUpdated && userSolutionRef.current) {
			const solution = userSolutionRef.current();
			setUserSolutions([...userSolutions.filter((x) => x.exercise !== solution.exercise), { ...solution, module: "level" as Module, exam: exam.id, shuffleMaps: exam.shuffle ? [...shuffles.find((x) => x.exerciseID == currentExercise?.id)?.shuffles!] : [] }]);
			setSolutionWasUpdated(false);
		}
		// eslint-disable-next-line react-hooks/exhaustive-deps
	}, [solutionWasUpdated]);

	useEffect(() => {
		if (finalizeModule || timeIsUp) {
			updateTimers();
			if (timeIsUp) setTimeIsUp(false);
			dispatch({ type: "FINALIZE_MODULE", payload: { updateTimers: false } })
		}
		// eslint-disable-next-line react-hooks/exhaustive-deps
	}, [finalizeModule, timeIsUp])


	const renderAudioPlayer = () => (
		<div className="flex flex-col gap-8 w-full bg-mti-gray-seasalt rounded-xl py-8 px-16">
			{exam?.parts[partIndex]?.audio?.source ? (
				<>
					<div className="flex flex-col w-full gap-2">
						<h4 className="text-xl font-semibold">Please listen to the following audio attentively.</h4>
						<span className="text-base">
							{(() => {
								const audioRepeatTimes = exam?.parts[partIndex]?.audio?.repeatableTimes;
								return audioRepeatTimes && audioRepeatTimes > 0
									? `You will only be allowed to listen to the audio ${audioRepeatTimes - timesListened} time(s).`
									: "You may listen to the audio as many times as you would like.";
							})()}
						</span>
					</div>
					<div className="rounded-xl flex flex-col gap-4 items-center w-full h-fit">
						<AudioPlayer
							key={partIndex}
							src={exam?.parts[partIndex]?.audio?.source ?? ''}
							color="listening"
							onEnd={() => setTimesListened((prev) => prev + 1)}
							disabled={exam?.parts[partIndex]?.audio?.repeatableTimes != null &&
								timesListened === exam.parts[partIndex]?.audio?.repeatableTimes}
							disablePause
						/>
					</div>
				</>
			) : (
				<span>This section will be displayed the audio once it has been generated.</span>
			)}

		</div>
	);

	const renderText = () => (
		<>
			<div className={clsx("flex flex-col gap-6 w-full bg-mti-gray-seasalt rounded-xl mt-4 relative py-8 px-16")}>
				<>
					<div className="flex flex-col w-full gap-2">
						{textRender && !textRenderDisabled ? (
							<>
								<h4 className="text-xl font-semibold">
									Please read the following excerpt attentively, you will then be asked questions about the text you&apos;ve read.
								</h4>
								<span className="text-base">You will be allowed to read the text while doing the exercises</span>
							</>
						) : (
							<h4 className="text-xl font-semibold">
								Answer the questions on the right based on what you&apos;ve read.
							</h4>
						)}
						<div className="border border-mti-gray-dim w-full rounded-full opacity-10" />
						{(exam.parts[partIndex].context || exam.parts[partIndex].text) &&
							<TextComponent
								part={exam.parts[partIndex]}
								contextWords={contextWords}
								setContextWordLines={setContextWordLines}
								setTotalLines={setTotalLines}
							/>}
					</div>
				</>
			</div>
			{textRender && !textRenderDisabled && (
				<div className="self-end flex justify-between w-full gap-8 absolute bottom-8 left-0 px-8">
					<Button
						color="purple"
						variant="outline"
						className="max-w-[200px] w-full"
						onClick={() => { setTextRender(false); previousExercise(); }}
					>
						Back
					</Button>

					<Button color="purple" onClick={() => setTextRender(false)} className="max-w-[200px] self-end w-full">
						Next
					</Button>
				</div>
			)}
		</>
	);

	const partLabel = () => {
		const partCategory = exam.parts[partIndex].category ? ` (${exam.parts[partIndex].category})` : '';
		if (currentExercise?.type === "fillBlanks" && typeCheckWordsMC(currentExercise.words))
			return `Part ${partIndex + 1} (Questions ${currentExercise.words[0].id} - ${currentExercise.words[currentExercise.words.length - 1].id})${partCategory}\n\n${currentExercise.prompt}`

		if (currentExercise?.type === "multipleChoice") {
			return `Part ${partIndex + 1} (Questions ${currentExercise.questions[0].id} - ${currentExercise.questions[currentExercise.questions.length - 1].id})${partCategory}\n\n${currentExercise.prompt}`
		}

		if (typeof exam.parts[partIndex].context === "string") {
			const nextExercise = exam.parts[partIndex].exercises[0] as MultipleChoiceExercise;
			return `Part ${partIndex + 1} (Questions ${nextExercise.questions[0].id} - ${nextExercise.questions[nextExercise.questions.length - 1].id})${partCategory}\n\n${nextExercise.prompt}`
		}
	}

	useEffect(() => {
		const regex = /.*?['"](.*?)['"] in line (\d+)\?$/;

		const findMatch = (index: number) => {
			if (currentExercise && currentExercise.type === "multipleChoice" && currentExercise!.questions[index]) {
				const match = currentExercise!.questions[index].prompt.match(regex);
				if (match) {
					return { match: match[1], originalLine: match[2] }
				}
			}
			return;
		}

		// if the client for some whatever random reason decides
		// to add more questions update this
		const numberOfQuestions = 2;

		if (exam.parts[partIndex].context) {
			const hits = Array.from({ length: numberOfQuestions }).reduce<{ match: string, originalLine: string }[]>((acc, _, i) => {
				const result = findMatch(questionIndex + i);
				if (!!result) {
					acc.push(result);
				}
				return acc;
			}, []);

			if (hits.length > 0) {
				setContextWords(hits)
			}
		}

		// eslint-disable-next-line react-hooks/exhaustive-deps
	}, [currentExercise, questionIndex, totalLines]);

	useEffect(() => {
		if (
			currentExercise && currentExercise.type === "multipleChoice" &&
			exam.parts[partIndex].context && contextWordLines
		) {
			if (contextWordLines.length > 0) {
				contextWordLines.forEach((n, i) => {
					if (contextWords && contextWords[i] && n !== -1) {
						const updatedPrompt = currentExercise!.questions[questionIndex + i].prompt.replace(
							`in line ${contextWords[i].originalLine}`,
							`in line ${n}`
						);
						currentExercise!.questions[questionIndex + i].prompt = updatedPrompt;
					}
				})
			}
		}
		// eslint-disable-next-line react-hooks/exhaustive-deps
	}, [contextWordLines]);


	useEffect(() => {
		if (continueAnyways) {
			setContinueAnyways(false);
			nextExercise(true);
		}
		// eslint-disable-next-line react-hooks/exhaustive-deps
	}, [continueAnyways]);

	const mcNavKwargs = {
		userSolutions: userSolutions,
		exam: exam,
		partIndex: partIndex,
		showSolutions: showSolutions,
		setExerciseIndex: setExerciseIndex,
		setPartIndex: setPartIndex,
		runOnClick: setQuestionIndex
	}

	const progressButtons = useMemo(() =>
		// Do not remove the ()=> in handle next
		<ProgressButtons handlePrevious={previousExercise} handleNext={() => nextExercise()} />
		, [nextExercise, previousExercise]);

	return (
		<>
			<div className={clsx("flex flex-col h-full w-full gap-8 items-center", showPartDivider && "justify-center")}>
				<PracticeModal key={`${partIndex}_${showPartDivider}`} open={hasPractice} />
				<Modal
					className={"!w-2/6 !p-8"}
					titleClassName={"font-bold text-3xl text-mti-rose-light"}
					isOpen={showSubmissionModal}
					onClose={() => { }}
					title={"Confirm Submission"}
				>
					<>
						<p className="text-xl mt-8 mb-12">Are you sure you want to proceed with the submission?</p>
						<div className="w-full flex justify-between">
							<Button color="purple" onClick={() => setShowSubmissionModal(false)} variant="outline" className="max-w-[200px] self-end w-full !text-xl">
								Cancel
							</Button>
							<Button color="rose" onClick={() => { setShowSubmissionModal(false); setContinueAnyways(true) }} className="max-w-[200px] self-end w-full !text-xl">
								Confirm
							</Button>
						</div>
					</>
				</Modal>
				<QuestionsModal isOpen={showQuestionsModal} {...questionModalKwargs} />
				{
					(!showPartDivider && !startNow) &&
					<Timer minTimer={exam.minTimer} disableTimer={showSolutions || preview} standalone={true} />
				}
				{(showPartDivider || (startNow && partIndex === 0)) ?
					<PartDivider
						module="level"
						sectionLabel="Part"
						defaultTitle="Placement Test"
						section={exam.parts[partIndex]}
						sectionIndex={partIndex}
						onNext={() => { setShowPartDivider(false); setStartNow(false); setBgColor("bg-white"); setSeenParts(prev => new Set(prev).add(partIndex)); }}
					/> : (
						<>
							{exam.parts.length > 1 && <SectionNavbar
								module="level"
								sectionLabel="Part"
								seenParts={seenParts}
								setShowPartDivider={setShowPartDivider}
								setSeenParts={setSeenParts}
								preview={preview}
							/>}
							<ModuleTitle
								examLabel={exam.label}
								partLabel={partLabel()}
								minTimer={timer.current}
								exerciseIndex={calculateExerciseIndex(exam, partIndex, exerciseIndex, questionIndex)}
								module="level"
								totalExercises={countExercises(exam.parts.flatMap((x) => x.exercises))}
								disableTimer={showSolutions}
								showTimer={false}
								preview={preview}
								{...mcNavKwargs}
							/>
							<div
								className={clsx(
									"mb-20 w-full",
									!!exam.parts[partIndex].context && !textRender && "grid grid-cols-2 gap-4",
								)}>
								{textRender && !textRenderDisabled ?
									renderText() :
									<>
										{exam.parts[partIndex]?.context && renderText()}
										{exam.parts[partIndex]?.audio && renderAudioPlayer()}
										{(showSolutions) ?
											currentExercise && renderSolution(currentExercise, progressButtons, progressButtons) :
											currentExercise && renderExercise(currentExercise, exam.id, registerSolution, preview, progressButtons, progressButtons)
										}
									</>
								}
							</div>
						</>
					)}
			</div>
		</>
	);
}

export default Level;
