import useExamEditorStore from "@/stores/examEditor";
import useSettingsState from "../Hooks/useSettingsState";
import { SpeakingSectionSettings } from "@/stores/examEditor/types";
import Option from "@/interfaces/option";
import { useCallback, useState } from "react";
import { generate } from "./Shared/Generate";
import SettingsEditor from ".";
import Dropdown from "./Shared/SettingsDropdown";
import Input from "@/components/Low/Input";
import GenerateBtn from "./Shared/GenerateBtn";
import clsx from "clsx";
import { FaFemale, FaMale, FaChevronDown } from "react-icons/fa";
import { InteractiveSpeakingExercise, SpeakingExam, SpeakingExercise } from "@/interfaces/exam";
import { toast } from "react-toastify";
import { generateVideos } from "./Shared/generateVideos";
import { usePersistentExamStore } from "@/stores/examStore";
import { useRouter } from "next/router";
import openDetachedTab from "@/utils/popout";
import axios from "axios";
import { playSound } from "@/utils/sound";

export interface Avatar {
    name: string;
    gender: string;
}

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

    const router = useRouter();

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

    const { title, currentModule, speakingAvatars, dispatch } = useExamEditorStore();
    const { focusedSection, difficulty, sections, minTimer, isPrivate } = useExamEditorStore((store) => store.modules[currentModule])

    const section = sections.find((section) => section.sectionId == focusedSection)?.state;


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

    const [selectedAvatar, setSelectedAvatar] = useState<Avatar | null>(null);

    const defaultPresets: Option[] = [
        {
            label: "Preset: Speaking Part 1",
            value: "Welcome to {part} of the {label}. You will engage in a conversation about yourself and familiar topics such as your home, family, work, studies, and interests. General questions will be asked."
          },
          {
            label: "Preset: Speaking Part 2",
            value: "Welcome to {part} of the {label}. You will be given a topic card describing a particular person, object, event, or experience."
          },
          {
            label: "Preset: Speaking Part 3",
            value: "Welcome to {part} of the {label}. You will engage in an in-depth discussion about abstract ideas and issues. The examiner will ask questions that require you to explain, analyze, and speculate about various aspects of the topic."
          }
    ];

    const generateScript = useCallback((sectionId: number) => {
        const queryParams: {
            difficulty: string;
            first_topic?: string;
            second_topic?: string;
            topic?: string;
        } = { difficulty };

        if (sectionId === 1) {
            if (localSettings.topic) {
                queryParams['first_topic'] = localSettings.topic;
            }
            if (localSettings.secondTopic) {
                queryParams['second_topic'] = localSettings.secondTopic;
            }
        } else {
            if (localSettings.topic) {
                queryParams['topic'] = localSettings.topic;
            }
        }

        generate(
            sectionId,
            currentModule,
            "context", // <- not really context but exercises is reserved for reading, listening and level
            {
                method: 'GET',
                queryParams
            },
            (data: any) => {
                switch (sectionId) {
                    case 1:
                        return [{
                            prompts: data.questions,
                            first_topic: data.first_topic,
                            second_topic: data.second_topic
                        }];
                    case 2:
                        return [{
                            topic: data.topic,
                            question: data.question,
                            prompts: data.prompts,
                            suffix: data.suffix
                        }];
                    case 3:
                        return [{
                            title: data.topic,
                            prompts: data.questions
                        }];
                    default:
                        return [data];
                }
            }
        );
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [localSettings, difficulty]);

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

    const onSecondTopicChange = useCallback((topic: string) => {
        updateLocalAndScheduleGlobal({ secondTopic: topic });
    }, [updateLocalAndScheduleGlobal]);

    const canPreviewOrSubmit = (() => {
        return sections.every((s) => {
            const section = s.state as SpeakingExercise | InteractiveSpeakingExercise;
            switch (section.type) {
                case 'speaking':
                    return section.title !== '' &&
                        section.text !== '' &&
                        section.video_url !== '' &&
                        section.prompts.every(prompt => prompt !== '');

                case 'interactiveSpeaking':
                    if ('first_title' in section && 'second_title' in section) {
                        return section.first_title !== '' &&
                            section.second_title !== '' &&
                            section.prompts.every(prompt => prompt.video_url !== '') &&
                            section.prompts.length > 2;
                    }
                    return section.title !== '' &&
                        section.prompts.every(prompt => prompt.video_url !== '');

                default:
                    return false;
            }
        });
    })();

    const canGenerate = section && (() => {
        switch (focusedSection) {
            case 1: {
                const currentSection = section as InteractiveSpeakingExercise;
                return currentSection.first_title !== "" &&
                    currentSection.second_title !== "" &&
                    currentSection.prompts.every(prompt => prompt.text !== "") && currentSection.prompts.length > 2;
            }
            case 2: {
                const currentSection = section as SpeakingExercise;
                return currentSection.title !== "" &&
                    currentSection.text !== "" &&
                    currentSection.prompts.every(prompt => prompt !== "");
            }
            case 3: {
                const currentSection = section as InteractiveSpeakingExercise;
                return currentSection.title !== "" &&
                    currentSection.prompts.every(prompt => prompt.text !== "");
            }
            default:
                return false;
        }
    })();

    const generateVideoCallback = useCallback((sectionId: number) => {
        dispatch({ type: "UPDATE_SECTION_SINGLE_FIELD", payload: { sectionId, module: currentModule, field: "generating", value: "media" } })
        generateVideos(
            section as InteractiveSpeakingExercise | SpeakingExercise,
            sectionId,
            selectedAvatar,
            speakingAvatars
        ).then((results) => {
            switch (sectionId) {
                case 1:
                case 3: {
                    const interactiveSection = section as InteractiveSpeakingExercise;
                    const updatedPrompts = interactiveSection.prompts.map((prompt, index) => ({
                        ...prompt,
                        video_url: results[index].url || ''
                    }));
                    dispatch({ type: "UPDATE_SECTION_SINGLE_FIELD", payload: { sectionId, module: currentModule, field: "genResult", value: [{ prompts: updatedPrompts }] } })
                    break;
                }
                case 2: {
                    if (results[0]?.url) {
                        dispatch({ type: "UPDATE_SECTION_SINGLE_FIELD", payload: { sectionId, module: currentModule, field: "genResult", value: [{ video_url: results[0].url }] } })
                    }
                    break;
                }
            }
        }).catch((error) => {
            toast.error("Failed to generate the video, try again later!")
        });
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [selectedAvatar, section]);


    const submitSpeaking = async () => {
        if (title === "") {
          toast.error("Enter a title for the exam!");
          return;
        }
      
        try {
          const formData = new FormData();
          const urlMap = new Map<string, string>();
      
          const sectionsWithVideos = sections.filter(s => {
            const exercise = s.state as SpeakingExercise | InteractiveSpeakingExercise;
            if (exercise.type === "speaking") {
              return exercise.video_url !== "";
            }
            if (exercise.type === "interactiveSpeaking") {
              return exercise.prompts?.some(prompt => prompt.video_url !== "");
            }
            return false;
          });
      
          if (sectionsWithVideos.length === 0) {
            toast.error('No video sections found in the exam! Please record or import videos.');
            return;
          }
      
          await Promise.all(
            sectionsWithVideos.map(async (section) => {
              const exercise = section.state as SpeakingExercise | InteractiveSpeakingExercise;
              
              if (exercise.type === "speaking") {
                const response = await fetch(exercise.video_url);
                const blob = await response.blob();
                formData.append('file', blob, 'video.mp4');
                urlMap.set(`${section.sectionId}`, exercise.video_url);
              } else {
                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();
                      formData.append('file', blob, 'video.mp4');
                      urlMap.set(`${section.sectionId}-${promptIndex}`, prompt.video_url);
                    }
                  })
                );
              }
            })
          );
      
          const response = await axios.post('/api/storage', formData, {
            params: {
              directory: 'speaking_videos'
            },
            headers: {
              'Content-Type': 'multipart/form-data'
            }
          });
      
          const { urls } = response.data;
      
          const exam: SpeakingExam = {
            exercises: sections.map((s) => {
              const exercise = s.state as SpeakingExercise | InteractiveSpeakingExercise;
      
              if (exercise.type === "speaking") {
                const videoIndex = Array.from(urlMap.entries())
                  .findIndex(([key]) => key === `${s.sectionId}`);
                
                return {
                  ...exercise,
                  video_url: videoIndex !== -1 ? urls[videoIndex] : exercise.video_url,
                  intro: s.settings.currentIntro,
                  category: s.settings.category
                };
              } else {
                const updatedPrompts = exercise.prompts.map((prompt, promptIndex) => {
                  const videoIndex = Array.from(urlMap.entries())
                    .findIndex(([key]) => key === `${s.sectionId}-${promptIndex}`);
                  
                  return {
                    ...prompt,
                    video_url: videoIndex !== -1 ? urls[videoIndex] : prompt.video_url
                  };
                });
      
                return {
                  ...exercise,
                  prompts: updatedPrompts,
                  intro: s.settings.currentIntro,
                  category: s.settings.category
                };
              }
            }),
            minTimer,
            module: "speaking",
            id: title,
            isDiagnostic: false,
            variant: undefined,
            difficulty,
            instructorGender: "varied",
            private: isPrivate,
          };
      
          const result = await axios.post('/api/exam/speaking', exam);
          playSound("sent");
          toast.success(`Submitted Exam ID: ${result.data.id}`);
      
          Array.from(urlMap.values()).forEach(url => {
            URL.revokeObjectURL(url);
          });
      
        } catch (error: any) {
          toast.error(
            "Something went wrong while submitting, please try again later."
          );
        }
      };

    const preview = () => {
        setExam({
            exercises: sections
                .filter((s) => {
                    const exercise = s.state as SpeakingExercise | InteractiveSpeakingExercise;

                    if (exercise.type === "speaking") {
                        return exercise.video_url !== "";
                    }

                    if (exercise.type === "interactiveSpeaking") {
                        return exercise.prompts?.every(prompt => prompt.video_url !== "");
                    }

                    return false;
                })
                .map((s) => {
                    const exercise = s.state as SpeakingExercise | InteractiveSpeakingExercise;
                    return {
                        ...exercise,
                        intro: s.settings.currentIntro,
                        category: s.settings.category
                    };
                }),
            minTimer,
            module: "speaking",
            id: title,
            isDiagnostic: false,
            variant: undefined,
            difficulty,
            private: isPrivate,
        } as SpeakingExam);
        setExerciseIndex(0);
        setQuestionIndex(0);
        setBgColor("bg-white");
        openDetachedTab("popout?type=Exam&module=speaking", router)
    }

    return (
        <SettingsEditor
            sectionLabel={`Speaking ${focusedSection}`}
            sectionId={focusedSection}
            module="speaking"
            introPresets={[defaultPresets[focusedSection - 1]]}
            preview={preview}
            canPreview={canPreviewOrSubmit}
            canSubmit={canPreviewOrSubmit}
            submitModule={submitSpeaking}
        >
            <Dropdown
                title="Generate Script"
                module={currentModule}
                open={localSettings.isExerciseDropdownOpen}
                setIsOpen={(isOpen: boolean) => updateLocalAndScheduleGlobal({ isExerciseDropdownOpen: isOpen }, false)}
            >

                <div className={clsx("gap-2 px-2 pb-4", focusedSection === 1 ? "flex flex-col w-full" : "flex flex-row items-center")}>
                    <div className="flex flex-col flex-grow gap-4 px-2">
                        <label className="font-normal text-base text-mti-gray-dim">{`${focusedSection === 1 ? "First Topic" : "Topic"}`} (Optional)</label>
                        <Input
                            key={`section-${focusedSection}`}
                            type="text"
                            placeholder="Topic"
                            name="category"
                            onChange={onTopicChange}
                            roundness="full"
                            value={localSettings.topic}
                        />
                    </div>
                    {focusedSection === 1 &&
                        <div className="flex flex-col flex-grow gap-4 px-2">
                            <label className="font-normal text-base text-mti-gray-dim">Second Topic (Optional)</label>
                            <Input
                                key={`section-${focusedSection}`}
                                type="text"
                                placeholder="Topic"
                                name="category"
                                onChange={onSecondTopicChange}
                                roundness="full"
                                value={localSettings.secondTopic}
                            />
                        </div>
                    }
                    <div className={clsx("flex h-16 mb-1", focusedSection === 1 ? "justify-center mt-4" : "self-end")}>
                        <GenerateBtn
                            module={currentModule}
                            genType="context"
                            sectionId={focusedSection}
                            generateFnc={generateScript}
                        />
                    </div>
                </div>
            </Dropdown>
            <Dropdown
                title="Generate Video"
                module={currentModule}
                open={localSettings.isGenerateAudioOpen}
                disabled={!canGenerate}
                setIsOpen={(isOpen: boolean) => updateLocalAndScheduleGlobal({ isGenerateAudioOpen: isOpen }, false)}
            >
                <div className={clsx("flex items-center justify-between gap-4 px-2 pb-4")}>
                    <div className="relative flex-1 max-w-xs">
                        <select
                            value={selectedAvatar ? `${selectedAvatar.name}-${selectedAvatar.gender}` : ""}
                            onChange={(e) => {
                                if (e.target.value === "") {
                                    setSelectedAvatar(null);
                                } else {
                                    const [name, gender] = e.target.value.split("-");
                                    const avatar = speakingAvatars.find(a => a.name === name && a.gender === gender);
                                    if (avatar) setSelectedAvatar(avatar);
                                }
                            }}
                            className="w-full appearance-none px-4 py-2 border border-gray-200 rounded-full text-base bg-white focus:ring-1 focus:ring-blue-500 focus:outline-none"
                        >
                            <option value="">Select an avatar</option>
                            {speakingAvatars.map((avatar) => (
                                <option
                                    key={`${avatar.name}-${avatar.gender}`}
                                    value={`${avatar.name}-${avatar.gender}`}
                                >
                                    {avatar.name}
                                </option>
                            ))}
                        </select>
                        <div className="absolute right-2.5 top-2.5 pointer-events-none">
                            {selectedAvatar && (
                                selectedAvatar.gender === 'male' ? (
                                    <FaMale className="w-5 h-5 text-blue-500" />
                                ) : (
                                    <FaFemale className="w-5 h-5 text-pink-500" />
                                )
                            )}
                        </div>
                    </div>

                    <GenerateBtn
                        module={currentModule}
                        genType="media"
                        sectionId={focusedSection}
                        generateFnc={generateVideoCallback}
                    />
                </div>
            </Dropdown>
        </SettingsEditor>
    );
};

export default SpeakingSettings;
