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, MultipleChoiceQuestion, ShuffleMap, UserSolution } from "@/interfaces/exam";
import useExamStore from "@/stores/examStore";
import { countExercises } from "@/utils/moduleUtils";
import clsx from "clsx";
import { use, useEffect, useState } from "react";
import TextComponent from "./TextComponent";
import PartDivider from "./PartDivider";
import Timer from "@/components/Medium/Timer";
import { Stat } from "@/interfaces/user";

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

const typeCheckWordsMC = (words: any[]): words is FillBlanksMCOption[] => {
    return Array.isArray(words) && words.every(
        word => word && typeof word === 'object' && 'id' in word && 'options' in word
    );
}


export default function Level({ exam, showSolutions = false, onFinish, editing = false }: Props) {
    const levelBgColor = "bg-ielts-level-light";
    const [multipleChoicesDone, setMultipleChoicesDone] = useState<{ id: string; amount: number }[]>([]);
    const [showQuestionsModal, setShowQuestionsModal] = useState(false);

    const { setBgColor } = useExamStore((state) => state);
    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 [shuffleMaps, setShuffleMaps] = useExamStore((state) => [state.shuffleMaps, state.setShuffleMaps])
    const [currentExercise, setCurrentExercise] = useState<Exercise>();
    const [showPartDivider, setShowPartDivider] = useState<boolean>(typeof exam.parts[0].intro === "string" && !showSolutions);

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

    const [contextWord, setContextWord] = useState<string | undefined>(undefined);
    const [contextWordLine, setContextWordLine] = useState<number | undefined>(undefined);

    useEffect(() => {
        if (showSolutions && exerciseIndex && userSolutions[exerciseIndex].shuffleMaps) {
            setShuffleMaps(userSolutions[exerciseIndex].shuffleMaps as ShuffleMap[])
        }
    }, [showSolutions, exerciseIndex, setShuffleMaps, userSolutions])

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

    const getExercise = () => {
        let exercise = exam.parts[partIndex]?.exercises[exerciseIndex];
        if (!exercise) return undefined;

        exercise = {
            ...exercise,
            userSolutions: userSolutions.find((x) => x.exercise === exercise.id)?.solutions || [],
        };

        if (exam.shuffle && exercise.type === "multipleChoice" && !showSolutions) {
            console.log("Shuffling");
            const exerciseShuffles = userSolutions[exerciseIndex].shuffleMaps;
            if (exerciseShuffles && exerciseShuffles.length == 0) {
                const newShuffleMaps: ShuffleMap[] = [];

                exercise.questions = exercise.questions.map(question => {
                    const options = [...question.options];
                    let shuffledOptions = [...options].sort(() => Math.random() - 0.5);

                    const newOptions = options.map((option, index) => ({
                        id: option.id,
                        text: shuffledOptions[index].text
                    }));

                    const optionMapping = options.reduce<{ [key: string]: string }>((acc, originalOption) => {
                        const shuffledPosition = newOptions.find(newOpt => newOpt.text === originalOption.text)?.id;
                        if (shuffledPosition) {
                            acc[shuffledPosition] = originalOption.id;
                        }
                        return acc;
                    }, {});

                    newShuffleMaps.push({ id: question.id, map: optionMapping });

                    return { ...question, options: newOptions };
                });

                setShuffleMaps(newShuffleMaps);
            } else {
                console.log("retrieving shuffles");
                exercise.questions = exercise.questions.map(question => {
                    const questionShuffleMap = shuffleMaps.find(map => map.id === question.id);
                    if (questionShuffleMap) {
                        const newOptions = question.options.map(option => ({
                            id: option.id,
                            text: question.options.find(o => questionShuffleMap.map[o.id] === option.id)?.text || option.text
                        }));
                        return { ...question, options: newOptions };
                    }
                    return question;
                });
            }
        } else if (exam.shuffle && exercise.type === "fillBlanks" && typeCheckWordsMC(exercise.words) && !showSolutions) {
            if (shuffleMaps.length === 0 && !showSolutions) {
                const newShuffleMaps: ShuffleMap[] = [];

                exercise.words = exercise.words.map(word => {
                    if ('options' in word) {
                        const options = { ...word.options };
                        const originalKeys = Object.keys(options);
                        const shuffledKeys = [...originalKeys].sort(() => Math.random() - 0.5);

                        const newOptions = shuffledKeys.reduce((acc, key, index) => {
                            acc[key as keyof typeof options] = options[originalKeys[index] as keyof typeof options];
                            return acc;
                        }, {} as { [key in keyof typeof options]: string });

                        const optionMapping = originalKeys.reduce((acc, key, index) => {
                            acc[key as keyof typeof options] = shuffledKeys[index];
                            return acc;
                        }, {} as { [key in keyof typeof options]: string });

                        newShuffleMaps.push({ id: word.id, map: optionMapping });

                        return { ...word, options: newOptions };
                    }
                    return word;
                });

                setShuffleMaps(newShuffleMaps);
            }
        }
        return exercise;
    };

    useEffect(() => {
        if (exerciseIndex !== -1) {
            setCurrentExercise(getExercise());
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [partIndex, exerciseIndex, shuffleMaps, exam.parts[partIndex].context]);


    useEffect(() => {
        const regex = /.*?['"](.*?)['"] in line (\d+)\?$/;
        if (exerciseIndex !== -1 && currentExercise && currentExercise.type === "multipleChoice" && currentExercise.questions[storeQuestionIndex].prompt) {
            const match = currentExercise.questions[storeQuestionIndex].prompt.match(regex);
            if (match) {
                const word = match[1];
                const originalLineNumber = match[2];

                if (word !== contextWord) {
                    setContextWord(word);
                }

                const updatedPrompt = currentExercise.questions[storeQuestionIndex].prompt.replace(
                    `in line ${originalLineNumber}`,
                    `in line ${contextWordLine || originalLineNumber}`
                );

                currentExercise.questions[storeQuestionIndex].prompt = updatedPrompt;
            } else {
                setContextWord(undefined);
            }
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [currentExercise, storeQuestionIndex]);

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

        /*if (storeQuestionIndex > 0 || currentExercise?.type == "fillBlanks") {
            setMultipleChoicesDone((prev) => [...prev.filter((x) => x.id !== currentExercise!.id), { id: currentExercise!.id, amount: currentExercise?.type == "fillBlanks" ? currentExercise.words.length - 1 : storeQuestionIndex }]);
        }*/

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

        if (partIndex + 1 < exam.parts.length && !hasExamEnded && (showQuestionsModal || showSolutions)) {
            if (!showSolutions && exam.parts[0].intro) {
                setShowPartDivider(true);
                setBgColor(levelBgColor);
            }
            setPartIndex(partIndex + 1);
            setExerciseIndex(!!exam.parts[partIndex + 1].context ? -1 : 0);
            setStoreQuestionIndex(0);
            setMultipleChoicesDone((prev) => [...prev.filter((x) => x.id !== currentExercise!.id), { id: currentExercise!.id, amount: currentExercise?.type == "fillBlanks" ? currentExercise.words.length - 1 : storeQuestionIndex }]);
            return;
        }

        if (partIndex + 1 < exam.parts.length && !hasExamEnded && !showQuestionsModal && !showSolutions) {
            setShowQuestionsModal(true);
            return;
        }

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

        setHasExamEnded(false);

        if (solution) {
            let stat = { ...solution, module: "level" as Module, exam: exam.id }
            if (exam.shuffle) {
                stat.shuffleMaps = shuffleMaps
            }
            onFinish([...userSolutions.filter((x) => x.exercise !== solution.exercise), { ...stat }]);
        } else {
            onFinish(userSolutions);
        }
    };

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

        setExerciseIndex(exerciseIndex - 1);
        if (exerciseIndex - 1 === -1) {
            setPartIndex(partIndex - 1);
            const lastPartExerciseIndex = exam.parts[partIndex - 1].exercises.length - 1;
            const previousExercise = exam.parts[partIndex - 1].exercises[lastPartExerciseIndex];
            if (previousExercise.type === "multipleChoice") {
                setStoreQuestionIndex(previousExercise.questions.length - 1)
            }
            const multipleChoiceQuestionsDone = [];
            for (let i = 0; i < exam.parts.length; i++) {
                if (i == (partIndex - 1)) break;
                for (let j = 0; j < exam.parts[i].exercises.length; j++) {
                    const exercise = exam.parts[i].exercises[j];
                    if (exercise.type === "multipleChoice") {
                        multipleChoiceQuestionsDone.push({ id: exercise.id, amount: exercise.questions.length - 1 })
                    }
                    if (exercise.type === "fillBlanks") {
                        multipleChoiceQuestionsDone.push({ id: exercise.id, amount: exercise.words.length - 1 })
                    }
                }
            }
            setMultipleChoicesDone(multipleChoiceQuestionsDone);
        }

    };

    useEffect(() => {
        if (exerciseIndex === -1) {
            nextExercise()
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [exerciseIndex])

    const calculateExerciseIndex = () => {
        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) => { return acc + curr.amount }, 0)
        );
    };

    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">
                    <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>
                </div>
                <TextComponent
                    part={exam.parts[partIndex]}
                    contextWord={contextWord}
                    setContextWordLine={setContextWordLine}
                />
            </>
        </div>
    );

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

        if (currentExercise?.type === "multipleChoice") {
            return `Part ${partIndex + 1} (Questions ${currentExercise.questions[0].id} - ${currentExercise.questions[currentExercise.questions.length - 1].id})\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})\n\n${nextExercise.prompt}`
        }
    }

    const modalKwargs = () => {
        const allSolutionsCorrectLength = exam.parts[partIndex].exercises.every((exercise) => {
            const userSolution = userSolutions.find(x => x.exercise === exercise.id);
            if (exercise.type === "multipleChoice") {
                return userSolution?.solutions.length === exercise.questions.length;
            }
            if (exercise.type === "fillBlanks") {
                return userSolution?.solutions.length === exercise.words.length;
            }
            return false;
        });

        return {
            blankQuestions: !allSolutionsCorrectLength,
	        finishingWhat: "part",
	        onClose: partIndex !== exam.parts.length - 1 ? (
                function (x: boolean | undefined) { if (x) { setShowQuestionsModal(false); nextExercise(); } else { setShowQuestionsModal(false) } }
            ) : function (x: boolean | undefined) { if (x) { setShowQuestionsModal(false); onFinish(userSolutions); } else { setShowQuestionsModal(false) } }
        }
        
    }

    return (
        <>
            <div className={clsx("flex flex-col h-full w-full gap-8 items-center", showPartDivider && "justify-center")}>
                <QuestionsModal isOpen={showQuestionsModal} {...modalKwargs()} />
                {
                    !(partIndex === 0 && storeQuestionIndex === 0 && showPartDivider) &&
                    <Timer minTimer={exam.minTimer} disableTimer={showSolutions} standalone={true} />
                }
                {exam.parts[0].intro && showPartDivider ? <PartDivider part={exam.parts[partIndex]} partIndex={partIndex} onNext={() => { setShowPartDivider(false); setBgColor("bg-white") }} /> : (
                    <>
                        <ModuleTitle
                            partLabel={partLabel()}
                            minTimer={exam.minTimer}
                            exerciseIndex={calculateExerciseIndex()}
                            module="level"
                            totalExercises={countExercises(exam.parts.flatMap((x) => x.exercises))}
                            disableTimer={showSolutions || editing}
                            showTimer={typeof exam.parts[0].intro === "undefined"}
                        />
                        <div
                            className={clsx(
                                "mb-20 w-full",
                                partIndex > -1 && exerciseIndex > -1 && !!exam.parts[partIndex].context && "grid grid-cols-2 gap-4",
                            )}>
                            {partIndex > -1 && !!exam.parts[partIndex].context && renderText()}

                            {exerciseIndex > -1 &&
                                partIndex > -1 &&
                                exerciseIndex < exam.parts[partIndex].exercises.length &&
                                !showSolutions &&
                                !editing &&
                                currentExercise &&
                                renderExercise(currentExercise, exam.id, nextExercise, previousExercise)}

                            {exerciseIndex > -1 &&
                                partIndex > -1 &&
                                exerciseIndex < exam.parts[partIndex].exercises.length &&
                                (showSolutions || editing) &&
                                currentExercise &&
                                renderSolution(currentExercise, nextExercise, previousExercise)}
                        </div>
                        {/*exerciseIndex === -1 && partIndex > 0 && (
                            <div className="self-end flex justify-between w-full gap-8 absolute bottom-8 left-0 px-8">
                                <Button
                                    color="purple"
                                    variant="outline"
                                    onClick={() => {
                                        setExerciseIndex(exam.parts[partIndex - 1].exercises.length - 1);
                                        setPartIndex(partIndex - 1);
                                    }}
                                    className="max-w-[200px] w-full"
                                    disabled={
                                        exam && typeof partIndex !== "undefined" && exam.module === "level" && 
                                        typeof exam.parts[0].intro === "string" && storeQuestionIndex === 0}    
                                >
                                    Back
                                </Button>

                                <Button color="purple" onClick={() => nextExercise()} className="max-w-[200px] self-end w-full">
                                    Next
                                </Button>
                            </div>
                        )*/}
                        {exerciseIndex === -1 && partIndex === 0 && (
                            <Button color="purple" onClick={() => nextExercise()} className="max-w-[200px] self-end w-full">
                                Start now
                            </Button>
                        )}
                    </>
                )}
            </div>
        </>
    );
}
