import { InteractiveSpeakingExercise, SpeakingExercise } from "@/interfaces/exam";
import { Avatar } from "../speaking";
import axios from "axios";

interface VideoResponse {
    status: 'STARTED' | 'ERROR' | 'COMPLETED' | 'IN_PROGRESS';
    result: string;
}

interface VideoGeneration {
    index: number;
    text: string;
    videoId?: string;
    url?: string;
}

export async function generateVideos(section: InteractiveSpeakingExercise | SpeakingExercise, focusedSection: number, selectedAvatar: Avatar | null, speakingAvatars: Avatar[]) {
    const abortController = new AbortController();
    let activePollingIds: string[] = [];

    const avatarToUse = selectedAvatar || speakingAvatars[Math.floor(Math.random() * speakingAvatars.length)];

    const pollVideoGeneration = async (videoId: string): Promise<string> => {
        while (true) {
            try {
                const { data } = await axios.get<VideoResponse>(`api/exam/media/poll?videoId=${videoId}`, {
                    signal: abortController.signal
                });

                if (data.status === 'ERROR') {
                    abortController.abort();
                    throw new Error('Video generation failed');
                }

                if (data.status === 'COMPLETED') {
                    const videoResponse = await axios.get(data.result, {
                        responseType: 'blob',
                        signal: abortController.signal
                    });
                    const videoUrl = URL.createObjectURL(
                        new Blob([videoResponse.data], { type: 'video/mp4' })
                    );
                    return videoUrl;
                }

                await new Promise(resolve => setTimeout(resolve, 10000)); // 10 secs
            } catch (error: any) {
                if (error.name === 'AbortError' || axios.isCancel(error)) {
                    throw new Error('Operation aborted');
                }
                throw error;
            }
        }
    };

    const generateSingleVideo = async (text: string, index: number): Promise<VideoGeneration> => {
        try {
            const { data } = await axios.post<VideoResponse>('/api/exam/media/speaking', 
                { text, avatar: avatarToUse.name },
                {
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    signal: abortController.signal
                }
            );

            if (data.status === 'ERROR') {
                abortController.abort();
                throw new Error('Initial video generation failed');
            }

            activePollingIds.push(data.result);
            const videoUrl = await pollVideoGeneration(data.result);
            return { index, text, videoId: data.result, url: videoUrl };
        } catch (error) {
            abortController.abort();
            throw error;
        }
    };

    try {
        let videosToGenerate: { text: string; index: number }[] = [];
        switch (focusedSection) {
            case 1: {
                const interactiveSection = section as InteractiveSpeakingExercise;
                videosToGenerate = interactiveSection.prompts.map((prompt, index) => ({
                    text: index === 0 ? prompt.text.replace("{avatar}", avatarToUse.name) : prompt.text,
                    index
                }));
                break;
            }
            case 2: {
                const speakingSection = section as SpeakingExercise;
                videosToGenerate = [{ text: `${speakingSection.text}. You have 1 minute to take notes.`, index: 0 }];
                break;
            }
            case 3: {
                const interactiveSection = section as InteractiveSpeakingExercise;
                videosToGenerate = interactiveSection.prompts.map((prompt, index) => ({
                    text: prompt.text,
                    index
                }));
                break;
            }
        }

        // Generate all videos concurrently
        const results = await Promise.all(
            videosToGenerate.map(({ text, index }) => generateSingleVideo(text, index))
        );

        // by order which they came in
        return results.sort((a, b) => a.index - b.index);

    } catch (error) {
        // Clean up any ongoing requests
        abortController.abort();
        // Clean up any created URLs
        activePollingIds.forEach(id => {
            if (id) URL.revokeObjectURL(id);
        });
        throw error;
    }
}
