"use client"; import { useCallback, useState } from "react"; import { Sparkles, Settings2, Clock, Zap, Tv2, LoaderCircle, CheckCircle2, Target, } from "lucide-react"; import type { LucideIcon } from "lucide-react"; import { Modal, PrimaryButton, SecondaryButton } from "@/components/dashboard/dashboardPrimitives"; import { useJobPolling } from "@/hooks/useJobPolling"; import { getVideoPlanExtended } from "@/lib/api/intelligence"; import { createVideoRenderJob, createVideoScenePlan, getVideoRenderJob } from "@/lib/api/video"; import { normalizeApiError } from "@/lib/api/client"; import type { StudentLevel, VideoFormat, VideoPlanV2Response, VideoPlanDurationType, VideoPlanStyle, VideoRenderJob, VoiceMode, VoiceProfile, } from "@/lib/api/types"; import { prepareScenePlanForRender } from "@/lib/video/durations"; import { cn } from "@/lib/utils"; const ALLOW_LOCAL_PREVIEW_AUDIO = process.env.NODE_ENV !== "production"; const durationOptions: { value: VideoPlanDurationType; label: string; time: string; icon: LucideIcon }[] = [ { value: "quick_concept", label: "5-minute video", time: "5 min", icon: Zap }, { value: "exam_focus", label: "10-minute video", time: "10 min", icon: Target }, { value: "deep_masterclass", label: "30-minute video", time: "30 min", icon: Tv2 }, ]; const styleOptions: { value: VideoPlanStyle; label: string; desc: string }[] = [ { value: "normal_teacher", label: "Normal Teacher", desc: "Traditional academic style" }, { value: "visual_tutor", label: "Visual Tutor", desc: "Heavy on diagrams & flowcharts" }, { value: "anime_tutor", label: "Anime Tutor", desc: "High energy, character-led" }, { value: "malayalam_english_tutor", label: "Mixed Mode", desc: "Malayalam + English key terms" }, { value: "exam_war_mode", label: "Exam War Mode", desc: "Strictly mark-focused, fast-paced" }, ]; const formatOptions: { value: VideoFormat; label: string }[] = [ { value: "16:9", label: "Wide Class Video" }, { value: "9:16", label: "Phone Revision" }, ]; const voiceOptions: { value: VoiceProfile; label: string; voiceMode: VoiceMode }[] = [ { value: "anime_female_tutor", label: "Anime Tutor", voiceMode: "english_soft" }, { value: "calm_teacher", label: "Calm Teacher", voiceMode: "english_soft" }, { value: "malayalam_english_teacher", label: "Malayalam + English Tutor", voiceMode: "ml_en_mix" }, { value: "exam_war_coach", label: "Exam War Coach", voiceMode: "english_soft" }, { value: "premium_human_teacher", label: "Premium Human Teacher", voiceMode: "english_soft" }, ]; export function VideoTutorBrainModal({ isOpen, onClose, documentId, studentLevel, onPlanGenerated }: { isOpen: boolean; onClose: () => void; documentId: string; studentLevel: StudentLevel; onPlanGenerated: (plan: VideoPlanV2Response) => void; }) { const [step, setStep] = useState<"config" | "loading" | "success">("config"); const [duration, setDuration] = useState("exam_focus"); const [style, setStyle] = useState("visual_tutor"); const [format, setFormat] = useState("16:9"); const [voiceProfile, setVoiceProfile] = useState("calm_teacher"); const [generatedPlan, setGeneratedPlan] = useState(null); const [renderStep, setRenderStep] = useState<"idle" | "planning" | "voice" | "building" | "ready" | "failed">("idle"); const [renderJob, setRenderJob] = useState(null); const [error, setError] = useState(null); const pollRenderJob = useCallback(async () => { if (!renderJob) throw new Error("No video render job is running."); return getVideoRenderJob(renderJob.id); }, [renderJob]); useJobPolling({ enabled: Boolean(renderJob && (renderJob.status === "queued" || renderJob.status === "running")), fetcher: pollRenderJob, shouldContinue: (job) => job.status === "queued" || job.status === "running", onData: (job) => { setRenderJob(job); if (job.status === "ready") { setRenderStep("ready"); } else if (job.status === "failed") { setRenderStep("failed"); setError("DocDoe could not finish this video. Try again from a shorter source."); } else if (job.current_step?.startsWith("generating_audio")) { setRenderStep("voice"); } else { setRenderStep("building"); } }, onError: () => setError("DocDoe could not refresh the video status. Open Videos to check it."), }); async function generatePlan() { setStep("loading"); setError(null); try { const data = await getVideoPlanExtended(documentId, duration, style); setGeneratedPlan(data); setStep("success"); onPlanGenerated(data); } catch (caughtError) { setError(normalizeApiError(caughtError)); setStep("config"); } } async function buildVideo() { setRenderStep("planning"); setError(null); try { const selectedVoice = voiceOptions.find((option) => option.value === voiceProfile) ?? voiceOptions[1]; const durationMinutes = duration === "quick_concept" ? 5 : duration === "exam_focus" ? 10 : duration === "deep_masterclass" ? 30 : 10; const scenePlan = prepareScenePlanForRender( await createVideoScenePlan({ document_id: documentId, title: generatedPlan?.title ?? "DocDoe Study Video", language: selectedVoice.voiceMode === "ml_en_mix" ? "Malayalam + English" : "English", duration_minutes: durationMinutes, visual_style: style === "anime_tutor" ? "anime_visual_explainer" : "clean_explainer", video_format: format, }), durationMinutes * 60, ); setRenderStep("voice"); const job = await createVideoRenderJob({ scene_plan: scenePlan, title: scenePlan.title, allow_mock_audio: ALLOW_LOCAL_PREVIEW_AUDIO, voice_mode: selectedVoice.voiceMode, voice: selectedVoice.value, language: scenePlan.language, }); setRenderJob(job); setRenderStep(job.status === "ready" ? "ready" : "building"); } catch (caughtError) { setError(normalizeApiError(caughtError)); setRenderStep("failed"); } } return ( {step === "config" && (

Select Duration

{durationOptions.map((opt) => ( ))}

Teaching Style

{styleOptions.map((opt) => ( ))}

Format

{formatOptions.map((opt) => ( ))}

Voice

Current level: {studentLevel}. Long videos can take more time to prepare.

{error ? (

{error}

) : null} Generate Extended Video Plan
)} {step === "loading" && (

DocDoe is building your study video plan.

Creating hook, analogy, and source-based teaching flow for your {duration.replace("_", " ")} video.

)} {step === "success" && generatedPlan && (

Plan Ready

{generatedPlan.title}

Master Script Hook

"{generatedPlan.hook}"

The Analogy

{generatedPlan.analogy}

Scene Breakdown ({generatedPlan.sections.length} scenes)

{generatedPlan.sections.map(s => (
#{s.scene_number}

{s.label}

{s.visual_suggestion}

))}
{renderStep !== "idle" ? (

{renderStep === "planning" ? "Preparing" : renderStep === "voice" ? "Adding voice" : renderStep === "building" ? "Building" : renderStep === "ready" ? "Your video is ready" : "Could not build video"}

{renderJob ? (

Open Videos to track progress and preview the final study video.

) : error ? (

{error}

) : (

Long videos can take more time to prepare.

)}
) : null}
setStep("config")} className="h-11">Edit Config {renderStep === "idle" || renderStep === "failed" ? "Build Video" : renderStep === "ready" ? "Build Again" : "Preparing"}
{renderStep === "ready" ? ( Close & Save ) : null}
)} ); }