import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
import Button from "@/components/Low/Button";
import Modal from "@/components/Modal";
import useExamEditorStore from "@/stores/examEditor";
import { PRESETS, isValidPresetID } from "./presets";
import AudioPlayer from "@/components/Low/AudioPlayer";
import Select from "@/components/Low/Select";
import AutoExpandingTextArea from "@/components/Low/AutoExpandingTextarea";
import { ListeningInstructionsState } from "@/stores/examEditor/types";
import { debounce } from "lodash";
import axios from "axios";
import { toast } from "react-toastify";
import { playSound } from "@/utils/sound";
import { BsArrowRepeat } from "react-icons/bs";
import { GiBrain } from "react-icons/gi";

const ListeningInstructions: React.FC = () => {
    const { dispatch } = useExamEditorStore();
    const [loading, setLoading] = useState(false);
    const { instructionsState: globalInstructions, sections } = useExamEditorStore(s => s.modules["listening"]);
    const [localInstructions, setLocalInstructions] = useState<ListeningInstructionsState>(globalInstructions);
    const pendingUpdatesRef = useRef<Partial<ListeningInstructionsState>>({});

    useEffect(() => {
        if (globalInstructions) {
            setLocalInstructions(globalInstructions);
        }
    }, [globalInstructions]);

    const debouncedUpdateGlobal = useMemo(() => {
        return debounce(() => {
            if (Object.keys(pendingUpdatesRef.current).length > 0) {
                dispatch({
                    type: "UPDATE_MODULE",
                    payload: {
                        module: "listening",
                        updates: {
                            instructionsState: {
                                ...globalInstructions,
                                ...pendingUpdatesRef.current
                            }
                        }
                    }
                });
                pendingUpdatesRef.current = {};
            }
        }, 1000);
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [dispatch, globalInstructions]);

    const updateInstructionsAndSchedule = useCallback((
        updates: Partial<ListeningInstructionsState> | ((prev: ListeningInstructionsState) => Partial<ListeningInstructionsState>),
        schedule: boolean = true
    ) => {
        const newUpdates = typeof updates === 'function' ? updates(localInstructions) : updates;

        setLocalInstructions(prev => ({
            ...prev,
            ...newUpdates
        }));

        if (schedule) {
            pendingUpdatesRef.current = {
                ...pendingUpdatesRef.current,
                ...newUpdates
            };
            debouncedUpdateGlobal();
        } else {
            dispatch({
                type: "UPDATE_MODULE",
                payload: {
                    module: "listening",
                    updates: {
                        instructionsState: {
                            ...globalInstructions,
                            ...newUpdates
                        }
                    }
                }
            });
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [dispatch, debouncedUpdateGlobal]);

    const setIsOpen = useCallback((isOpen: boolean) => {
        updateInstructionsAndSchedule({ isInstructionsOpen: isOpen }, false);
    }, [updateInstructionsAndSchedule]);

    const onOptionChange = useCallback((option: { value: string, label: string }) => {
        const sectionIds = sections.map(s => s.sectionId);
        const presetID = [...sectionIds].sort((a, b) => a - b).join('_');
        const preset = isValidPresetID(presetID) ? PRESETS[presetID] : null;

        updateInstructionsAndSchedule(prev => {
            const updates: Partial<ListeningInstructionsState> = {
                chosenOption: option
            };

            if (option.value === "Automatic" && preset) {
                updates.currentInstructions = preset.text;
                updates.currentInstructionsURL = preset.url;
            } else if (option.value === "Custom") {
                updates.currentInstructions = prev.customInstructions || "";
                updates.currentInstructionsURL = "";
            }

            return updates;
        }, false);
    }, [sections, updateInstructionsAndSchedule]);

    const onCustomInstructionChange = useCallback((text: string) => {
        updateInstructionsAndSchedule({
            chosenOption: { value: 'Custom', label: 'Custom' },
            customInstructions: text,
            currentInstructions: text
        });
    }, [updateInstructionsAndSchedule]);

    useEffect(() => {
        const sectionIds = sections.map(s => s.sectionId);
        const presetID = [...sectionIds].sort((a, b) => a - b).join('_');

        if (isValidPresetID(presetID)) {
            const preset = PRESETS[presetID];
            updateInstructionsAndSchedule(prev => {
                const updates: Partial<ListeningInstructionsState> = {
                    presetInstructions: preset.text,
                    presetInstructionsURL: preset.url,
                };

                if (prev.chosenOption?.value === "Automatic") {
                    updates.currentInstructions = preset.text;
                    updates.currentInstructionsURL = preset.url;
                }

                return updates;
            }, false);
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [sections.length]);

    useEffect(() => {
        return () => {
            if (Object.keys(pendingUpdatesRef.current).length > 0) {
                dispatch({
                    type: "UPDATE_MODULE",
                    payload: {
                        module: "listening",
                        updates: {
                            instructionsState: {
                                ...globalInstructions,
                                ...pendingUpdatesRef.current
                            }
                        }
                    }
                });
            }
        };
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [dispatch]);

    const options = [
        { value: 'Automatic', label: 'Automatic' },
        { value: 'Custom', label: 'Custom' }
    ];

    const generateInstructionsMP3 = useCallback(async () => {
        if (!localInstructions.currentInstructions) {
            toast.error('Please enter instructions text first');
            return;
        }

        setLoading(true);

        try {
            const response = await axios.post(
                '/api/exam/media/instructions',
                {
                    text: localInstructions.currentInstructions
                },
                {
                    responseType: 'arraybuffer',
                    headers: {
                        'Accept': 'audio/mpeg'
                    }
                }
            );

            if (localInstructions.currentInstructionsURL?.startsWith('blob:')) {
                URL.revokeObjectURL(localInstructions.currentInstructionsURL);
            }

            const blob = new Blob([response.data], { type: 'audio/mpeg' });
            const url = URL.createObjectURL(blob);

            updateInstructionsAndSchedule({
                customInstructionsURL: url,
                currentInstructionsURL: url
            }, false);

            playSound("check");
            toast.success('Audio generated successfully!');
        } catch (error: any) {
            toast.error('Failed to generate audio');
        } finally {
            setLoading(false);
        }
    }, [localInstructions.currentInstructions, localInstructions.currentInstructionsURL, updateInstructionsAndSchedule]);


    return (
        <>
            <Modal
                isOpen={localInstructions.isInstructionsOpen}
                onClose={() => setIsOpen(false)}
            >
                <div className="flex flex-col gap-6 p-6 w-full">
                    <div className="flex flex-col gap-2">
                        <h2 className="text-xl font-semibold text-gray-800">Listening Instructions</h2>
                        <p className="text-sm text-gray-500">Choose instruction type or customize your own</p>
                    </div>

                    <div className="flex flex-col gap-4">
                        <div className="flex flex-col gap-1.5">
                            <label className="text-sm font-medium text-gray-700">Instruction Type</label>
                            <Select
                                options={options}
                                onChange={(o) => onOptionChange({ value: o!.value || "", label: o!.label })}
                                value={localInstructions.chosenOption}
                                className="w-full"
                            />
                        </div>

                        <div className="flex flex-col gap-2">
                            <div className="flex flex-col flex-grow gap-1.5">
                                <label className="text-sm font-medium text-gray-700">Instructions Text</label>
                                <div className="flex flex-row gap-2">
                                    <div className="flex flex-grow bg-gray-50 rounded-lg p-4 border border-gray-200 items-center">
                                        <AutoExpandingTextArea
                                            value={localInstructions.currentInstructions || ''}
                                            onChange={onCustomInstructionChange}
                                            className="bg-transparent resize-none w-full focus:outline-none"
                                            placeholder="Enter custom instructions here..."
                                        />
                                    </div>
                                    {localInstructions.chosenOption?.value === 'Custom' && (
                                        <div className="flex items-center">
                                        <Button
                                            onClick={generateInstructionsMP3}
                                            disabled={loading}
                                            customColor="bg-ielts-listening/70 hover:bg-ielts-listening border-ielts-listening"
                                            className="text-white rounded-md"
                                        >
                                            {loading ? (
                                                <div className="flex items-center justify-center">
                                                    <BsArrowRepeat className="text-white animate-spin" size={25} />
                                                </div>
                                            ) : (
                                                <div className="flex flex-row items-center">
                                                    <GiBrain className="mr-2" size={24} />
                                                    <span>Generate</span>
                                                </div>
                                            )}
                                        </Button>
                                        </div>
                                    )}
                                </div>
                            </div>
                        </div>

                        {(localInstructions.chosenOption?.value === 'Automatic' ||
                            (localInstructions.chosenOption?.value === 'Custom' && localInstructions.currentInstructionsURL.startsWith("blob:")) && localInstructions.currentInstructionsURL !== '') && (
                                <div className="flex flex-col gap-1.5">
                                    <label className="text-sm font-medium text-gray-700">Audio Preview</label>
                                    <div className="bg-gray-50 rounded-lg p-4 border border-gray-200">
                                        <AudioPlayer
                                            src={localInstructions.currentInstructionsURL ?? ''}
                                            color="listening"
                                        />
                                    </div>
                                </div>
                            )}
                    </div>
                </div>
            </Modal>
            <Button
                onClick={() => setIsOpen(true)}
                customColor="bg-ielts-listening/70 hover:bg-ielts-listening border-ielts-listening"
                className="text-white self-end"
            >
                Audio Instructions
            </Button>
        </>
    );
};

export default ListeningInstructions;