import { AlertCircle, CheckCircle, ChevronLeft, ChevronRight, Clock, FileText, Loader2, Palette, Sparkles, Users, Wand2, } from "lucide-react" import { FC, useCallback, useMemo, useState } from "react" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { Progress } from "@/components/ui/progress" import { ScrollArea } from "@/components/ui/scroll-area" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Slider } from "@/components/ui/slider" import { Switch } from "@/components/ui/switch" import { Textarea } from "@/components/ui/textarea" import { cn } from "@/lib/utils" import { useAIIntelligence } from "../../hooks/use-ai-intelligence" import type { EmotionalTone, GeneratedScript, ScriptGenerationParams, ScriptStyle, ScriptTemplate, UnifiedContentAnalysis, } from "../../shared/types" import { Emotion, Genre } from "../../shared/types/content-analysis" import { EditingStyle, NarrativeStyle, NarrativeType, PaceType, TemplateCategory, VisualStyle, } from "../../shared/types/script-generation" interface GenerationWizardProps { className?: string analysis?: UnifiedContentAnalysis | null onGenerate?: (script: GeneratedScript) => void onCancel?: () => void onClose?: () => void } type WizardStep = "template" | "style" | "narrative" | "characters" | "audio" | "review" | "generating" interface WizardState { currentStep: WizardStep template?: ScriptTemplate style: ScriptStyle genre: Genre[] duration?: number targetAudience: string tone: EmotionalTone includeDialogue: boolean includeVoiceover: boolean narrativeStructure: NarrativeType customPrompt: string voiceoverStyle: string pacing: PaceType characterCount: number includeNarrator: boolean } const defaultWizardState: WizardState = { currentStep: "template", style: { visual: VisualStyle.CINEMATIC, narrative: NarrativeStyle.LINEAR, editing: EditingStyle.CONTINUITY, }, genre: [Genre.GENERAL], targetAudience: "Общая аудитория", tone: { primary: Emotion.CALM, intensity: 0.5, }, includeDialogue: true, includeVoiceover: false, narrativeStructure: NarrativeType.THREE_ACT, customPrompt: "", voiceoverStyle: "narrative", pacing: PaceType.MODERATE, characterCount: 2, includeNarrator: false, } const SCRIPT_TEMPLATES: ScriptTemplate[] = [ { id: "cinematic-narrative", name: "Кинематографический рассказ", description: "Классический трёхактный фильм с драматической структурой", category: TemplateCategory.FILM, structure: { type: NarrativeType.THREE_ACT, acts: [ { number: 1, title: "Завязка", description: "Установка персонажей и конфликта", scenes: [], duration: 30 }, { number: 2, title: "Развитие", description: "Развитие конфликта и препятствия", scenes: [], duration: 60 }, { number: 3, title: "Развязка", description: "Разрешение конфликта", scenes: [], duration: 30 }, ], turningPoints: [], }, defaultParams: { style: { visual: VisualStyle.CINEMATIC, narrative: NarrativeStyle.LINEAR, editing: EditingStyle.CONTINUITY, }, genre: [Genre.DRAMA], includeDialogue: true, includeVoiceover: false, narrativeStructure: NarrativeType.THREE_ACT, }, examples: ["Драма", "Короткометражка", "Художественный фильм"], }, { id: "documentary", name: "Документальный", description: "Информационный документальный формат с закадровым голосом", category: TemplateCategory.DOCUMENTARY, structure: { type: NarrativeType.LINEAR, acts: [ { number: 1, title: "Введение", description: "Представление темы", scenes: [], duration: 20 }, { number: 2, title: "Исследование", description: "Раскрытие темы", scenes: [], duration: 80 }, { number: 3, title: "Заключение", description: "Выводы", scenes: [], duration: 20 }, ], turningPoints: [], }, defaultParams: { style: { visual: VisualStyle.DOCUMENTARY, narrative: NarrativeStyle.LINEAR, editing: EditingStyle.CONTINUITY, }, genre: [Genre.DOCUMENTARY], includeDialogue: false, includeVoiceover: true, narrativeStructure: NarrativeType.LINEAR, }, examples: ["Обучающее видео", "Репортаж", "Образовательный контент"], }, { id: "social-media", name: "Социальные сети", description: "Короткий динамичный контент для соцсетей", category: TemplateCategory.SOCIAL_MEDIA, structure: { type: NarrativeType.NONLINEAR, acts: [ { number: 1, title: "Хук", description: "Привлечение внимания", scenes: [], duration: 5 }, { number: 2, title: "Контент", description: "Основное сообщение", scenes: [], duration: 20 }, { number: 3, title: "CTA", description: "Призыв к действию", scenes: [], duration: 5 }, ], turningPoints: [], }, defaultParams: { style: { visual: VisualStyle.DYNAMIC, narrative: NarrativeStyle.MONTAGE, editing: EditingStyle.JUMP_CUT, }, genre: [Genre.GENERAL], includeDialogue: false, includeVoiceover: true, narrativeStructure: NarrativeType.NONLINEAR, }, examples: ["TikTok", "Instagram Reels", "YouTube Shorts"], }, { id: "commercial", name: "Коммерческий", description: "Рекламный ролик с фокусом на продукт", category: TemplateCategory.COMMERCIAL, structure: { type: NarrativeType.THREE_ACT, acts: [ { number: 1, title: "Проблема", description: "Представление потребности", scenes: [], duration: 10 }, { number: 2, title: "Решение", description: "Демонстрация продукта", scenes: [], duration: 15 }, { number: 3, title: "Результат", description: "Призыв к покупке", scenes: [], duration: 5 }, ], turningPoints: [], }, defaultParams: { style: { visual: VisualStyle.DYNAMIC, narrative: NarrativeStyle.LINEAR, editing: EditingStyle.MONTAGE, }, genre: [Genre.GENERAL], includeDialogue: true, includeVoiceover: true, narrativeStructure: NarrativeType.THREE_ACT, }, examples: ["Продающий ролик", "Презентация продукта", "Реклама услуг"], }, { id: "vlog", name: "Видеоблог", description: "Личный видеоблог с естественным повествованием", category: TemplateCategory.VLOG, structure: { type: NarrativeType.EPISODIC, acts: [ { number: 1, title: "Введение", description: "Приветствие и планы", scenes: [], duration: 15 }, { number: 2, title: "Активность", description: "Основные события", scenes: [], duration: 70 }, { number: 3, title: "Заключение", description: "Выводы и прощание", scenes: [], duration: 15 }, ], turningPoints: [], }, defaultParams: { style: { visual: VisualStyle.REALISTIC, narrative: NarrativeStyle.STREAM_OF_CONSCIOUSNESS, editing: EditingStyle.JUMP_CUT, }, genre: [Genre.LIFESTYLE], includeDialogue: true, includeVoiceover: false, narrativeStructure: NarrativeType.EPISODIC, }, examples: ["Влог путешествий", "Ежедневная жизнь", "Обзор дня"], }, ] const STEP_ORDER: WizardStep[] = ["template", "style", "narrative", "characters", "audio", "review", "generating"] export const GenerationWizard: FC = ({ className, analysis, onGenerate, onCancel, onClose }) => { const [state, setState] = useState(defaultWizardState) const [isGenerating, setIsGenerating] = useState(false) const [generationProgress, setGenerationProgress] = useState(0) const [error, setError] = useState(null) const { generateScript } = useAIIntelligence({ onProgress: (progress) => { setGenerationProgress(progress.overall) }, onError: (err) => { setError(err.message) setIsGenerating(false) }, }) const currentStepIndex = STEP_ORDER.indexOf(state.currentStep) const isFirstStep = currentStepIndex === 0 const isLastStep = currentStepIndex === STEP_ORDER.length - 1 const canProceed = useMemo(() => { switch (state.currentStep) { case "template": return state.template !== undefined case "style": return state.genre.length > 0 case "narrative": return true case "characters": return true case "audio": return true case "review": return true default: return false } }, [state]) const updateState = useCallback((updates: Partial) => { setState((prev) => ({ ...prev, ...updates })) }, []) const goToStep = useCallback( (step: WizardStep) => { updateState({ currentStep: step }) }, [updateState], ) const goNext = useCallback(() => { if (!isLastStep && canProceed) { const nextIndex = currentStepIndex + 1 goToStep(STEP_ORDER[nextIndex]) } }, [currentStepIndex, isLastStep, canProceed, goToStep]) const goBack = useCallback(() => { if (!isFirstStep) { const prevIndex = currentStepIndex - 1 goToStep(STEP_ORDER[prevIndex]) } }, [currentStepIndex, isFirstStep, goToStep]) const handleTemplateSelect = useCallback( (template: ScriptTemplate) => { // Если кликнули на уже выбранный шаблон, отменяем выбор if (state.template?.id === template.id) { updateState({ template: undefined, // Сбрасываем на значения по умолчанию style: defaultWizardState.style, genre: defaultWizardState.genre, includeDialogue: defaultWizardState.includeDialogue, includeVoiceover: defaultWizardState.includeVoiceover, narrativeStructure: defaultWizardState.narrativeStructure, }) } else { // Выбираем новый шаблон updateState({ template, style: template.defaultParams.style || state.style, genre: template.defaultParams.genre || state.genre, includeDialogue: template.defaultParams.includeDialogue ?? state.includeDialogue, includeVoiceover: template.defaultParams.includeVoiceover ?? state.includeVoiceover, narrativeStructure: template.defaultParams.narrativeStructure || state.narrativeStructure, }) } }, [state, updateState], ) const handleGenerate = useCallback(async () => { if (!analysis) { setError("Нет данных анализа для генерации скрипта") return } try { setIsGenerating(true) setError(null) updateState({ currentStep: "generating" }) setGenerationProgress(0) const params: ScriptGenerationParams = { style: state.style, genre: state.genre, duration: state.duration || analysis.mediaFile.duration, targetAudience: state.targetAudience, tone: state.tone, includeDialogue: state.includeDialogue, includeVoiceover: state.includeVoiceover, narrativeStructure: state.narrativeStructure, customPrompt: state.customPrompt, } const script = await generateScript(analysis, params) onGenerate?.(script) onClose?.() } catch (error) { console.error("Script generation failed:", error) setError(error instanceof Error ? error.message : "Ошибка генерации скрипта") } finally { setIsGenerating(false) } }, [analysis, state, generateScript, onGenerate, onClose, updateState]) const renderStepContent = () => { switch (state.currentStep) { case "template": return ( ) case "style": return case "narrative": return case "characters": return case "audio": return case "review": return case "generating": return default: return null } } const getStepTitle = () => { switch (state.currentStep) { case "template": return "Выбор шаблона" case "style": return "Стиль и жанр" case "narrative": return "Структура повествования" case "characters": return "Персонажи и диалоги" case "audio": return "Аудио и озвучка" case "review": return "Проверка параметров" case "generating": return "Генерация скрипта" default: return "Мастер генерации" } } return (
{/* Header */}

Мастер генерации скрипта

{getStepTitle()}

{state.currentStep !== "generating" && ( )}
{/* Progress */} {state.currentStep !== "generating" && (
Шаг {currentStepIndex + 1} из {STEP_ORDER.length - 1} {Math.round((currentStepIndex / (STEP_ORDER.length - 2)) * 100)}%
)} {/* Content */}
{renderStepContent()}
{/* Navigation */} {state.currentStep !== "generating" && (
{state.currentStep === "review" ? ( ) : ( )}
)}
) } // Step Components interface TemplateStepProps { templates: ScriptTemplate[] selectedTemplate?: ScriptTemplate onSelect: (template: ScriptTemplate) => void } const TemplateStep: FC = ({ templates, selectedTemplate, onSelect }) => { return (

Выберите тип скрипта

Выберите подходящий шаблон для вашего видео. Каждый шаблон настроен под определённый тип контента.

{templates.map((template) => ( onSelect(template)} >
{template.name} {template.category}

{template.description}

{template.examples?.map((example) => ( {example} ))}
))}
) } interface StyleStepProps { state: WizardState onUpdate: (updates: Partial) => void } const StyleStep: FC = ({ state, onUpdate }) => { const genreOptions = [ { value: Genre.ACTION, label: "Экшен" }, { value: Genre.COMEDY, label: "Комедия" }, { value: Genre.DRAMA, label: "Драма" }, { value: Genre.DOCUMENTARY, label: "Документальный" }, { value: Genre.EDUCATIONAL, label: "Образовательный" }, { value: Genre.LIFESTYLE, label: "Лайфстайл" }, { value: Genre.TRAVEL, label: "Путешествия" }, { value: Genre.TECH, label: "Технологии" }, { value: Genre.FOOD, label: "Еда" }, { value: Genre.FITNESS, label: "Фитнес" }, { value: Genre.GENERAL, label: "Общий" }, ] const visualStyleOptions = [ { value: VisualStyle.CINEMATIC, label: "Кинематографический" }, { value: VisualStyle.DOCUMENTARY, label: "Документальный" }, { value: VisualStyle.DYNAMIC, label: "Динамичный" }, { value: VisualStyle.MINIMALIST, label: "Минималистичный" }, { value: VisualStyle.ARTISTIC, label: "Художественный" }, { value: VisualStyle.REALISTIC, label: "Реалистичный" }, ] const emotionOptions = [ { value: Emotion.HAPPY, label: "Радостный" }, { value: Emotion.CALM, label: "Спокойный" }, { value: Emotion.EXCITED, label: "Возбуждённый" }, { value: Emotion.INSPIRING, label: "Вдохновляющий" }, { value: Emotion.DRAMATIC, label: "Драматичный" }, { value: Emotion.COMEDIC, label: "Комичный" }, { value: Emotion.MYSTERIOUS, label: "Таинственный" }, { value: Emotion.ROMANTIC, label: "Романтичный" }, ] return (

Стиль и настроение

Настройте визуальный стиль и эмоциональный тон вашего видео.

{/* Genre Selection */}
{genreOptions.map((option) => ( { const newGenres = state.genre.includes(option.value) ? state.genre.filter((g) => g !== option.value) : [...state.genre, option.value] onUpdate({ genre: newGenres }) }} > {option.label} ))}
{/* Visual Style */}
{/* Emotional Tone */}
{/* Tone Intensity */}
onUpdate({ tone: { ...state.tone, intensity: value / 100, }, }) } max={100} step={10} className="w-full" />
Слабая Сильная
{/* Target Audience */}
onUpdate({ targetAudience: e.target.value })} placeholder="Например: Молодые люди 18-25 лет" />
) } const NarrativeStep: FC = ({ state, onUpdate }) => { const narrativeTypeOptions = [ { value: NarrativeType.THREE_ACT, label: "Трёхактная структура", description: "Классическая структура: завязка, развитие, развязка", }, { value: NarrativeType.FIVE_ACT, label: "Пятиактная структура", description: "Расширенная структура для сложных историй", }, { value: NarrativeType.HEROS_JOURNEY, label: "Путешествие героя", description: "Мономиф: вызов, путешествие, возвращение", }, { value: NarrativeType.LINEAR, label: "Линейное повествование", description: "Прямая хронологическая последовательность", }, { value: NarrativeType.NONLINEAR, label: "Нелинейное повествование", description: "Фрагментарная структура с переходами во времени", }, { value: NarrativeType.EPISODIC, label: "Эпизодическая структура", description: "Серия связанных эпизодов" }, ] const narrativeStyleOptions = [ { value: NarrativeStyle.LINEAR, label: "Линейный" }, { value: NarrativeStyle.NONLINEAR, label: "Нелинейный" }, { value: NarrativeStyle.MONTAGE, label: "Монтажный" }, { value: NarrativeStyle.PARALLEL, label: "Параллельный" }, { value: NarrativeStyle.STREAM_OF_CONSCIOUSNESS, label: "Поток сознания" }, ] const pacingOptions = [ { value: PaceType.SLOW, label: "Медленный", description: "Спокойное, размеренное повествование" }, { value: PaceType.MODERATE, label: "Умеренный", description: "Сбалансированный темп" }, { value: PaceType.FAST, label: "Быстрый", description: "Динамичное, энергичное повествование" }, { value: PaceType.VARIABLE, label: "Переменный", description: "Изменяющийся темп в зависимости от сцены" }, ] return (

Структура повествования

Выберите, как будет организована ваша история.

{/* Narrative Structure */}
{narrativeTypeOptions.map((option) => ( onUpdate({ narrativeStructure: option.value })} >

{option.label}

{option.description}

{state.narrativeStructure === option.value && }
))}
{/* Narrative Style */}
{/* Pacing */}
{pacingOptions.map((option) => ( onUpdate({ pacing: option.value })} >

{option.label}

{option.description}

{state.pacing === option.value && }
))}
) } const CharactersStep: FC = ({ state, onUpdate }) => { return (

Персонажи и диалоги

Настройте параметры персонажей и диалогов в вашем скрипте.

{/* Include Dialogue */}

Добавить разговоры между персонажами

onUpdate({ includeDialogue: checked })} />
{/* Character Count */} {state.includeDialogue && (
onUpdate({ characterCount: value })} min={1} max={8} step={1} className="w-full" />
1 {state.characterCount} 8
)} {/* Include Narrator */}

Добавить голос повествователя

onUpdate({ includeNarrator: checked })} />
{/* Custom Prompt */}