import { Exercise, InteractiveSpeakingExercise, LevelExam, LevelPart, SpeakingExercise } from "@/interfaces/exam";
import SettingsEditor from ".";
import Option from "@/interfaces/option";
import Dropdown from "@/components/Dropdown";
import clsx from "clsx";
import ExercisePicker from "../ExercisePicker";
import useExamEditorStore from "@/stores/examEditor";
import useSettingsState from "../Hooks/useSettingsState";
import { LevelSectionSettings } from "@/stores/examEditor/types";
import { toast } from "react-toastify";
import axios from "axios";
import { playSound } from "@/utils/sound";
import { useRouter } from "next/router";
import { usePersistentExamStore } from "@/stores/exam";
import openDetachedTab from "@/utils/popout";
import ListeningComponents from "./listening/components";
import ReadingComponents from "./reading/components";
import SpeakingComponents from "./speaking/components";
import SectionPicker from "./Shared/SectionPicker";
import { getExamById } from "@/utils/exams";


const LevelSettings: React.FC = () => {

    const router = useRouter();

    const {
        setExam,
        setExerciseIndex,
        setPartIndex,
        setQuestionIndex,
        setBgColor,
    } = usePersistentExamStore();

    const { currentModule, title } = useExamEditorStore();
    const {
        focusedSection,
        difficulty,
        sections,
        minTimer,
        access,
    } = useExamEditorStore(state => state.modules[currentModule]);

    const { localSettings, updateLocalAndScheduleGlobal } = useSettingsState<LevelSectionSettings>(
        currentModule,
        focusedSection
    );

    const section = sections.find((section) => section.sectionId == focusedSection);
    const focusedExercise = section?.focusedExercise;
    if (section === undefined) return <></>;

    const currentSection = section.state as LevelPart;
    const readingSection = section.readingSection;
    const listeningSection = section.listeningSection;

    const canPreviewOrSubmit = sections.length > 0 && sections.some(s => {
        const part = s.state as LevelPart;
        return part.exercises.length > 0 && part.exercises.every((exercise) => {
            if (exercise.type === 'speaking') {
                return exercise.title !== '' &&
                    exercise.text !== '' &&
                    exercise.video_url !== '' &&
                    exercise.prompts.every(prompt => prompt !== '');
            } else if (exercise.type === 'interactiveSpeaking') {
                if ('first_title' in exercise && 'second_title' in exercise) {
                    return exercise.first_title !== '' &&
                        exercise.second_title !== '' &&
                        exercise.prompts.every(prompt => prompt.video_url !== '') &&
                        exercise.prompts.length > 2;
                }
                return exercise.title !== '' &&
                    exercise.prompts.every(prompt => prompt.video_url !== '');
            }
            return true;
        });
    });

    const submitLevel = async () => {
        if (title === "") {
            toast.error("Enter a title for the exam!");
            return;
        }

        const partsWithMissingAudio = sections.some(s => {
            const part = s.state as LevelPart;
            return part.audio && !part.audio.source;
        });

        if (partsWithMissingAudio) {
            toast.error("There are parts with missing audio recordings. Either generate them or remove the listening sections.");
            return;
        }


        try {
            const audioFormData = new FormData();
            const videoFormData = new FormData();
            const audioMap = new Map<number, string>();
            const videoMap = new Map<string, string>();

            const partsWithAudio = sections.filter(s => (s.state as LevelPart).audio?.source);
            await Promise.all(
                partsWithAudio.map(async (section) => {
                    const levelPart = section.state as LevelPart;
                    const blobUrl = levelPart.audio!.source;
                    const response = await fetch(blobUrl);
                    const blob = await response.blob();
                    audioFormData.append('file', blob, 'audio.mp3');
                    audioMap.set(section.sectionId, blobUrl);
                })
            );

            await Promise.all(
                sections.flatMap(async (section) => {
                    const levelPart = section.state as LevelPart;
                    return Promise.all(
                        levelPart.exercises.map(async (exercise, exerciseIndex) => {
                            if (exercise.type === "speaking") {
                                if (exercise.video_url) {
                                    const response = await fetch(exercise.video_url);
                                    const blob = await response.blob();
                                    videoFormData.append('file', blob, 'video.mp4');
                                    videoMap.set(`${section.sectionId}-${exerciseIndex}`, exercise.video_url);
                                }
                            } else if (exercise.type === "interactiveSpeaking") {
                                await Promise.all(
                                    exercise.prompts.map(async (prompt, promptIndex) => {
                                        if (prompt.video_url) {
                                            const response = await fetch(prompt.video_url);
                                            const blob = await response.blob();
                                            videoFormData.append('file', blob, 'video.mp4');
                                            videoMap.set(`${section.sectionId}-${exerciseIndex}-${promptIndex}`, prompt.video_url);
                                        }
                                    })
                                );
                            }
                        })
                    );
                })
            );

            const [audioUrls, videoUrls] = await Promise.all([
                audioMap.size > 0
                    ? axios.post('/api/storage', audioFormData, {
                        params: { directory: 'listening_recordings' },
                        headers: { 'Content-Type': 'multipart/form-data' }
                    }).then(response => response.data.urls)
                    : [],
                videoMap.size > 0
                    ? axios.post('/api/storage', videoFormData, {
                        params: { directory: 'speaking_videos' },
                        headers: { 'Content-Type': 'multipart/form-data' }
                    }).then(response => response.data.urls)
                    : []
            ]);

            const exam: LevelExam = {
                parts: sections.map((s) => {
                    const part = s.state as LevelPart;
                    const audioIndex = Array.from(audioMap.entries())
                        .findIndex(([id]) => id === s.sectionId);
                    const updatedExercises = part.exercises.map((exercise, exerciseIndex) => {
                        if (exercise.type === "speaking") {
                            const videoIndex = Array.from(videoMap.entries())
                                .findIndex(([key]) => key === `${s.sectionId}-${exerciseIndex}`);
                            return {
                                ...exercise,
                                video_url: videoIndex !== -1 ? videoUrls[videoIndex] : exercise.video_url
                            };
                        } else if (exercise.type === "interactiveSpeaking") {
                            const updatedPrompts = exercise.prompts.map((prompt, promptIndex) => {
                                const videoIndex = Array.from(videoMap.entries())
                                    .findIndex(([key]) => key === `${s.sectionId}-${exerciseIndex}-${promptIndex}`);
                                return {
                                    ...prompt,
                                    video_url: videoIndex !== -1 ? videoUrls[videoIndex] : prompt.video_url
                                };
                            });
                            return {
                                ...exercise,
                                prompts: updatedPrompts
                            };
                        }
                        return exercise;
                    });
                    return {
                        ...part,
                        audio: part.audio ? {
                            ...part.audio,
                            source: audioIndex !== -1 ? audioUrls[audioIndex] : part.audio.source
                        } : undefined,
                        exercises: updatedExercises,
                        intro: s.settings.currentIntro,
                        category: s.settings.category
                    };
                }).filter(part => part.exercises.length > 0),
                isDiagnostic: true, // using isDiagnostic to keep exam hidden until the respective approval workflow is completed.
                minTimer,
                module: "level",
                id: title,
                difficulty,
                access,
            };

            const result = await axios.post('/api/exam/level', exam);
            playSound("sent");
            // Successfully submitted exam
            if (result.status === 200) {
                toast.success(result.data.message);
            } else if (result.status === 207) {
                toast.warning(result.data.message);
            }

            Array.from(audioMap.values()).forEach(url => {
                URL.revokeObjectURL(url);
            });
            Array.from(videoMap.values()).forEach(url => {
                URL.revokeObjectURL(url);
            });

        } catch (error: any) {
            console.error('Error submitting exam:', error);
            toast.error(
                "Something went wrong while submitting, please try again later."
            );
        }
    };

    const preview = () => {
        setExam({
            parts: sections.map((s) => {
                const part = s.state as LevelPart;
                return {
                    ...part,
                    intro: s.settings.currentIntro,
                    category: s.settings.category
                };
            }),
            minTimer,
            module: "level",
            id: title,
            isDiagnostic: false,
            variant: undefined,
            difficulty,
            access,
        } as LevelExam);
        setExerciseIndex(0);
        setQuestionIndex(0);
        setPartIndex(0);
        openDetachedTab("popout?type=Exam&module=level", router)
    }

    const speakingExercise = focusedExercise === undefined ? undefined : currentSection.exercises.find((ex) => ex.id === focusedExercise.id) as SpeakingExercise | InteractiveSpeakingExercise;
    return (
        <SettingsEditor
            sectionLabel={`Part ${focusedSection}`}
            sectionId={focusedSection}
            module="level"
            introPresets={[]}
            preview={preview}
            canPreview={canPreviewOrSubmit}
            canSubmit={canPreviewOrSubmit}
            submitModule={submitLevel}
        >
            <div>
                <Dropdown title="Add Level Exercises" className={
                    clsx(
                        "w-full font-semibold flex justify-between items-center p-4 bg-gradient-to-r border",
                        "bg-ielts-level/70 border-ielts-level hover:bg-ielts-level",
                        "text-white shadow-md transition-all duration-300",
                        localSettings.isLevelDropdownOpen ? "rounded-t-lg" : "rounded-lg"
                    )
                }
                    contentWrapperClassName={"pt-4 px-2 bg-white rounded-b-lg shadow-md transition-all duration-300 ease-in-out"}
                    open={localSettings.isLevelDropdownOpen}
                    setIsOpen={(isOpen: boolean) => updateLocalAndScheduleGlobal({ isLevelDropdownOpen: isOpen }, false)}
                >
                    <ExercisePicker
                        module="level"
                        sectionId={focusedSection}
                    />
                </Dropdown>
            </div>
            <div>
                <Dropdown title="Add Reading Exercises" className={
                    clsx(
                        "w-full font-semibold flex justify-between items-center p-4 bg-gradient-to-r border",
                        "bg-ielts-reading/70 border-ielts-reading hover:bg-ielts-reading",
                        "text-white shadow-md transition-all duration-300",
                        localSettings.isReadingDropdownOpen ? "rounded-t-lg" : "rounded-lg"
                    )
                }
                    contentWrapperClassName={"pt-4 px-2 bg-white rounded-b-lg shadow-md transition-all duration-300 ease-in-out"}
                    open={localSettings.isReadingDropdownOpen}
                    setIsOpen={(isOpen: boolean) => updateLocalAndScheduleGlobal({ isReadingDropdownOpen: isOpen }, false)}
                >
                    <div className="space-y-2 px-2 pb-2">
                        <SectionPicker {...{ module: "reading", sectionId: focusedSection, localSettings, updateLocalAndScheduleGlobal }} />
                        <ReadingComponents
                            {...{ localSettings, updateLocalAndScheduleGlobal, currentSection, generatePassageDisabled: readingSection === undefined, levelId: readingSection, level: true }}
                        />
                    </div>
                </Dropdown>
            </div>
            <div>
                <Dropdown title="Add Listening Exercises" className={
                    clsx(
                        "w-full font-semibold flex justify-between items-center p-4 bg-gradient-to-r border",
                        "bg-ielts-listening/70 border-ielts-listening hover:bg-ielts-listening",
                        "text-white shadow-md transition-all duration-300",
                        localSettings.isListeningDropdownOpen ? "rounded-t-lg" : "rounded-lg"
                    )
                }
                    contentWrapperClassName={"pt-4 px-2 bg-white rounded-b-lg shadow-md transition-all duration-300 ease-in-out"}
                    open={localSettings.isListeningDropdownOpen}
                    setIsOpen={(isOpen: boolean) => updateLocalAndScheduleGlobal({ isListeningDropdownOpen: isOpen }, false)}
                >
                    <div className="space-y-2 px-2 pb-2">
                        <SectionPicker {...{ module: "listening", sectionId: focusedSection, localSettings, updateLocalAndScheduleGlobal }} />
                        <ListeningComponents
                            {...{ localSettings, updateLocalAndScheduleGlobal, currentSection, audioContextDisabled: listeningSection === undefined, levelId: listeningSection, level: true }}
                        />
                    </div>
                </Dropdown>
            </div>
            <div>
                <Dropdown title="Add Writing Exercises" className={
                    clsx(
                        "w-full font-semibold flex justify-between items-center p-4 bg-gradient-to-r border",
                        "bg-ielts-writing/70 border-ielts-writing hover:bg-ielts-writing",
                        "text-white shadow-md transition-all duration-300",
                        localSettings.isWritingDropdownOpen ? "rounded-t-lg" : "rounded-lg"
                    )
                }
                    contentWrapperClassName={"pt-4 px-2 bg-white rounded-b-lg shadow-md transition-all duration-300 ease-in-out"}
                    open={localSettings.isWritingDropdownOpen}
                    setIsOpen={(isOpen: boolean) => updateLocalAndScheduleGlobal({ isWritingDropdownOpen: isOpen }, false)}
                >
                    <ExercisePicker
                        module="writing"
                        sectionId={focusedSection}
                        levelSectionId={focusedSection}
                        level
                    />
                </Dropdown>
            </div >
            <div>
                <Dropdown title="Add Speaking Exercises" className={
                    clsx(
                        "w-full font-semibold flex justify-between items-center p-4 bg-gradient-to-r border",
                        "bg-ielts-speaking/70 border-ielts-speaking hover:bg-ielts-speaking",
                        "text-white shadow-md transition-all duration-300",
                        localSettings.isSpeakingDropdownOpen ? "rounded-t-lg" : "rounded-lg"
                    )
                }
                    contentWrapperClassName={"pt-4 px-2 bg-white rounded-b-lg shadow-md transition-all duration-300 ease-in-out"}
                    open={localSettings.isSpeakingDropdownOpen}
                    setIsOpen={(isOpen: boolean) => updateLocalAndScheduleGlobal({ isSpeakingDropdownOpen: isOpen }, false)}
                >
                    <div className="space-y-2 px-2 pb-2">
                        <Dropdown title="Exercises" className={
                            clsx(
                                "w-full font-semibold flex justify-between items-center p-4 bg-gradient-to-r border",
                                "bg-ielts-speaking/70 border-ielts-speaking hover:bg-ielts-speaking",
                                "text-white shadow-md transition-all duration-300",
                                localSettings.isSpeakingExercisesOpen ? "rounded-t-lg" : "rounded-lg"
                            )
                        }
                            contentWrapperClassName={"pt-4 px-2 bg-white rounded-b-lg shadow-md transition-all duration-300 ease-in-out border border-ielts-speaking"}
                            open={localSettings.isSpeakingExercisesOpen}
                            setIsOpen={(isOpen: boolean) => updateLocalAndScheduleGlobal({ isSpeakingExercisesOpen: isOpen }, false)}
                        >
                            <ExercisePicker
                                module="speaking"
                                sectionId={focusedSection}
                                levelSectionId={focusedSection}
                                level
                            />

                        </Dropdown>

                        {speakingExercise !== undefined &&
                            <Dropdown title={`Configure Speaking Exercise #${Number(focusedExercise!.questionId) + 1}`} className={
                                clsx(
                                    "w-full font-semibold flex justify-between items-center p-4 bg-gradient-to-r border",
                                    "bg-ielts-speaking/70 border-ielts-speaking hover:bg-ielts-speaking",
                                    "text-white shadow-md transition-all duration-300",
                                    localSettings.isConfigureExercisesOpen ? "rounded-t-lg" : "rounded-lg"
                                )
                            }
                                contentWrapperClassName={"pt-4 px-2 bg-white rounded-b-lg shadow-md transition-all duration-300 ease-in-out border border-ielts-speaking"}
                                open={localSettings.isConfigureExercisesOpen}
                                setIsOpen={(isOpen: boolean) => updateLocalAndScheduleGlobal({ isConfigureExercisesOpen: isOpen }, false)}
                            >
                                <div className="space-y-2 px-2 pb-2">
                                    <SpeakingComponents
                                        {...{ localSettings, updateLocalAndScheduleGlobal, section: speakingExercise, id: speakingExercise.id, sectionId: focusedSection }}
                                        level
                                    />
                                </div>
                            </Dropdown>
                        }
                    </div>
                </Dropdown>
            </div>
        </SettingsEditor >
    );
};

export default LevelSettings;
