import React, { useCallback, useMemo, useReducer, useEffect, ReactNode } from "react";
import {
    DndContext,
    DragEndEvent,
    DragStartEvent,
    MeasuringStrategy,
    PointerSensor,
    useSensor,
    useSensors,
} from "@dnd-kit/core";
import {
    restrictToWindowEdges,
    snapCenterToCursor,
} from "@dnd-kit/modifiers";
import AutoExpandingTextArea from "@/components/Low/AutoExpandingTextarea";
import Header from "../../Shared/Header";
import Alert, { AlertItem } from "../Shared/Alert";
import clsx from "clsx";
import { Card, CardContent } from "@/components/ui/card";
import { Blank, DropZone } from "./DragNDrop";
import { getTextSegments, BlankState, BlanksState, BlanksAction, BlankToken } from "./BlanksReducer";
import PromptEdit from "../Shared/PromptEdit";
import { Difficulty } from "@/interfaces/exam";


interface Props {
    title?: string;
    initialText: string;
    description: string;
    difficulty?: Difficulty;
    saveDifficulty: (difficulty: Difficulty) => void; 
    state: BlanksState;
    module: string;
    editing: boolean;
    showBlankBank: boolean;
    alerts: AlertItem[];
    prompt: string;
    updatePrompt: (prompt: string) => void;
    setEditing: React.Dispatch<React.SetStateAction<boolean>>;
    blanksDispatcher: React.Dispatch<BlanksAction>
    onBlankSelect?: (blankId: number | null) => void;
    onBlankRemove: (blankId: number) => void;
    onSave: () => void;
    onDiscard: () => void;
    onDelete: () => void;
    onPractice: () => void;
    isEvaluationEnabled?: boolean;
    children: ReactNode;
}

const BlanksEditor: React.FC<Props> = ({
    title = "Fill Blanks",
    initialText,
    description,
    difficulty,
    saveDifficulty,
    state,
    editing,
    module,
    children,
    showBlankBank = true,
    alerts,
    blanksDispatcher,
    onBlankSelect,
    onBlankRemove,
    onSave,
    onDiscard,
    onDelete,
    onPractice,
    isEvaluationEnabled,
    setEditing,
    prompt,
    updatePrompt
}) => {

    useEffect(() => {
        const tokens = getTextSegments(initialText);
        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_TEXT", payload: initialText });
        blanksDispatcher({ type: "SET_BLANKS", payload: initialBlanks });
    }, [initialText, blanksDispatcher]);

    const tokens = useMemo(() => {
        return getTextSegments(state.text || "");
    }, [state.text]);

    const handleDragStart = useCallback((event: DragStartEvent) => {
        blanksDispatcher({ type: "SET_DRAGGED_ITEM", payload: event.active.id.toString() });
    }, [blanksDispatcher]);

    const handleDragEnd = useCallback(
        (event: DragEndEvent) => {
            const { active, over } = event;
            if (!over) return;

            const blankId = parseInt(active.id.toString().split("-").pop() || "");
            const dropIndex = parseInt(over.id.toString().split("-")[1]);

            blanksDispatcher({
                type: "MOVE_BLANK",
                payload: { blankId, newPosition: dropIndex },
            });

            blanksDispatcher({ type: "SET_DRAGGED_ITEM", payload: null });
        },
        [blanksDispatcher]
    );

    const handleTextChange = useCallback(
        (newText: string) => {
            const processedText = newText.replace(/\[(\d+)\]/g, "{{$1}}");

            const existingBlankIds = getTextSegments(state.text)
                .filter(token => token.type === 'blank')
                .map(token => (token as BlankToken).id);

            const newBlankIds = getTextSegments(processedText)
                .filter(token => token.type === 'blank')
                .map(token => (token as BlankToken).id);

            const removedBlankIds = existingBlankIds.filter(id => !newBlankIds.includes(id));

            removedBlankIds.forEach(id => {
                onBlankRemove(id);
            });

            blanksDispatcher({ type: "SET_TEXT", payload: processedText });
        },
        [blanksDispatcher, state.text, onBlankRemove]
    );

    useEffect(() => {
        if (onBlankSelect !== undefined) onBlankSelect(state.selectedBlankId);
    }, [state.selectedBlankId, onBlankSelect]);

    const handleBlankSelect = (blankId: number) => {
        blanksDispatcher({
            type: "SELECT_BLANK",
            payload: blankId === state.selectedBlankId ? null : blankId,
        });
    };

    const handleBlankRemove = useCallback((blankId: number) => {
        onBlankRemove(blankId);
        blanksDispatcher({ type: "REMOVE_BLANK", payload: blankId });
    }, [blanksDispatcher, onBlankRemove]);

    const sensors = useSensors(
        useSensor(PointerSensor, {
            activationConstraint: {
                distance: 4,
                tolerance: 5,
            },
        })
    );

    const modifiers = [snapCenterToCursor, restrictToWindowEdges];

    const measuring = {
        droppable: {
            strategy: MeasuringStrategy.Always,
        },
    };

    return (
        <div className="space-y-4 p-4">
            <Header
                title={title}
                description={description}
                editing={editing}
                difficulty={difficulty}
                saveDifficulty={saveDifficulty}
                handleSave={onSave}
                handleDelete={onDelete}
                handleDiscard={onDiscard}
                handlePractice={onPractice}
                isEvaluationEnabled={isEvaluationEnabled}
            />
            {alerts.length > 0 && <Alert alerts={alerts} />}
            <PromptEdit value={prompt} onChange={(text: string) => updatePrompt(text)} />
            <Card>
                <CardContent className="p-4 text-white font-semibold flex gap-2">
                    <button
                        onClick={() => blanksDispatcher({ type: "ADD_BLANK" })}
                        className={`px-3 py-1.5 bg-ielts-${module} rounded-md hover:bg-ielts-${module}/50 transition-colors`}
                    >
                        Add Blank
                    </button>
                    <button
                        onClick={() => blanksDispatcher({ type: "TOGGLE_EDIT_MODE" })}
                        className={clsx(
                            "px-3 py-1.5 rounded-md transition-colors",
                            `bg-ielts-${module} text-white hover:bg-ielts-${module}/50`
                        )}
                    >
                        {state.textMode ? "Drag Mode" : "Text Mode"}
                    </button>
                </CardContent>
            </Card>

            <DndContext
                sensors={sensors}
                onDragStart={handleDragStart}
                onDragEnd={handleDragEnd}
                modifiers={modifiers}
                measuring={measuring}
            >
                <Card>
                    <CardContent className="p-4">
                        {state.textMode ? (
                            <AutoExpandingTextArea
                                value={state.text.replace(/{{(\d+)}}/g, "[$1]")}
                                onChange={(text) => { handleTextChange(text); if (!editing) setEditing(true) }}
                                className="w-full h-full min-h-[200px] p-2 bg-white border rounded-md"
                                placeholder="Enter text here. Use [1], [2], etc. for blanks..."
                            />
                        ) : (
                            <div className="leading-relaxed p-4">
                                {tokens.map((token, index) => {
                                    const isWordToken = token.type === 'text' && !token.isWhitespace;
                                    const showDropZone = isWordToken || token.type === 'blank';

                                    return (
                                        <React.Fragment key={index}>
                                            {showDropZone && <DropZone index={index} module={module} />}
                                            {token.type === 'blank' ? (
                                                <Blank
                                                    id={token.id}
                                                    module={module}
                                                    variant="text"
                                                    isSelected={token.id === state.selectedBlankId}
                                                    isDragging={state.draggedItemId === `text-blank-${token.id}`}
                                                />
                                            ) : token.isLineBreak ? (
                                                <br />
                                            ) : (
                                                <span className="select-none">{token.content}</span>
                                            )}
                                        </React.Fragment>
                                    );
                                })}
                                {tokens.length > 0 &&
                                    tokens[tokens.length - 1].type === 'text' && (
                                        <DropZone index={tokens.length} module={module} />
                                    )}
                            </div>
                        )}
                    </CardContent>
                </Card>


                {(!state.textMode && showBlankBank) && (
                    <Card>
                        <CardContent className="flex flex-wrap gap-2 p-4">
                            {state.blanks.map(blank => (
                                <Blank
                                    key={blank.id}
                                    id={blank.id}
                                    module={module}
                                    variant="bank"
                                    isSelected={blank.id === state.selectedBlankId}
                                    isDragging={state.draggedItemId === `bank-blank-${blank.id}`}
                                    onSelect={handleBlankSelect}
                                    onRemove={handleBlankRemove}
                                    disabled={state.textMode}
                                />
                            ))}
                        </CardContent>
                    </Card>
                )}
                {children}
            </DndContext>
        </div>
    );
}

export default BlanksEditor;
