import { Difficulty, FillBlanksExercise, ReadingPart } from "@/interfaces/exam";
import { useCallback, useEffect, useReducer, useState } from "react";
import BlanksEditor from "..";
import { Card, CardContent } from "@/components/ui/card";
import { MdEdit, MdEditOff } from "react-icons/md";
import FillBlanksWord from "./FillBlanksWord";
import { FaPlus } from "react-icons/fa";
import useExamEditorStore from "@/stores/examEditor";
import { blanksReducer, BlankState, getTextSegments } from "../BlanksReducer";
import useSectionEdit from "@/components/ExamEditor/Hooks/useSectionEdit";
import { AlertItem } from "../../Shared/Alert";
import validateBlanks from "../validateBlanks";
import { toast } from "react-toastify";
import setEditingAlert from "../../Shared/setEditingAlert";
import PromptEdit from "../../Shared/PromptEdit";
import { uuidv4 } from "@/utils/uuid";

interface Word {
    letter: string;
    word: string;
}

const FillBlanksLetters: React.FC<{ exercise: FillBlanksExercise; sectionId: number }> = ({ exercise, sectionId }) => {
    const { currentModule, dispatch } = useExamEditorStore();
    const difficulty = useExamEditorStore((state) => state.modules[currentModule].difficulty);
    const { state } = useExamEditorStore(
        (state) => state.modules[currentModule].sections.find((section) => section.sectionId === sectionId)!
    );

    const section = state as ReadingPart;

    const [alerts, setAlerts] = useState<AlertItem[]>([]);

    const [local, setLocal] = useState(exercise);
    const [selectedBlankId, setSelectedBlankId] = useState<string | null>(null);
    const [answers, setAnswers] = useState<Map<string, string>>(
        new Map(exercise.solutions.map(({ id, solution }) => [id, solution]))
    );
    const [isEditMode, setIsEditMode] = useState(false);
    const [newWord, setNewWord] = useState('');

    const [editing, setEditing] = useState(false);

    const updateLocal = (exercise: FillBlanksExercise) => {
        setLocal(exercise);
        setEditingAlert(true, setAlerts);
        setEditing(true);
    };

    const [blanksState, blanksDispatcher] = useReducer(blanksReducer, {
        text: exercise.text || "",
        blanks: [],
        selectedBlankId: null,
        draggedItemId: null,
        textMode: false,
        setEditing,
    });

    const { handleSave, handleDiscard, handleDelete, handlePractice } = useSectionEdit({
        sectionId,
        editing,
        setEditing,
        onSave: () => {
            if (!validateBlanks(blanksState.blanks, answers, alerts, setAlerts)) {
                toast.error("Please fix the errors before saving!");
                return;
            }

            setEditing(false);
            setAlerts([]);
            
            const updatedExercise = {
                ...local,
                text: blanksState.text,
                solutions: Array.from(answers.entries()).map(([id, solution]) => ({
                    uuid: local.solutions.find(sol => sol.id === id)?.uuid || uuidv4(),
                    id,
                    solution
                }))
            };

            const newState = { ...section };
            newState.exercises = newState.exercises.map((ex) =>
                ex.id === exercise.id ? updatedExercise : ex
            );

            dispatch({ type: 'UPDATE_SECTION_STATE', payload: { sectionId, update: newState, module: currentModule } });
        },
        onDiscard: () => {
            setSelectedBlankId(null);
            setAnswers(new Map(exercise.solutions.map(({ id, solution }) => [id, solution])));
            setIsEditMode(false);
            setNewWord('');
            setLocal(exercise);

            blanksDispatcher({ type: "RESET", payload: { text: exercise.text } });
            blanksDispatcher({ type: "SET_TEXT", payload: exercise.text || "" });

            const tokens = getTextSegments(exercise.text || "");
            const initialBlanks = tokens.reduce((acc, token, idx) => {
                if (token.type === 'blank') {
                    acc.push({ id: token.id, position: idx });
                }
                return acc;
            }, [] as BlankState[]);
            blanksDispatcher({ type: "SET_BLANKS", payload: initialBlanks });

        },
        onDelete: () => {
            const newSection = {
                ...section,
                exercises: section.exercises.filter((ex) => ex.id !== local.id)
            };
            dispatch({ type: "UPDATE_SECTION_STATE", payload: { sectionId, update: newSection, module: currentModule } });
        },
        onPractice: () => {
            const updatedExercise = {
                ...local,
                isPractice: !local.isPractice,
            };
            const newState = { ...section };
            newState.exercises = newState.exercises.map((ex) =>
                ex.id === exercise.id ? updatedExercise : ex
            );
            setLocal((prev) => ({...prev, isPractice: !local.isPractice}))
            dispatch({ type: 'UPDATE_SECTION_STATE', payload: { sectionId, update: newState, module: currentModule } });
        }
    });

    useEffect(() => {
        if (!editing) {
            setLocal(exercise);
            setAnswers(new Map(exercise.solutions.map(({ id, solution }) => [id, solution])));
        }
    }, [exercise, editing]);

    const handleWordSelect = (word: string) => {
        if (!selectedBlankId) return;

        if (!editing) setEditing(true);

        const newAnswers = new Map(answers);
        newAnswers.set(selectedBlankId, word);

        setAnswers(newAnswers);

        setLocal(prev => ({
            ...prev,
            solutions: Array.from(newAnswers.entries()).map(([id, solution]) => ({
                uuid: prev.solutions.find(sol => sol.id === id)?.uuid || uuidv4(),
                id,
                solution
            }))
        }));
    };

    const handleAddWord = () => {
        const word = newWord.trim();
        if (!word) return;

        setLocal(prev => {
            const nextLetter = String.fromCharCode(65 + prev.words.length);
            return {
                ...prev,
                words: [...prev.words, { letter: nextLetter, word }]
            };
        });
        setNewWord('');
    };

    const handleRemoveWord = (index: number) => {
        if (!editing) setEditing(true);

        if (answers.size === 1) {
            toast.error("There needs to be at least 1 word!");
            return;
        } 

        setLocal(prev => {
            const newWords = prev.words.filter((_, i) => i !== index) as Word[];
            const removedWord = prev.words[index] as Word;

            const newAnswers = new Map(answers);
            for (const [blankId, answer] of newAnswers.entries()) {
                if (answer === removedWord.word) {
                    newAnswers.delete(blankId);
                }
            }
            setAnswers(newAnswers);

            return {
                ...prev,
                words: newWords,
                solutions: Array.from(newAnswers.entries()).map(([id, solution]) => ({
                    uuid: prev.solutions.find(sol => sol.id === id)?.uuid || uuidv4(),
                    id,
                    solution
                }))
            };
        });
    };

    const handleEditWord = (index: number, newWord: string) => {
        if (!editing) setEditing(true);
        

        setLocal(prev => {
            const newWords = [...prev.words] as Word[];
            const oldWord = newWords[index].word;
            newWords[index] = { ...newWords[index], word: newWord };

            const newAnswers = new Map(answers);
            for (const [blankId, answer] of newAnswers.entries()) {
                if (answer === oldWord) {
                    newAnswers.set(blankId, newWord);
                }
            }
            setAnswers(newAnswers);

            return {
                ...prev,
                words: newWords,
                solutions: Array.from(newAnswers.entries()).map(([id, solution]) => ({
                    uuid: prev.solutions.find(sol => sol.id === id)?.uuid || uuidv4(),
                    id,
                    solution
                }))
            };
        });
    };

    const handleBlankRemove = (blankId: number) => {
        if (!editing) setEditing(true);
        
        const newAnswers = new Map(answers);
        newAnswers.delete(blankId.toString());
        setAnswers(newAnswers);

        setLocal(prev => ({
            ...prev,
            solutions: Array.from(newAnswers.entries()).map(([id, solution]) => ({
                uuid: prev.solutions.find(sol => sol.id === id)?.uuid || uuidv4(),
                id,
                solution
            }))
        }));
        blanksDispatcher({ type: "REMOVE_BLANK", payload: blankId });
    };

    useEffect(() => {
        validateBlanks(blanksState.blanks, answers, alerts, setAlerts);
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [answers, blanksState.blanks, blanksState.textMode])

    useEffect(()=> {
        setEditingAlert(editing, setAlerts);
    }, [editing])

    const saveDifficulty = useCallback((diff: Difficulty) => {
        if (!difficulty.includes(diff)) {
            dispatch({ type: 'UPDATE_MODULE', payload: { updates: { difficulty: [...difficulty, diff]} } });
        }
        const updatedExercise = { ...exercise, difficulty: diff };
        const newState = { ...section };
        newState.exercises = newState.exercises.map((ex) => ex.id === exercise.id ? updatedExercise : ex );
        dispatch({ type: 'UPDATE_SECTION_STATE', payload: { sectionId, update: newState, module: currentModule } });
    }, [currentModule, difficulty, dispatch, exercise, section, sectionId]);

    return (
        <div className="space-y-4">
            <BlanksEditor
                alerts={alerts}
                editing={editing}
                state={blanksState}
                difficulty={exercise.difficulty}
                saveDifficulty={saveDifficulty}
                blanksDispatcher={blanksDispatcher}
                description="Place blanks and assign words from the word bank"
                initialText={local.text}
                module={currentModule}
                showBlankBank={true}
                onBlankSelect={(blankId) => setSelectedBlankId(blankId?.toString() || null)}
                onBlankRemove={handleBlankRemove}
                onSave={handleSave}
                onDiscard={handleDiscard}
                onDelete={handleDelete}
                setEditing={setEditing}
                onPractice={handlePractice}
                isEvaluationEnabled={!local.isPractice}
                prompt={local.prompt}
                updatePrompt={(prompt: string) => updateLocal({...local, prompt})}
            >
                <>
                    {!blanksState.textMode && <Card className="p-4">
                        <CardContent>
                            <div className="flex justify-between items-center mb-4">
                                <div className="text-lg font-semibold">Word Bank</div>
                                <button
                                    onClick={() => setIsEditMode(!isEditMode)}
                                    className="p-2 hover:bg-gray-100 rounded-lg transition-colors"
                                >
                                    {isEditMode ?
                                        <MdEditOff size={20} className="text-gray-500" /> :
                                        <MdEdit size={20} className="text-gray-500" />
                                    }
                                </button>
                            </div>

                            <div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-2">
                                {(local.words as Word[]).map((wordItem, index) => (
                                    <FillBlanksWord
                                        key={wordItem.letter}
                                        letter={wordItem.letter}
                                        word={wordItem.word}
                                        isSelected={answers.get(selectedBlankId || '') === wordItem.word}
                                        onClick={() => handleWordSelect(wordItem.word)}
                                        onRemove={isEditMode ? () => handleRemoveWord(index) : undefined}
                                        onEdit={isEditMode ? (newWord) => handleEditWord(index, newWord) : undefined}
                                        isEditMode={isEditMode}
                                    />
                                ))}
                            </div>

                            {isEditMode && (
                                <div className="flex flex-row mt-8">
                                    <input
                                        type="text"
                                        value={newWord}
                                        onChange={(e) => setNewWord(e.target.value)}
                                        placeholder="Enter new word"
                                        className="flex-1 px-3 py-2 border border-r-0 rounded-l-md focus:outline-none"
                                        name=""
                                    />
                                    <button
                                        onClick={handleAddWord}
                                        disabled={!isEditMode || newWord === ""}
                                        className="px-4 bg-blue-500 text-white rounded-r-md border border-blue-500 hover:bg-blue-600 disabled:opacity-50 disabled:cursor-not-allowed"
                                    >
                                        <FaPlus className="h-4 w-4" />
                                    </button>
                                </div>

                            )}
                        </CardContent>
                    </Card>
                    }
                </>
            </BlanksEditor>
        </div>
    );
};

export default FillBlanksLetters;