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 from "@/stores/examStore";
import { countExercises } from "@/utils/moduleUtils";
import clsx from "clsx";
import { use, useEffect, useMemo, useState } from "react";
import TextComponent from "./TextComponent";
import PartDivider from "./PartDivider";
import Timer from "@/components/Medium/Timer";
import shuffleExamExercise from "./Shuffle";
import { Tab } from "@headlessui/react";
import Modal from "@/components/Modal";

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 {
        userSolutions,
        hasExamEnded,
        partIndex,
        exerciseIndex,
        questionIndex,
        shuffles,
        currentSolution,
        setBgColor,
        setUserSolutions,
        setHasExamEnded,
        setPartIndex,
        setExerciseIndex,
        setQuestionIndex,
        setShuffles,
        setCurrentSolution
    } = useExamStore((state) => state);

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

    const [showSubmissionModal, setShowSubmissionModal] = useState(false);
    const [showQuestionsModal, setShowQuestionsModal] = useState(false);
    const [continueAnyways, setContinueAnyways] = useState(false);
    const [textRender, setTextRender] = useState(false);
    const [changedPrompt, setChangedPrompt] = useState(false);
    const [nextExerciseCalled, setNextExerciseCalled] = useState(false);
    const [currentSolutionSet, setCurrentSolutionSet] = useState(false);

    const [seenParts, setSeenParts] = useState<Set<number>>(new Set(showSolutions ? exam.parts.map((_, index) => index) : [0]));

    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 [currentExercise, setCurrentExercise] = useState<Exercise | undefined>(undefined);
    const [showPartDivider, setShowPartDivider] = useState<boolean>(typeof exam.parts[0].intro === "string" && !showSolutions);
    const [startNow, setStartNow] = useState<boolean>(true && !showSolutions);

    useEffect(() => {
        if (currentExercise === undefined && partIndex === 0 && exerciseIndex === 0) {
            setCurrentExercise(exam.parts[0].exercises[0]);
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [currentExercise, partIndex, exerciseIndex]);

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

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

    const [showSolutionsSave, setShowSolutionsSave] = useState(showSolutions ? userSolutions.filter((x) => x.module === "level") : undefined)

    useEffect(() => {
        if (typeof currentSolution !== "undefined") {
            setUserSolutions([...userSolutions.filter((x) => x.exercise !== currentSolution.exercise), { ...currentSolution, module: "level" as Module, exam: exam.id, shuffleMaps: exam.shuffle ? [...shuffles.find((x) => x.exerciseID == currentExercise?.id)?.shuffles!] : [] }]);
            setCurrentSolutionSet(true);
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [currentSolution, exam.id, exam.shuffle, shuffles, currentExercise])

    useEffect(() => {
        if (typeof currentSolution !== "undefined") {
            setCurrentSolution(undefined);
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [currentSolution]);

    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 getExercise = () => {
        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;
    };

    useEffect(() => {
        setCurrentExercise(getExercise());
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [partIndex, exerciseIndex, questionIndex]);

    const next = () => {
        setNextExerciseCalled(true);
    }

    const nextExercise = () => {
        scrollToTop();

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

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

        if (partIndex + 1 < exam.parts.length && !hasExamEnded) {
            if (!answeredEveryQuestion(partIndex) && !continueAnyways && !showSolutions && !seenParts.has(partIndex + 1)) {
                modalKwargs();
                setShowQuestionsModal(true);
                return;
            }

            if (!showSolutions && exam.parts[0].intro && !seenParts.has(partIndex + 1)) {
                setShowPartDivider(true);
                setBgColor(levelBgColor);
            }

            setSeenParts(prev => new Set(prev).add(partIndex + 1));

            if (partIndex < exam.parts.length - 1 && exam.parts[partIndex + 1].context && !textRenderDisabled) {
                setTextRender(true);
            }
            setPartIndex(partIndex + 1);
            setExerciseIndex(0);
            setQuestionIndex(0);
            setCurrentSolutionSet(false);
            return;
        }

        if (partIndex + 1 === exam.parts.length && exerciseIndex === exam.parts[partIndex].exercises.length - 1 && !continueAnyways) {
            modalKwargs();
            setShowQuestionsModal(true);
        }

        setHasExamEnded(false);
        setCurrentSolutionSet(false);
        if (typeof showSolutionsSave !== "undefined") {
            onFinish(showSolutionsSave);
        } else {
            onFinish(userSolutions);
        }
    }

    useEffect(() => {
        if (nextExerciseCalled && currentSolutionSet) {
            nextExercise();
            setNextExerciseCalled(false);
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [nextExerciseCalled, currentSolutionSet])

    const previousExercise = (solution?: UserSolution) => {
        scrollToTop();

        if (exam.parts[partIndex].context && questionIndex === 0 && !textRender && !textRenderDisabled) {
            setTextRender(true);
            return;
        }

        if (questionIndex == 0) {
            setPartIndex(partIndex - 1);
            if (!seenParts.has(partIndex - 1)) {
                setBgColor(levelBgColor);
                setShowPartDivider(true);
                setQuestionIndex(0);
                setSeenParts(prev => new Set(prev).add(partIndex - 1));
                return;
            }

            const lastExerciseIndex = exam.parts[partIndex - 1].exercises.length - 1;
            const lastExercise = exam.parts[partIndex - 1].exercises[lastExerciseIndex];
            setExerciseIndex(lastExerciseIndex);

            if (lastExercise.type === "multipleChoice") {
                setQuestionIndex(lastExercise.questions.length - 1)
            } else {
                setQuestionIndex(0)
            }
            return;
        }

        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") {
                setQuestionIndex(previousExercise.questions.length - 1)
            }
        }

    };

    const calculateExerciseIndex = () => {
        return exam.parts.reduce((acc, curr, index) => {
            if (index < partIndex) {
                return acc + countExercises(curr.exercises)
            }
            return acc;
        }, 0) + (questionIndex + 1);
    };

    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 &&
                            <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}`
        }
    }

    const answeredEveryQuestion = (partIndex: number) => {
        return exam.parts[partIndex].exercises.every((exercise) => {
            const userSolution = userSolutions.find(x => x.exercise === exercise.id);
            switch (exercise.type) {
                case 'multipleChoice':
                    return userSolution?.solutions.length === exercise.questions.length;
                case 'fillBlanks':
                    return userSolution?.solutions.length === exercise.words.length;
                case 'writeBlanks':
                    return userSolution?.solutions.length === exercise.solutions.length;
                case 'matchSentences':
                    return userSolution?.solutions.length === exercise.sentences.length;
                case 'trueFalse':
                    return userSolution?.solutions.length === exercise.questions.length;
            }
            return false;
        });
    }

    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 (
            exerciseIndex !== -1 && 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;
                    }
                })
                setChangedPrompt(true);
            }
        }
    }, [contextWordLines]);


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

    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) => answeredEveryQuestion(partIndex));
            kwargs.onClose = function (x: boolean | undefined) { if (x) { setShowSubmissionModal(true); setShowQuestionsModal(false); } else { setShowQuestionsModal(false) } };
        }
        setQuestionModalKwargs(kwargs);
    }

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


    const memoizedRender = useMemo(() => {
        setChangedPrompt(false);
        return (
            <>
                {textRender && !textRenderDisabled ?
                    renderText() :
                    <>
                        {exam.parts[partIndex].context && renderText()}
                        {(showSolutions || editing) ?
                            currentExercise && renderSolution(currentExercise, nextExercise, previousExercise) :
                            currentExercise && renderExercise(currentExercise, exam.id, next, previousExercise)
                        }
                    </>
                }
            </>)
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [textRender, currentExercise, changedPrompt]);

    return (
        <>
            <div className={clsx("flex flex-col h-full w-full gap-8 items-center", showPartDivider && "justify-center")}>
                <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} />
                {
                    !(partIndex === 0 && questionIndex === 0 && (showPartDivider || startNow)) &&
                    <Timer minTimer={exam.minTimer} disableTimer={showSolutions} standalone={true} />
                }
                {(showPartDivider || startNow) ? <PartDivider part={exam.parts[partIndex]} partIndex={partIndex} onNext={() => { setShowPartDivider(false); setStartNow(false); setBgColor("bg-white"); }} /> : (
                    <>
                        {exam.parts[0].intro && (
                            <div className="w-full">
                                <Tab.Group className="w-[90%]" selectedIndex={partIndex} onChange={setPartIndex}>
                                    <Tab.List className="flex space-x-1 rounded-xl bg-ielts-level/20 p-1">
                                        {exam.parts.map((_, index) =>
                                            <Tab key={index} onClick={(e) => {
                                                /*
                                                    // If client wants to revert uncomment and remove the added if statement
                                                    if (!seenParts.has(index)) {
                                                        e.preventDefault();
                                                    } else {
                                                 */
                                                setExerciseIndex(0);
                                                setQuestionIndex(0);
                                                if (!seenParts.has(index)) {
                                                    setShowPartDivider(true);
                                                    setBgColor(levelBgColor);
                                                    setSeenParts(prev => new Set(prev).add(index));
                                                }
                                            }}
                                                className={({ selected }) =>
                                                    clsx(
                                                        "w-full rounded-lg py-2.5 text-sm font-medium leading-5 text-ielts-level/80",
                                                        "ring-white ring-opacity-60 focus:outline-none",
                                                        "transition duration-300 ease-in-out hover:bg-white/70",
                                                        selected && "bg-white shadow",
                                                        // seenParts.includes(index) ? "hover:bg-white/70" : "cursor-not-allowed"
                                                    )
                                                }
                                            >{`Part ${index + 1}`}</Tab>
                                        )
                                        }
                                    </Tab.List>
                                </Tab.Group>
                            </div>
                        )}
                        <ModuleTitle
                            examLabel={exam.label}
                            partLabel={partLabel()}
                            minTimer={exam.minTimer}
                            exerciseIndex={calculateExerciseIndex()}
                            module="level"
                            totalExercises={countExercises(exam.parts.flatMap((x) => x.exercises))}
                            disableTimer={showSolutions || editing}
                            showTimer={false}
                            {...mcNavKwargs}
                        />
                        <div
                            className={clsx(
                                "mb-20 w-full",
                                !!exam.parts[partIndex].context && !textRender && "grid grid-cols-2 gap-4",
                            )}>
                            {memoizedRender}
                        </div>
                    </>
                )}
            </div>
        </>
    );
}
