import {ListeningExam, MultipleChoiceExercise, UserSolution} from "@/interfaces/exam";
import {useEffect, useState} from "react";
import {renderExercise} from "@/components/Exercises";
import {renderSolution} from "@/components/Solutions";
import ModuleTitle from "@/components/Medium/ModuleTitle";
import AudioPlayer from "@/components/Low/AudioPlayer";
import Button from "@/components/Low/Button";
import BlankQuestionsModal from "@/components/QuestionsModal";
import useExamStore from "@/stores/examStore";
import {countExercises} from "@/utils/moduleUtils";

interface Props {
	exam: ListeningExam;
	showSolutions?: boolean;
	onFinish: (userSolutions: UserSolution[]) => void;
}

const INSTRUCTIONS_AUDIO_SRC =
	"https://firebasestorage.googleapis.com/v0/b/storied-phalanx-349916.appspot.com/o/generic_listening_intro_v2.mp3?alt=media&token=16769f5f-1e9b-4a72-86a9-45a6f0fa9f82";

export default function Listening({exam, showSolutions = false, onFinish}: Props) {
	const [timesListened, setTimesListened] = useState(0);
	const [showBlankModal, setShowBlankModal] = useState(false);
	const [multipleChoicesDone, setMultipleChoicesDone] = useState<{id: string; amount: number}[]>([]);

	const {userSolutions, setUserSolutions} = useExamStore((state) => state);
	const {hasExamEnded, setHasExamEnded} = useExamStore((state) => state);
	const {partIndex, setPartIndex} = useExamStore((state) => state);
	const {exerciseIndex, setExerciseIndex} = useExamStore((state) => state);
	const [storeQuestionIndex, setStoreQuestionIndex] = useExamStore((state) => [state.questionIndex, state.setQuestionIndex]);

	const scrollToTop = () => Array.from(document.getElementsByTagName("body")).forEach((body) => body.scrollTo(0, 0));

	useEffect(() => {
		if (showSolutions) return setExerciseIndex(-1);
	}, [setExerciseIndex, showSolutions]);

	useEffect(() => {
		if (partIndex === -1 && exam.variant === "partial") {
			setPartIndex(0);
		}
	}, [partIndex, exam, setPartIndex]);

	useEffect(() => {
		const previousParts = exam.parts.filter((_, index) => index < partIndex);
		let previousMultipleChoice = previousParts.flatMap((x) => x.exercises).filter((x) => x.type === "multipleChoice") as MultipleChoiceExercise[];

		if (partIndex > -1 && exerciseIndex > -1) {
			const previousPartExercises = exam.parts[partIndex].exercises.filter((_, index) => index < exerciseIndex);
			const partMultipleChoice = previousPartExercises.filter((x) => x.type === "multipleChoice") as MultipleChoiceExercise[];

			previousMultipleChoice = [...previousMultipleChoice, ...partMultipleChoice];
		}

		setMultipleChoicesDone(previousMultipleChoice.map((x) => ({id: x.id, amount: x.questions.length - 1})));
		// eslint-disable-next-line react-hooks/exhaustive-deps
	}, []);

	useEffect(() => {
		if (hasExamEnded && exerciseIndex === -1) {
			setExerciseIndex(exerciseIndex + 1);
		}
	}, [hasExamEnded, exerciseIndex, setExerciseIndex]);

	const confirmFinishModule = (keepGoing?: boolean) => {
		if (!keepGoing) {
			setShowBlankModal(false);
			return;
		}

		onFinish(userSolutions);
	};

	const nextExercise = (solution?: UserSolution) => {
		scrollToTop();
		if (solution) {
			setUserSolutions([...userSolutions.filter((x) => x.exercise !== solution.exercise), {...solution, module: "listening", exam: exam.id}]);
		}
		if (storeQuestionIndex > 0) {
			const exercise = getExercise();
			setMultipleChoicesDone((prev) => [...prev.filter((x) => x.id !== exercise.id), {id: exercise.id, amount: storeQuestionIndex}]);
		}
		setStoreQuestionIndex(0);

		if (exerciseIndex + 1 < exam.parts[partIndex].exercises.length && !hasExamEnded) {
			setExerciseIndex(exerciseIndex + 1);
			return;
		}

		if (partIndex + 1 < exam.parts.length && !hasExamEnded) {
			setPartIndex(partIndex + 1);
			setTimesListened(0);
			setExerciseIndex(showSolutions ? 0 : -1);
			return;
		}

		if (
			solution &&
			![...userSolutions.filter((x) => x.exercise !== solution?.exercise).map((x) => x.score.missing), solution?.score.missing].every(
				(x) => x === 0,
			) &&
			!showSolutions &&
			!hasExamEnded
		) {
			setShowBlankModal(true);
			return;
		}

		setHasExamEnded(false);

		if (solution) {
			onFinish([...userSolutions.filter((x) => x.exercise !== solution.exercise), {...solution, module: "listening", exam: exam.id}]);
		} else {
			onFinish(userSolutions);
		}
	};

	const previousExercise = (solution?: UserSolution) => {
		scrollToTop();
		if (solution) {
			setUserSolutions([...userSolutions.filter((x) => x.exercise !== solution.exercise), {...solution, module: "listening", exam: exam.id}]);
		}
		setStoreQuestionIndex(0);

		setExerciseIndex(exerciseIndex - 1);
	};

	const getExercise = () => {
		const exercise = exam.parts[partIndex].exercises[exerciseIndex];
		return {
			...exercise,
			userSolutions: userSolutions.find((x) => x.exercise === exercise.id)?.solutions || [],
		};
	};

	useEffect(() => {
		if (partIndex > -1 && exerciseIndex > -1) {
			const exercise = getExercise();
			setMultipleChoicesDone((prev) => prev.filter((x) => x.id !== exercise.id));
		}
		// eslint-disable-next-line react-hooks/exhaustive-deps
	}, [exerciseIndex, partIndex]);

	const calculateExerciseIndex = () => {
		if (partIndex === -1) return 0;
		if (partIndex === 0)
			return (
				(exerciseIndex === -1 ? 0 : exerciseIndex + 1) + storeQuestionIndex + multipleChoicesDone.reduce((acc, curr) => acc + curr.amount, 0)
			);

		const exercisesPerPart = exam.parts.map((x) => x.exercises.length);
		const exercisesDone = exercisesPerPart.filter((_, index) => index < partIndex).reduce((acc, curr) => curr + acc, 0);
		return (
			exercisesDone +
			(exerciseIndex === -1 ? 0 : exerciseIndex + 1) +
			storeQuestionIndex +
			multipleChoicesDone.reduce((acc, curr) => acc + curr.amount, 0)
		);
	};

	const renderAudioInstructionsPlayer = () => (
		<div className="flex flex-col gap-8 w-full bg-mti-gray-seasalt rounded-xl py-8 px-16">
			<div className="flex flex-col w-full gap-2">
				<h4 className="text-xl font-semibold">Please listen to the instructions audio attentively.</h4>
			</div>
			<div className="rounded-xl flex flex-col gap-4 items-center w-full h-fit">
				<AudioPlayer key={partIndex} src={INSTRUCTIONS_AUDIO_SRC} color="listening" />
			</div>
		</div>
	);

	const renderAudioPlayer = () => (
		<div className="flex flex-col gap-8 w-full bg-mti-gray-seasalt rounded-xl py-8 px-16">
			<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">
					{exam.parts[partIndex].audio.repeatableTimes > 0
						? `You will only be allowed to listen to the audio ${exam.parts[partIndex].audio.repeatableTimes - 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={timesListened === exam.parts[partIndex].audio.repeatableTimes}
					disablePause
				/>
			</div>
		</div>
	);

	return (
		<>
			<BlankQuestionsModal isOpen={showBlankModal} onClose={confirmFinishModule} />
			<div className="flex flex-col h-full w-full gap-8 justify-between">
				<ModuleTitle
					exerciseIndex={calculateExerciseIndex()}
					minTimer={exam.minTimer}
					module="listening"
					totalExercises={countExercises(exam.parts.flatMap((x) => x.exercises))}
					disableTimer={showSolutions}
				/>
				{/* Audio Player for the Instructions */}
				{partIndex === -1 && renderAudioInstructionsPlayer()}

				{/* Part's audio player */}
				{partIndex > -1 && renderAudioPlayer()}

				{/* Exercise renderer */}
				{exerciseIndex > -1 &&
					partIndex > -1 &&
					exerciseIndex < exam.parts[partIndex].exercises.length &&
					!showSolutions &&
					renderExercise(getExercise(), exam.id, nextExercise, previousExercise)}

				{/* Solution renderer */}
				{exerciseIndex > -1 &&
					partIndex > -1 &&
					exerciseIndex < exam.parts[partIndex].exercises.length &&
					showSolutions &&
					renderSolution(exam.parts[partIndex].exercises[exerciseIndex], nextExercise, previousExercise)}
			</div>

			{exerciseIndex === -1 && partIndex > -1 && exam.variant !== "partial" && (
				<div className="self-end flex justify-between w-full gap-8 absolute bottom-8 left-0 px-8">
					<Button
						color="purple"
						variant="outline"
						onClick={() => {
							if (partIndex === 0) return setPartIndex(-1);

							setExerciseIndex(exam.parts[partIndex - 1].exercises.length - 1);
							setPartIndex(partIndex - 1);
						}}
						className="max-w-[200px] w-full">
						Back
					</Button>

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

			{partIndex === -1 && exam.variant !== "partial" && (
				<Button color="purple" onClick={() => setPartIndex(0)} className="max-w-[200px] self-end w-full justify-self-end">
					Start now
				</Button>
			)}
			{exerciseIndex === -1 && partIndex === 0 && exam.variant === "partial" && (
				<Button color="purple" onClick={() => nextExercise()} className="max-w-[200px] self-end w-full justify-self-end">
					Start now
				</Button>
			)}
		</>
	);
}
