import Dropdown from "../Shared/SettingsDropdown";
import ExercisePicker from "../../ExercisePicker";
import GenerateBtn from "../Shared/GenerateBtn";
import { useCallback, useState } from "react";
import { generate } from "../Shared/Generate";
import { LevelSectionSettings, ListeningSectionSettings } from "@/stores/examEditor/types";
import useExamEditorStore from "@/stores/examEditor";
import { LevelPart, ListeningPart, Script } from "@/interfaces/exam";
import Input from "@/components/Low/Input";
import axios from "axios";
import { toast } from "react-toastify";
import { playSound } from "@/utils/sound";
import { FaFileUpload } from "react-icons/fa";
import clsx from "clsx";
import AudioUpload from "./AudioUpload";
import { downloadBlob } from "@/utils/evaluation";
import { BsArrowRepeat } from "react-icons/bs";

interface Props {
    localSettings: ListeningSectionSettings | LevelSectionSettings;
    updateLocalAndScheduleGlobal: (updates: Partial<ListeningSectionSettings | LevelSectionSettings>, schedule?: boolean) => void;
    currentSection: ListeningPart | LevelPart;
    audioContextDisabled?: boolean;
    levelId?: number;
    level?: boolean;
}

const ListeningComponents: React.FC<Props> = ({ currentSection, localSettings, updateLocalAndScheduleGlobal, levelId, level = false, audioContextDisabled = false }) => {
    const { currentModule, dispatch, modules } = useExamEditorStore();
    const {
        focusedSection,
        difficulty,
    } = useExamEditorStore(state => state.modules[currentModule]);

    const [originalAudioUrl, setOriginalAudioUrl] = useState<string | undefined>();
    const [audioUrl, setAudioUrl] = useState<string | undefined>();
    const [isUploaderOpen, setIsUploaderOpen] = useState(false);
    const [isUploading, setIsUploading] = useState(false);

    const generateScript = useCallback(() => {
        if (audioUrl) {
            URL.revokeObjectURL(audioUrl);
            setAudioUrl(undefined);
            dispatch({
                type: "UPDATE_SECTION_STATE",
                payload: {
                    sectionId: focusedSection,
                    module: "listening",
                    update: {
                        audio: undefined
                    }
                }
            });
        }
        generate(
            levelId ? levelId : focusedSection,
            "listening",
            "listeningScript",
            {
                method: 'GET',
                queryParams: {
                    difficulty,
                    ...(localSettings.listeningTopic && { topic: localSettings.listeningTopic })
                }
            },
            (data: any) => [{
                script: data.dialog
            }],
            level ? focusedSection : undefined,
            level
        );
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [localSettings.listeningTopic, difficulty, focusedSection, levelId, level]);

    const onTopicChange = useCallback((listeningTopic: string) => {
        updateLocalAndScheduleGlobal({ listeningTopic });
    }, [updateLocalAndScheduleGlobal]);


    const generateAudio = useCallback(async (sectionId: number) => {
        let body: any;
        if ([1, 3].includes(levelId ? levelId : focusedSection)) {
            body = { conversation: currentSection.script }
        } else {
            body = { monologue: currentSection.script }
        }

        try {
            if (level) {
                dispatch({
                    type: "UPDATE_SECTION_SINGLE_FIELD", payload: {
                        sectionId, module: "level", field: "levelGenerating", value:
                            [...modules["level"].sections.find((s) => s.sectionId === sectionId)!.levelGenerating, "audio"]
                    }
                });
            } else {
                dispatch({ type: "UPDATE_SECTION_SINGLE_FIELD", payload: { sectionId, module: currentModule, field: "generating", value: "audio" } });
            }

            const response = await axios.post(
                '/api/exam/media/listening',
                body,
                {
                    responseType: 'arraybuffer',
                    headers: {
                        'Accept': 'audio/mpeg'
                    },
                }
            );

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

            if (currentSection.audio?.source) {
                URL.revokeObjectURL(currentSection.audio?.source)
            }

            dispatch({
                type: "UPDATE_SECTION_STATE",
                payload: {
                    sectionId,
                    module: level ? "level" : "listening",
                    update: {
                        audio: {
                            source: url,
                            repeatableTimes: 3
                        }
                    }
                }
            });
            playSound("check");
            toast.success('Audio generated successfully!');
        } catch (error: any) {
            toast.error('Failed to generate audio');
        } finally {
            if (level) {
                dispatch({
                    type: "UPDATE_SECTION_SINGLE_FIELD", payload: {
                        sectionId, module: "level", field: "levelGenerating", value:
                            [...modules["level"].sections.find((s) => s.sectionId === sectionId)!.levelGenerating.filter(g => g !== "audio")]
                    }
                });
            } else {
                dispatch({ type: "UPDATE_SECTION_SINGLE_FIELD", payload: { sectionId, module: currentModule, field: "generating", value: undefined } });
            }
        }

        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [currentSection?.script, dispatch, level, levelId]);


    const handleFileSelect = (file: File | null) => {
        if (file) {
            const url = URL.createObjectURL(file);
            setOriginalAudioUrl(url);
            setAudioUrl(url);
            dispatch({ 
                type: "UPDATE_SECTION_STATE", 
                payload: { 
                    sectionId: focusedSection, 
                    module: "listening", 
                    update: { 
                        audio: { 
                            source: url,
                            repeatableTimes: 3 
                        } 
                    } 
                } 
            });
        } else {
            if (audioUrl) {
                URL.revokeObjectURL(audioUrl);
                URL.revokeObjectURL(originalAudioUrl!);
                dispatch({ 
                    type: "UPDATE_SECTION_STATE", 
                    payload: { 
                        sectionId: focusedSection, 
                        module: "listening", 
                        update: { audio: undefined } 
                    } 
                });
            }
            setAudioUrl(undefined);
            setOriginalAudioUrl(undefined);
        }
    };


    const transcribeAudio = async () => {
        try {
            setIsUploading(true);
            dispatch({type: "UPDATE_SECTION_SINGLE_FIELD", payload: {module: "listening", sectionId: focusedSection, field: "scriptLoading", value: true}})
            const formData = new FormData();
            const audioBlob = await downloadBlob(audioUrl!);
            const audioFile = new File([audioBlob], "audio");
            formData.append("audio", audioFile);

            const config = {
                headers: {
                    "Content-Type": "multipart/form-data",
                },
            };

            const response = await axios.post(`/api/transcribe`, formData, config);
            dispatch({
                type: "UPDATE_SECTION_STATE",
                payload: {
                    sectionId: focusedSection,
                    module: "listening",
                    update: {
                        script: (response.data as any).dialog as Script,
                        audio: { source: audioUrl!, repeatableTimes: 3 }
                    }
                }
            });

        } catch (error) {
            toast.error("An unexpected error has occurred, try again later!");
        } finally {
            setIsUploading(false);
            dispatch({type: "UPDATE_SECTION_SINGLE_FIELD", payload: {module: "listening", sectionId: focusedSection, field: "scriptLoading", value: false}})
        }
    };

    return (
        <>
            <AudioUpload isOpen={isUploaderOpen} setIsOpen={setIsUploaderOpen} audioFile={originalAudioUrl} onFileSelect={handleFileSelect} transcribeAudio={transcribeAudio} setAudioUrl={setAudioUrl} />
            <Dropdown
                title="Audio Context"
                module="listening"
                open={localSettings.isAudioContextOpen}
                disabled={audioContextDisabled}
                setIsOpen={(isOpen: boolean) => updateLocalAndScheduleGlobal({ isAudioContextOpen: isOpen }, false)}
                contentWrapperClassName={level ? `border border-ielts-listening` : ''}
            >
                <div className="flex flex-row flex-wrap gap-2 items-center justify-center px-2 pb-4">
                    <div className="flex flex-col flex-grow gap-4 px-2">
                        <label className="font-normal text-base text-mti-gray-dim">Topic (Optional)</label>
                        <Input
                            key={`section-${focusedSection}`}
                            type="text"
                            placeholder="Topic"
                            name="category"
                            onChange={onTopicChange}
                            roundness="full"
                            value={localSettings.listeningTopic}
                        />
                    </div>
                    <div className="flex self-end h-16 mb-1">
                        <GenerateBtn
                            module="listening"
                            genType="listeningScript"
                            sectionId={focusedSection}
                            generateFnc={generateScript}
                            level={level}
                            disabled={isUploading}
                        />
                    </div>
                </div>
                <div className="flex justify-center text-mti-gray-dim font-semibold">Or</div>
                <div className="flex flex-col w-full gap-2 px-2 pb-4">
                    <div className="flex flex-row items-center text-mti-gray-dim justify-between w-full gap-4 py-2 pl-2">
                        <div className="flex-1 bg-gray-100 px-3.5 py-2.5 rounded-lg border border-gray-300">
                            Import your own audio file
                        </div>
                        <div className="flex self-end h-16 mb-1 flex-shrink-0">
                            <button
                                className={clsx(
                                    "flex items-center w-[140px] justify-center px-4 py-2 text-white rounded-xl transition-colors duration-300 text-lg disabled:cursor-not-allowed",
                                    "bg-ielts-listening/70 border border-ielts-listening hover:bg-ielts-listening disabled:bg-ielts-listening/40"
                                )}
                                onClick={() => setIsUploaderOpen(true)}
                            >
                                <div className="flex flex-row">
                                    {isUploading ? (
                                        <BsArrowRepeat className="mr-2 text-white animate-spin" size={25} />
                                    ) : (
                                        <>
                                            <FaFileUpload className="mr-2" size={24} />
                                            <span>Upload</span>
                                        </>
                                    )}
                                </div>
                            </button>
                        </div>
                    </div>
                </div >
            </Dropdown >
            <Dropdown
                title="Add Exercises"
                module="listening"
                open={localSettings.isListeningTopicOpen}
                setIsOpen={(isOpen: boolean) => updateLocalAndScheduleGlobal({ isListeningTopicOpen: isOpen }, false)}
                disabled={currentSection === undefined || currentSection.script === undefined && currentSection.audio === undefined}
                contentWrapperClassName={level ? `border border-ielts-listening` : ''}
            >
                <ExercisePicker
                    module="listening"
                    sectionId={levelId !== undefined ? levelId : focusedSection}
                    extraArgs={{ script: currentSection === undefined || currentSection.audio === undefined ? "" : currentSection.script }}
                    levelSectionId={focusedSection}
                    level={level}
                />
            </Dropdown>

            <Dropdown
                title="Generate Audio"
                module="listening"
                open={localSettings.isAudioGenerationOpen}
                setIsOpen={(isOpen: boolean) => updateLocalAndScheduleGlobal({ isAudioGenerationOpen: isOpen }, false)}
                disabled={currentSection === undefined || currentSection.script === undefined || currentSection.exercises.length === 0 || audioUrl !== undefined}
                contentWrapperClassName={level ? `border border-ielts-listening` : ''}
            >
                <div className="flex flex-row items-center text-mti-gray-dim justify-center mb-4 gap-4 p-2">
                    <span className="bg-gray-100 px-3.5 py-2.5 rounded-lg border border-gray-300">
                        Generate audio recording for this section
                    </span>
                    <GenerateBtn
                        module="listening"
                        genType="audio"
                        sectionId={levelId ? levelId : focusedSection}
                        generateFnc={generateAudio}
                    />
                </div>
            </Dropdown>
        </>
    );
};

export default ListeningComponents;