'use client';

import { useEffect, useState, useRef, useCallback, use } from 'react';
import Link from 'next/link';
import { ArrowLeft, Sparkles, FileText, List, Image, Video, ChevronRight, Edit3, Save, Eye, LayoutGrid, AlertTriangle, Palette, RefreshCw, Film } from 'lucide-react';
import { Button } from '@/components/ui/Button';
import { Card, CardContent, CardHeader } from '@/components/ui/Card';
import { Toast } from '@/components/ui/Toast';
import { ProgressModal, type ProgressLogEntry } from '@/components/ui/ProgressModal';
import { WizardStepper } from '@/components/wizard/WizardStepper';
import { PlanoCard } from '@/components/capitulo/PlanoCard';
import { PlanoAssetCard } from '@/components/capitulo/PlanoAssetCard';
import { FondoCard } from '@/components/capitulo/FondoCard';
import { GuionFieldsEditor } from '@/components/capitulo/GuionFieldsEditor';
import { VideoVariationsCard } from '@/components/capitulo/VideoVariationsCard';
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import type { GuionConductor, Escaleta, PlanoEscaleta, PreEscaleta, PlanoConPrompt, FondoReferencia, PromptComposition, PersonajeEnPlano } from '@/types/project';
import type { CharacterAssetsSummary } from '@/lib/db-service';

const CAPITULO_STEPS = [
  { id: 'guion', name: 'Guión Conductor', description: 'Estructura narrativa' },
  { id: 'escaleta', name: 'Escaleta', description: 'Desglose de planos' },
  { id: 'prompts', name: 'Prompts', description: 'Para generación de imágenes' },
  { id: 'assets', name: 'Assets', description: 'Imágenes y videos' },
];

export default function CapituloPage({ 
  params 
}: { 
  params: Promise<{ slug: string; numero: string }> 
}) {
  const { slug, numero } = use(params);
  
  const [guion, setGuion] = useState<GuionConductor | null>(null);
  const [preEscaleta, setPreEscaleta] = useState<PreEscaleta | null>(null);
  const [escaleta, setEscaleta] = useState<Escaleta | null>(null);
  const [promptsData, setPromptsData] = useState<{ contenidoMarkdown?: string; totalPrompts?: number } | null>(null);
  const [currentStep, setCurrentStep] = useState(0);
  const [loading, setLoading] = useState(true);
  const [generating, setGenerating] = useState(false);
  const [toast, setToast] = useState<{ message: string; type: 'success' | 'error' } | null>(null);
  const [editingGuion, setEditingGuion] = useState(false);
  const [editingGuionFields, setEditingGuionFields] = useState(false);
  const [editingEscaleta, setEditingEscaleta] = useState(false);
  const [guionDraft, setGuionDraft] = useState('');
  const [escaletaDraft, setEscaletaDraft] = useState('');
  const [escaletaView, setEscaletaView] = useState<'cards' | 'markdown'>('cards');
  const [assetsMap, setAssetsMap] = useState<Record<string, CharacterAssetsSummary>>({});
  const [personajesList, setPersonajesList] = useState<{ id: string; nombre: string }[]>([]);
  const [imagenesData, setImagenesData] = useState<{ planos: PlanoConPrompt[]; total: number; generated: number; withPrompt?: number; estiloVisual?: string } | null>(null);
  const [generatingPlano, setGeneratingPlano] = useState<string | null>(null);
  const [generatingPromptSingle, setGeneratingPromptSingle] = useState<string | null>(null);
  const [fondosData, setFondosData] = useState<{ fondos: FondoReferencia[]; total: number; generated: number } | null>(null);
  const [generatingFondo, setGeneratingFondo] = useState<string | null>(null);
  const [generatingAllFondos, setGeneratingAllFondos] = useState(false);
  const [fondosVersion, setFondosVersion] = useState(0);
  const [imgVersion, setImgVersion] = useState(() => Date.now());
  const [videosData, setVideosData] = useState<{ planos: PlanoConPrompt[]; total: number; generated: number; withPrompt?: number } | null>(null);
  const [generatingVideo, setGeneratingVideo] = useState<string | null>(null);

  // Remotion render state
  const [renderJobId, setRenderJobId] = useState<string | null>(null);
  const [renderProgress, setRenderProgress] = useState<any>(null);

  // Progress modal state
  const [progress, setProgress] = useState<{
    open: boolean;
    title: string;
    current: number;
    total: number;
    currentLabel: string;
    logs: ProgressLogEntry[];
  }>({ open: false, title: '', current: 0, total: 0, currentLabel: '', logs: [] });
  const abortRef = useRef<AbortController | null>(null);

  const startProgress = useCallback((title: string, total: number = 0, label: string = 'Iniciando...') => {
    abortRef.current = new AbortController();
    setProgress({ open: true, title, current: 0, total, currentLabel: label, logs: [] });
  }, []);

  const updateProgress = useCallback((current: number, total: number, label: string) => {
    setProgress(prev => ({ ...prev, current, total, currentLabel: label }));
  }, []);

  const addProgressLog = useCallback((label: string, status: 'success' | 'error', detail?: string) => {
    setProgress(prev => ({ ...prev, logs: [...prev.logs, { label, status, detail }] }));
  }, []);

  const finishProgress = useCallback((current: number, total: number) => {
    setProgress(prev => ({ ...prev, current, total, currentLabel: '' }));
  }, []);

  const closeProgress = useCallback(() => {
    abortRef.current?.abort();
    abortRef.current = null;
    setProgress(prev => ({ ...prev, open: false }));
  }, []);

  useEffect(() => {
    fetchData();
  }, [slug, numero]);

  const showToast = (message: string, type: 'success' | 'error') => {
    setToast({ message, type });
  };

  const fetchData = async () => {
    setLoading(true);
    try {
      const [guionRes, preEscaletaRes, escaletaRes, promptsRes, assetsRes, imagenesRes, fondosRes, personajesRes, videosRes] = await Promise.all([
        fetch(`/api/projects/${slug}/capitulos/${numero}/guion`),
        fetch(`/api/projects/${slug}/capitulos/${numero}/pre-escaleta`),
        fetch(`/api/projects/${slug}/capitulos/${numero}/escaleta`),
        fetch(`/api/projects/${slug}/capitulos/${numero}/prompts`),
        fetch(`/api/projects/${slug}/personajes/assets-summary`),
        fetch(`/api/projects/${slug}/capitulos/${numero}/imagenes`),
        fetch(`/api/projects/${slug}/capitulos/${numero}/fondos`),
        fetch(`/api/projects/${slug}/personajes`),
        fetch(`/api/projects/${slug}/capitulos/${numero}/videos`),
      ]);

      if (guionRes.ok) {
        const guionData = await guionRes.json();
        if (guionData) setGuion(guionData);
      }
      if (preEscaletaRes.ok) {
        const preEscaletaData = await preEscaletaRes.json();
        if (preEscaletaData) setPreEscaleta(preEscaletaData);
      }
      if (escaletaRes.ok) {
        const escaletaData = await escaletaRes.json();
        if (escaletaData) setEscaleta(escaletaData);
      }
      if (promptsRes.ok) {
        const promptsDataRes = await promptsRes.json();
        if (promptsDataRes) setPromptsData(promptsDataRes);
      }
      if (assetsRes.ok) {
        const assetsData = await assetsRes.json();
        if (assetsData) setAssetsMap(assetsData);
      }
      if (imagenesRes.ok) {
        const imagenesDataRes = await imagenesRes.json();
        if (imagenesDataRes) setImagenesData(imagenesDataRes);
      }
      if (fondosRes.ok) {
        const fondosDataRes = await fondosRes.json();
        if (fondosDataRes) setFondosData(fondosDataRes);
      }
      if (personajesRes.ok) {
        const pList = await personajesRes.json();
        if (Array.isArray(pList)) setPersonajesList(pList.map((p: any) => ({ id: p.id, nombre: p.nombre })));
      }
      if (videosRes.ok) {
        const videosDataRes = await videosRes.json();
        if (videosDataRes) setVideosData(videosDataRes);
      }
      setImgVersion(Date.now());
    } catch (error) {
      console.error('Error fetching data:', error);
    } finally {
      setLoading(false);
    }
  };

  const handleSaveGuionMarkdown = async () => {
    setGenerating(true);
    try {
      const res = await fetch(`/api/projects/${slug}/capitulos/${numero}/guion`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'save', contenidoMarkdown: guionDraft }),
      });
      if (!res.ok) {
        const data = await res.json();
        showToast(data.error || 'Error guardando guión', 'error');
        return;
      }
      setGuion({ ...guion!, contenidoMarkdown: guionDraft });
      setEditingGuion(false);
      showToast('Guión guardado correctamente', 'success');
    } catch (error) {
      console.error('Error saving guion:', error);
      showToast('Error al guardar guión', 'error');
    } finally {
      setGenerating(false);
    }
  };

  const handleSaveGuionFields = async (updatedGuion: GuionConductor) => {
    try {
      const res = await fetch(`/api/projects/${slug}/capitulos/${numero}/guion`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'save-fields', guionData: updatedGuion }),
      });
      const data = await res.json();
      if (!res.ok) {
        showToast(data.error || 'Error guardando campos', 'error');
        return;
      }
      setGuion(data);
      setEditingGuionFields(false);
      showToast('Guión actualizado correctamente', 'success');
    } catch (error) {
      console.error('Error saving guion fields:', error);
      showToast('Error al guardar campos del guión', 'error');
    }
  };

  const handleSaveEscaletaMarkdown = async () => {
    setGenerating(true);
    try {
      const res = await fetch(`/api/projects/${slug}/capitulos/${numero}/escaleta`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'save', contenidoMarkdown: escaletaDraft }),
      });
      if (!res.ok) {
        const data = await res.json();
        showToast(data.error || 'Error guardando escaleta', 'error');
        return;
      }
      setEscaleta({ ...escaleta!, contenidoMarkdown: escaletaDraft });
      setEditingEscaleta(false);
      showToast('Escaleta guardada correctamente', 'success');
    } catch (error) {
      console.error('Error saving escaleta:', error);
      showToast('Error al guardar escaleta', 'error');
    } finally {
      setGenerating(false);
    }
  };

  // SSE stream reader helper
  const readSSEStream = async (
    response: Response,
    onProgress: (current: number, total: number, label: string) => void,
    signal?: AbortSignal
  ): Promise<any> => {
    const reader = response.body!.getReader();
    const decoder = new TextDecoder();
    let buffer = '';
    let result: any = null;

    try {
      while (true) {
        if (signal?.aborted) { reader.cancel(); throw new Error('Cancelado'); }
        const { done, value } = await reader.read();
        if (done) break;
        buffer += decoder.decode(value, { stream: true });
        const lines = buffer.split('\n\n');
        buffer = lines.pop() || '';
        for (const line of lines) {
          const match = line.match(/^data:\s*(.*)/);
          if (!match) continue;
          try {
            const event = JSON.parse(match[1]);
            if (event.type === 'progress') onProgress(event.current, event.total, event.label);
            else if (event.type === 'done') result = event.result;
            else if (event.type === 'error') throw new Error(event.message);
          } catch (e: any) { if (e.message !== 'Cancelado') throw e; }
        }
      }
    } finally { reader.releaseLock(); }
    return result;
  };

  const handleGenerateGuion = async () => {
    startProgress('Generando Guión Conductor', 0, 'Creando estructura narrativa...');
    try {
      const res = await fetch(`/api/projects/${slug}/capitulos/${numero}/guion`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'generate' }),
        signal: abortRef.current?.signal,
      });
      const data = await res.json();
      if (!res.ok) { showToast(data.error || 'Error generando guión', 'error'); closeProgress(); return; }
      setGuion(data);
      addProgressLog('Guión conductor generado', 'success');
      finishProgress(1, 1);
      showToast('Guión conductor generado correctamente', 'success');
    } catch (error: any) {
      if (error.name !== 'AbortError') { showToast('Error de conexión al generar guión', 'error'); }
      closeProgress();
    }
  };

  const handleRegenerateActos = async () => {
    startProgress('Regenerando Actos', 0, 'Generando nuevos actos con IA...');
    try {
      const res = await fetch(`/api/projects/${slug}/capitulos/${numero}/guion`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'regenerate-actos' }),
        signal: abortRef.current?.signal,
      });
      const data = await res.json();
      if (!res.ok) { showToast(data.error || 'Error regenerando actos', 'error'); closeProgress(); return; }
      setGuion(data);
      addProgressLog('Actos regenerados (metadata conservada)', 'success');
      finishProgress(1, 1);
      showToast('Actos regenerados correctamente', 'success');
    } catch (error: any) {
      if (error.name !== 'AbortError') { showToast('Error de conexión al regenerar actos', 'error'); }
      closeProgress();
    }
  };

  const handleGeneratePrompts = async () => {
    if (!escaleta) return;
    startProgress('Generando Prompts', 0, 'Iniciando generación de prompts...');
    try {
      const res = await fetch(`/api/projects/${slug}/capitulos/${numero}/prompts`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'generate-stream' }),
        signal: abortRef.current?.signal,
      });
      if (!res.ok) {
        const errData = await res.json();
        showToast(errData.error || 'Error generando prompts', 'error');
        closeProgress();
        return;
      }
      const data = await readSSEStream(res, (current, total, label) => {
        updateProgress(current, total, label);
        if (current > 0) addProgressLog(`Lote ${current}`, 'success', label);
      }, abortRef.current?.signal);

      if (data) {
        setPromptsData(data);
        try {
          const imgRes = await fetch(`/api/projects/${slug}/capitulos/${numero}/imagenes`);
          if (imgRes.ok) { const imgData = await imgRes.json(); if (imgData) setImagenesData(imgData); }
        } catch { /* ok */ }
        addProgressLog(`${data.totalPrompts || 0} prompts generados`, 'success');
        finishProgress(data.totalPrompts || 1, data.totalPrompts || 1);
        showToast(`${data.totalPrompts || 0} prompts generados`, 'success');
      } else { closeProgress(); }
    } catch (error: any) {
      if (error.name !== 'AbortError' && error.message !== 'Cancelado') { showToast('Error al generar prompts', 'error'); }
      closeProgress();
    }
  };

  const handleGeneratePreEscaleta = async () => {
    if (!guion) return;
    startProgress('Generando Pre-Escaleta', 0, 'Calculando estructura de planos...');
    try {
      const res = await fetch(`/api/projects/${slug}/capitulos/${numero}/pre-escaleta`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'generate', guion }),
        signal: abortRef.current?.signal,
      });
      const data = await res.json();
      if (!res.ok) { showToast(data.error || 'Error generando pre-escaleta', 'error'); closeProgress(); return; }
      setPreEscaleta(data);
      addProgressLog(`Pre-escaleta: ${data.totalPlanos} planos en ${data.segmentos?.length} segmentos`, 'success');
      finishProgress(1, 1);
      showToast(`Pre-escaleta generada: ${data.totalPlanos} planos`, 'success');
    } catch (error: any) {
      if (error.name !== 'AbortError') { showToast('Error al generar pre-escaleta', 'error'); }
      closeProgress();
    }
  };

  const handleGenerateEscaleta = async () => {
    if (!guion || !preEscaleta) return;
    startProgress('Generando Escaleta', preEscaleta.segmentos.length, 'Iniciando generación por segmentos...');
    try {
      const res = await fetch(`/api/projects/${slug}/capitulos/${numero}/escaleta`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'generate-stream', guion }),
        signal: abortRef.current?.signal,
      });
      if (!res.ok) {
        const errData = await res.json();
        showToast(errData.error || 'Error generando escaleta', 'error');
        closeProgress();
        return;
      }
      const data = await readSSEStream(res, (current, total, label) => {
        updateProgress(current, total, label);
        if (current > 0) addProgressLog(`Segmento ${current}/${total}`, 'success', label);
      }, abortRef.current?.signal);

      if (data) {
        setEscaleta(data);
        addProgressLog(`Escaleta completa: ${data.planos?.length || 0} planos`, 'success');
        finishProgress(preEscaleta.segmentos.length, preEscaleta.segmentos.length);
        showToast('Escaleta generada correctamente', 'success');
      } else { closeProgress(); }
    } catch (error: any) {
      if (error.name !== 'AbortError' && error.message !== 'Cancelado') { showToast('Error al generar escaleta', 'error'); }
      closeProgress();
    }
  };

  const handleGenerateSinglePrompt = async (planoId: string) => {
    setGeneratingPromptSingle(planoId);
    startProgress('Generando Prompt', 0, `Prompt para plano ${planoId}...`);
    try {
      const res = await fetch(`/api/projects/${slug}/capitulos/${numero}/prompts`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'generate-single', planoId }),
        signal: abortRef.current?.signal,
      });
      const data = await res.json();
      if (!res.ok) { showToast(data.error || 'Error generando prompt', 'error'); closeProgress(); return; }
      if (imagenesData) {
        const updatedPlanos = imagenesData.planos.map(p => p.id === planoId ? { ...p, ...data.plano } : p);
        const withPrompt = updatedPlanos.filter((p: PlanoConPrompt) => p.promptImagen).length;
        setImagenesData({ ...imagenesData, planos: updatedPlanos, withPrompt });
      }
      addProgressLog(`Prompt generado para ${planoId}`, 'success');
      finishProgress(1, 1);
      showToast(`Prompt generado para plano ${planoId}`, 'success');
    } catch (error: any) {
      if (error.name !== 'AbortError') { showToast('Error al generar prompt', 'error'); }
      closeProgress();
    } finally { setGeneratingPromptSingle(null); }
  };

  const handleGeneratePlanoImage = async (planoId: string) => {
    setGeneratingPlano(planoId);
    startProgress('Generando Imagen', 0, `Generando imagen para plano ${planoId}...`);
    try {
      const res = await fetch(`/api/projects/${slug}/capitulos/${numero}/imagenes`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'generate', planoId }),
        signal: abortRef.current?.signal,
      });
      const data = await res.json();
      if (!res.ok) { showToast(data.error || 'Error generando imagen', 'error'); closeProgress(); return; }
      if (imagenesData) {
        const updatedPlanos = imagenesData.planos.map(p => p.id === planoId ? data.plano : p);
        const generated = updatedPlanos.filter((p: PlanoConPrompt) => p.imagenUrl).length;
        setImagenesData({ ...imagenesData, planos: updatedPlanos, generated });
      }
      setImgVersion(Date.now());
      addProgressLog(`Imagen generada para ${planoId}`, 'success');
      finishProgress(1, 1);
      showToast(`Imagen generada para plano ${planoId}`, 'success');
    } catch (error: any) {
      if (error.name !== 'AbortError') { showToast('Error al generar imagen', 'error'); }
      closeProgress();
    } finally { setGeneratingPlano(null); }
  };

  const handleGenerateAllImages = async (onlyPending = false) => {
    if (!imagenesData) return;
    const pending = onlyPending
      ? imagenesData.planos.filter(p => p.promptImagen && !p.imagenUrl)
      : imagenesData.planos.filter(p => p.promptImagen);
    if (pending.length === 0) return;

    startProgress('Generando Imágenes', pending.length, `Imagen 1/${pending.length}...`);
    let successCount = 0;
    let errorCount = 0;
    let localPlanos = [...imagenesData.planos];

    for (let i = 0; i < pending.length; i++) {
      if (abortRef.current?.signal.aborted) break;
      const plano = pending[i];
      updateProgress(i, pending.length, `Imagen ${i + 1}/${pending.length} — ${plano.numeroPlano || plano.id}`);

      try {
        const res = await fetch(`/api/projects/${slug}/capitulos/${numero}/imagenes`, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ action: 'generate', planoId: plano.id }),
          signal: abortRef.current?.signal,
        });
        const data = await res.json();
        if (res.ok) {
          localPlanos = localPlanos.map(p => p.id === plano.id ? data.plano : p);
          const generated = localPlanos.filter(p => p.imagenUrl).length;
          setImagenesData({ ...imagenesData, planos: localPlanos, generated });
          addProgressLog(`${plano.numeroPlano || plano.id}`, 'success');
          successCount++;
        } else {
          addProgressLog(`${plano.numeroPlano || plano.id}`, 'error', data.error);
          errorCount++;
        }
      } catch (error: any) {
        if (error.name === 'AbortError') break;
        addProgressLog(`${plano.numeroPlano || plano.id}`, 'error', 'Error de conexión');
        errorCount++;
      }
    }

    finishProgress(pending.length, pending.length);
    if (errorCount > 0) {
      showToast(`${successCount} imágenes generadas, ${errorCount} con error`, 'error');
    } else {
      showToast(`${successCount} imágenes generadas correctamente`, 'success');
    }
  };

  const handleGenerateFondo = async (fondoId: string) => {
    setGeneratingFondo(fondoId);
    startProgress('Generando Fondo', 0, `Generando fondo ${fondoId}...`);
    try {
      const res = await fetch(`/api/projects/${slug}/capitulos/${numero}/fondos`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'generate', fondoId }),
        signal: abortRef.current?.signal,
      });
      const data = await res.json();
      if (!res.ok) { showToast(data.error || 'Error generando fondo', 'error'); closeProgress(); return; }
      if (fondosData) {
        const updatedFondos = fondosData.fondos.map(f => f.id === fondoId ? data.fondo : f);
        const generated = updatedFondos.filter(f => f.imagenUrl).length;
        setFondosData({ ...fondosData, fondos: updatedFondos, generated });
      }
      setFondosVersion(v => v + 1);
      addProgressLog(`Fondo ${fondoId} generado`, 'success');
      finishProgress(1, 1);
      showToast('Fondo generado correctamente', 'success');
    } catch (error: any) {
      if (error.name !== 'AbortError') { showToast('Error al generar fondo', 'error'); }
      closeProgress();
    } finally { setGeneratingFondo(null); }
  };

  const handleGenerateAllFondos = async () => {
    if (!fondosData || fondosData.fondos.length === 0) return;
    const pending = fondosData.fondos;

    setGeneratingAllFondos(true);
    startProgress('Generando Fondos', pending.length, `Fondo 1/${pending.length}...`);
    let successCount = 0;
    let errorCount = 0;
    let localFondos = [...fondosData.fondos];

    for (let i = 0; i < pending.length; i++) {
      if (abortRef.current?.signal.aborted) break;
      const fondo = pending[i];
      updateProgress(i, pending.length, `Fondo ${i + 1}/${pending.length} — ${fondo.nombre}`);

      try {
        const res = await fetch(`/api/projects/${slug}/capitulos/${numero}/fondos`, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ action: 'generate', fondoId: fondo.id }),
          signal: abortRef.current?.signal,
        });
        const data = await res.json();
        if (res.ok) {
          localFondos = localFondos.map(f => f.id === fondo.id ? data.fondo : f);
          const generated = localFondos.filter(f => f.imagenUrl).length;
          setFondosData({ ...fondosData, fondos: localFondos, generated });
          setFondosVersion(v => v + 1);
          addProgressLog(`${fondo.nombre}`, 'success');
          successCount++;
        } else {
          addProgressLog(`${fondo.nombre}`, 'error', data.error);
          errorCount++;
        }
      } catch (error: any) {
        if (error.name === 'AbortError') break;
        addProgressLog(`${fondo.nombre}`, 'error', 'Error de conexión');
        errorCount++;
      }
    }

    finishProgress(pending.length, pending.length);
    setGeneratingAllFondos(false);
    if (errorCount > 0) {
      showToast(`${successCount} fondos generados, ${errorCount} con error`, 'error');
    } else {
      showToast(`${successCount} fondos generados correctamente`, 'success');
    }
  };

  const handleUpdateFondoDescription = async (fondoId: string, descripcion: string, promptGeneracion: string) => {
    try {
      const res = await fetch(`/api/projects/${slug}/capitulos/${numero}/fondos`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'update-description', fondoId, descripcion, promptGeneracion }),
      });
      const data = await res.json();
      if (!res.ok) {
        showToast(data.error || 'Error actualizando fondo', 'error');
        return;
      }
      if (fondosData) {
        const updatedFondos = fondosData.fondos.map(f =>
          f.id === fondoId ? data.fondo : f
        );
        setFondosData({ ...fondosData, fondos: updatedFondos });
      }
      showToast('Fondo actualizado', 'success');
    } catch (error) {
      console.error('Error updating fondo:', error);
      showToast('Error al actualizar fondo', 'error');
    }
  };

  const handleRefreshFondos = async () => {
    try {
      const res = await fetch(`/api/projects/${slug}/capitulos/${numero}/fondos`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'refresh' }),
      });
      const data = await res.json();
      if (!res.ok) { showToast(data.error || 'Error actualizando fondos', 'error'); return; }
      setFondosData(data);
      showToast('Fondos sincronizados con el guión', 'success');
    } catch {
      showToast('Error al sincronizar fondos', 'error');
    }
  };

  const handleSaveComposition = async (planoId: string, composition: PromptComposition) => {
    try {
      const res = await fetch(`/api/projects/${slug}/capitulos/${numero}/imagenes`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'save-composition', planoId, composition }),
      });
      const data = await res.json();
      if (!res.ok) {
        showToast(data.error || 'Error guardando composición', 'error');
        return;
      }
      // Update the specific plano in local state
      if (imagenesData) {
        const updatedPlanos = imagenesData.planos.map(p =>
          p.id === planoId ? data.plano : p
        );
        setImagenesData({ ...imagenesData, planos: updatedPlanos });
      }
      showToast('Composición guardada', 'success');
    } catch (error) {
      console.error('Error saving composition:', error);
      showToast('Error al guardar composición', 'error');
    }
  };

  const handleSavePersonajes = async (planoId: string, personajesDetalle: PersonajeEnPlano[]) => {
    try {
      const res = await fetch(`/api/projects/${slug}/capitulos/${numero}/imagenes`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'save-personajes', planoId, personajesDetalle }),
      });
      const data = await res.json();
      if (!res.ok) {
        showToast(data.error || 'Error guardando personajes', 'error');
        return;
      }
      if (imagenesData) {
        const updatedPlanos = imagenesData.planos.map(p =>
          p.id === planoId ? data.plano : p
        );
        setImagenesData({ ...imagenesData, planos: updatedPlanos });
      }
      showToast('Personajes actualizados', 'success');
    } catch (error) {
      console.error('Error saving personajes:', error);
      showToast('Error al guardar personajes', 'error');
    }
  };

  const handleGeneratePlanoVideo = async (planoId: string, customPrompt?: string) => {
    // El render es async (cola nocturna de H3): encolamos y devolvemos al instante; el estado
    // real (queued → processing → ready) lo refresca el polling de syncVideoJobs.
    setGeneratingVideo(planoId);
    try {
      const res = await fetch(`/api/projects/${slug}/capitulos/${numero}/videos`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'generate', planoId, promptVideo: customPrompt }),
      });
      const data = await res.json();
      if (!res.ok) { showToast(data.error || 'Error encolando el vídeo', 'error'); return; }
      if (videosData) {
        const updatedPlanos = videosData.planos.map(p => p.id === planoId ? data.plano : p);
        setVideosData({ ...videosData, planos: updatedPlanos });
      }
      showToast('Vídeo en cola — se generará en la ventana nocturna', 'success');
    } catch {
      showToast('Error al encolar el vídeo', 'error');
    } finally { setGeneratingVideo(null); }
  };

  const handleGenerateAllVideos = async (onlyPending = false) => {
    if (!videosData) return;
    const targets = onlyPending
      ? videosData.planos.filter(p => p.imagenUrl && !(p.videoVariations || []).some(v => v.status === 'ready' || v.status === 'queued' || v.status === 'processing'))
      : videosData.planos.filter(p => p.imagenUrl);
    if (targets.length === 0) return;

    startProgress('Encolando vídeos', targets.length, `Encolando 1/${targets.length}...`);
    let successCount = 0;
    let errorCount = 0;
    let localPlanos = [...videosData.planos];

    for (let i = 0; i < targets.length; i++) {
      if (abortRef.current?.signal.aborted) break;
      const plano = targets[i];
      updateProgress(i, targets.length, `Encolando ${i + 1}/${targets.length} — ${plano.numeroPlano || plano.id}`);

      try {
        const res = await fetch(`/api/projects/${slug}/capitulos/${numero}/videos`, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ action: 'generate', planoId: plano.id }),
          signal: abortRef.current?.signal,
        });
        const data = await res.json();
        if (res.ok) {
          localPlanos = localPlanos.map(p => p.id === plano.id ? data.plano : p);
          setVideosData({ ...videosData, planos: localPlanos });
          addProgressLog(`${plano.numeroPlano || plano.id}`, 'success');
          successCount++;
        } else {
          addProgressLog(`${plano.numeroPlano || plano.id}`, 'error', data.error);
          errorCount++;
        }
      } catch (error: any) {
        if (error.name === 'AbortError') break;
        addProgressLog(`${plano.numeroPlano || plano.id}`, 'error', 'Error de conexión');
        errorCount++;
      }
    }

    finishProgress(targets.length, targets.length);
    if (errorCount > 0) {
      showToast(`${successCount} vídeos encolados, ${errorCount} con error`, 'error');
    } else {
      showToast(`${successCount} vídeos en cola — se generarán en la ventana nocturna`, 'success');
    }
  };

  // Refresca el estado de los jobs de vídeo pendientes (queued/processing) del capítulo.
  const syncVideoJobs = useCallback(async () => {
    try {
      const res = await fetch(`/api/projects/${slug}/capitulos/${numero}/videos`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'sync-jobs' }),
      });
      if (!res.ok) return;
      const data = await res.json();
      if (data.planos) {
        setVideosData(prev => prev ? { ...prev, planos: data.planos, generated: data.generated } : prev);
      }
    } catch {
      // fallo transitorio de red: reintenta en el próximo tick
    }
  }, [slug, numero]);

  // Polling mientras haya variaciones de vídeo pendientes. Sin pings agresivos: cada 45s.
  const hasPendingVideos = !!videosData?.planos.some(
    p => (p.videoVariations || []).some(v => v.status === 'queued' || v.status === 'processing')
  );
  useEffect(() => {
    if (!hasPendingVideos) return;
    syncVideoJobs(); // refresco inmediato al detectar pendientes
    const id = setInterval(syncVideoJobs, 45000);
    return () => clearInterval(id);
  }, [hasPendingVideos, syncVideoJobs]);

  const handleSaveVideoPrompt = async (planoId: string, promptVideo: string) => {
    try {
      const res = await fetch(`/api/projects/${slug}/capitulos/${numero}/videos`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'save-prompt', planoId, promptVideo }),
      });
      const data = await res.json();
      if (!res.ok) {
        showToast(data.error || 'Error guardando prompt de video', 'error');
        return;
      }
      if (videosData) {
        const updatedPlanos = videosData.planos.map(p =>
          p.id === planoId ? data.plano : p
        );
        setVideosData({ ...videosData, planos: updatedPlanos });
      }
      showToast('Prompt de video guardado', 'success');
    } catch (error) {
      console.error('Error saving video prompt:', error);
      showToast('Error al guardar prompt de video', 'error');
    }
  };

  const handleSelectVideoVariation = async (planoId: string, variationId: string) => {
    try {
      const res = await fetch(`/api/projects/${slug}/capitulos/${numero}/videos`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'select-variation', planoId, variationId }),
      });
      const data = await res.json();
      if (!res.ok) {
        showToast(data.error || 'Error seleccionando variación', 'error');
        return;
      }
      if (videosData) {
        const updatedPlanos = videosData.planos.map(p =>
          p.id === planoId ? data.plano : p
        );
        setVideosData({ ...videosData, planos: updatedPlanos });
      }
      showToast('Variación seleccionada', 'success');
    } catch (error) {
      console.error('Error selecting variation:', error);
      showToast('Error al seleccionar variación', 'error');
    }
  };

  const handleDeleteVideoVariation = async (planoId: string, variationId: string) => {
    if (!confirm('¿Eliminar esta variación de video?')) return;
    try {
      const res = await fetch(`/api/projects/${slug}/capitulos/${numero}/videos`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'delete-variation', planoId, variationId }),
      });
      const data = await res.json();
      if (!res.ok) {
        showToast(data.error || 'Error eliminando variación', 'error');
        return;
      }
      if (videosData) {
        const updatedPlanos = videosData.planos.map(p =>
          p.id === planoId ? data.plano : p
        );
        setVideosData({ ...videosData, planos: updatedPlanos });
      }
      showToast('Variación eliminada', 'success');
    } catch (error) {
      console.error('Error deleting variation:', error);
      showToast('Error al eliminar variación', 'error');
    }
  };

  const handleStartRender = async () => {
    try {
      const res = await fetch('/api/render', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          projectSlug: slug,
          capituloNumero: parseInt(numero),
          includeIntro: true,
          transitionType: 'crossfade',
          quality: 90,
        }),
      });

      const data = await res.json();
      if (!res.ok) {
        showToast(data.error || 'Error iniciando render', 'error');
        return;
      }

      const { jobId } = data;
      setRenderJobId(jobId);

      // Poll for progress
      const pollInterval = setInterval(async () => {
        const progressRes = await fetch(`/api/render?jobId=${jobId}`);
        const progress = await progressRes.json();

        setRenderProgress(progress);

        if (progress.phase === 'complete') {
          clearInterval(pollInterval);
          setRenderJobId(null);
          showToast(`Video renderizado: ${progress.outputPath}`, 'success');
        } else if (progress.phase === 'error') {
          clearInterval(pollInterval);
          setRenderJobId(null);
          showToast(`Error: ${progress.message}`, 'error');
        }
      }, 2000);
    } catch (error) {
      console.error('Error starting render:', error);
      showToast('Error al iniciar renderizado', 'error');
    }
  };

  // Build fondoId → imagenUrl lookup
  const fondoImagenMap: Record<string, string> = {};
  if (fondosData) {
    for (const f of fondosData.fondos) {
      if (f.imagenUrl) fondoImagenMap[f.id] = `${f.imagenUrl}?v=${fondosVersion}`;
    }
  }

  if (loading) {
    return (
      <div className="min-h-screen flex items-center justify-center bg-[var(--background)]">
        <div className="animate-spin w-5 h-5 border-2 border-indigo-600 border-t-transparent rounded-full" />
      </div>
    );
  }

  return (
    <div className="min-h-screen bg-[var(--background)]">
      {toast && <Toast message={toast.message} type={toast.type} onClose={() => setToast(null)} />}
      <ProgressModal
        open={progress.open}
        title={progress.title}
        current={progress.current}
        total={progress.total}
        currentLabel={progress.currentLabel}
        logs={progress.logs}
        onCancel={closeProgress}
      />

      <header className="border-b border-stone-200/60 bg-white/80 backdrop-blur-sm sticky top-0 z-10">
        <div className="max-w-5xl mx-auto px-6 py-3 flex items-center gap-4">
          <Link href={`/project/${slug}`} className="p-1 -ml-1 text-stone-400 hover:text-stone-600 transition-colors">
            <ArrowLeft className="w-4 h-4" />
          </Link>
          <div className="min-w-0">
            <h1 className="text-sm font-semibold text-stone-900 truncate">
              Cap. {numero} {guion?.titulo ? `— ${guion.titulo}` : ''}
            </h1>
          </div>
        </div>
      </header>

      <main className="max-w-5xl mx-auto px-6 py-8">
        <WizardStepper
          steps={CAPITULO_STEPS}
          currentStep={currentStep}
          onStepClick={(index) => setCurrentStep(index)}
        />

        <div className="mt-8">
          {/* Step 0: Guión Conductor */}
          {currentStep === 0 && (
            <div className="space-y-6">
              <div>
                <h2 className="text-xl font-semibold text-stone-900">Guión Conductor</h2>
                <p className="mt-1 text-stone-500 text-sm">Estructura narrativa del capítulo.</p>
              </div>

              {!guion ? (
                <div className="text-center py-16">
                  <p className="text-stone-400 text-sm mb-5">La IA creará la estructura narrativa basada en tu Idea Motriz.</p>
                  <Button onClick={handleGenerateGuion} loading={generating}>
                    <Sparkles className="w-4 h-4" />
                    Generar guión
                  </Button>
                </div>
              ) : (
                <div className="bg-white rounded-lg border border-stone-200 overflow-hidden">
                  <div className="px-5 py-3 border-b border-stone-100 flex items-center justify-between">
                    <div className="flex items-baseline gap-3 min-w-0">
                      <span className="text-sm font-medium text-stone-800 truncate">{guion.titulo}</span>
                      {guion.virtudGanada && <span className="text-xs text-stone-400 flex-shrink-0">{guion.virtudGanada}</span>}
                    </div>
                    <div className="flex gap-1.5 flex-shrink-0">
                      {!editingGuion && !editingGuionFields && (
                        <>
                          <Button variant="ghost" size="sm" onClick={() => { setEditingGuionFields(true); setEditingGuion(false); }}>
                            <Edit3 className="w-3.5 h-3.5" />
                            Editar campos
                          </Button>
                          <Button variant="ghost" size="sm" onClick={() => { setGuionDraft(guion.contenidoMarkdown || ''); setEditingGuion(true); setEditingGuionFields(false); }}>
                            <FileText className="w-3.5 h-3.5" />
                            Editar markdown
                          </Button>
                        </>
                      )}
                      {editingGuion && (
                        <>
                          <Button variant="ghost" size="sm" onClick={() => setEditingGuion(false)}>
                            <Eye className="w-3.5 h-3.5" />
                            Vista previa
                          </Button>
                          <Button size="sm" onClick={handleSaveGuionMarkdown} loading={generating}>
                            <Save className="w-3.5 h-3.5" />
                            Guardar
                          </Button>
                        </>
                      )}
                      <Button variant="outline" size="sm" onClick={handleRegenerateActos} loading={generating}>
                        <Sparkles className="w-3.5 h-3.5" />
                        Regenerar actos
                      </Button>
                    </div>
                  </div>

                  <div className="px-5 py-4">
                    {editingGuionFields ? (
                      <GuionFieldsEditor
                        guion={guion}
                        onSave={handleSaveGuionFields}
                        onCancel={() => setEditingGuionFields(false)}
                      />
                    ) : (
                      <>
                        <div className="flex gap-6 mb-5 text-sm">
                          {guion.duracionTotal && (
                            <div>
                              <span className="text-[11px] font-medium text-stone-400 uppercase tracking-wider">Duración</span>
                              <p className="text-stone-800 font-medium mt-0.5">{guion.duracionTotal}</p>
                            </div>
                          )}
                          {guion.aliado?.nombre && (
                            <div>
                              <span className="text-[11px] font-medium text-stone-400 uppercase tracking-wider">Aliado</span>
                              <p className="text-stone-800 font-medium mt-0.5">{guion.aliado.nombre}</p>
                            </div>
                          )}
                          {guion.gemaGanada && (
                            <div>
                              <span className="text-[11px] font-medium text-stone-400 uppercase tracking-wider">Gema</span>
                              <p className="text-stone-800 font-medium mt-0.5">{guion.gemaGanada}</p>
                            </div>
                          )}
                        </div>

                        {guion.nudoDelMundo && (
                          <div className="mb-5 border-l-2 border-stone-200 pl-4">
                            <span className="text-[11px] font-medium text-stone-400 uppercase tracking-wider">Nudo del Mundo</span>
                            <p className="text-sm text-stone-600 mt-0.5">{guion.nudoDelMundo}</p>
                          </div>
                        )}

                        {editingGuion ? (
                          <textarea
                            value={guionDraft}
                            onChange={(e) => setGuionDraft(e.target.value)}
                            className="w-full h-[500px] px-4 py-3 border border-stone-200 rounded-lg font-mono text-sm bg-white text-stone-900 focus:outline-none focus:ring-2 focus:ring-indigo-500/20 focus:border-indigo-400 resize-y transition-colors"
                          />
                        ) : guion.contenidoMarkdown ? (
                          <div className="prose prose-sm max-w-none prose-headings:text-stone-900 prose-h2:text-lg prose-h2:border-b prose-h2:border-stone-100 prose-h2:pb-2 prose-h3:text-base prose-p:text-stone-600 prose-table:text-sm prose-td:p-2 prose-th:p-2 prose-th:bg-stone-50 prose-th:text-stone-600">
                            <ReactMarkdown remarkPlugins={[remarkGfm]}>{guion.contenidoMarkdown}</ReactMarkdown>
                          </div>
                        ) : null}
                      </>
                    )}
                  </div>
                </div>
              )}
            </div>
          )}

          {/* Step 1: Pre-Escaleta + Escaleta */}
          {currentStep === 1 && (
            <div className="space-y-6">
              <div>
                <h2 className="text-xl font-semibold text-stone-900">Escaleta</h2>
                <p className="mt-1 text-stone-500 text-sm">Desglose plano por plano con tiempos exactos.</p>
              </div>

              {/* Pre-Escaleta section */}
              <div className="bg-white rounded-lg border border-stone-200 overflow-hidden">
                <div className="px-5 py-3 border-b border-stone-100 flex items-center justify-between">
                  <div className="flex items-baseline gap-3">
                    <span className="text-sm font-medium text-stone-800">Pre-Escaleta</span>
                    <span className="text-xs text-stone-400">Estructura de planos por escena</span>
                  </div>
                  <Button
                    variant={preEscaleta ? 'outline' : 'primary'}
                    size="sm"
                    onClick={handleGeneratePreEscaleta}
                    loading={generating}
                    disabled={!guion || !guion.actos}
                  >
                    <Sparkles className="w-3.5 h-3.5" />
                    {preEscaleta ? 'Regenerar' : 'Generar'}
                  </Button>
                </div>

                {!guion?.actos && (
                  <div className="px-5 py-6 text-center">
                    <p className="text-sm text-amber-600">El guión conductor no tiene actos estructurados. Regenera el guión primero.</p>
                  </div>
                )}

                {preEscaleta && (
                  <div className="px-5 py-4">
                    <div className="flex items-center gap-6 mb-4 text-sm">
                      <div>
                        <span className="text-[11px] font-medium text-stone-400 uppercase tracking-wider">Total planos</span>
                        <p className="text-lg font-semibold text-stone-800">{preEscaleta.totalPlanos}</p>
                      </div>
                      <div className="w-px h-8 bg-stone-200" />
                      <div>
                        <span className="text-[11px] font-medium text-stone-400 uppercase tracking-wider">Duración</span>
                        <p className="text-lg font-semibold text-stone-800">
                          {Math.floor(preEscaleta.duracionTotalSegundos / 60)}:{(preEscaleta.duracionTotalSegundos % 60).toString().padStart(2, '0')}
                          <span className="text-sm font-normal text-stone-400 ml-1">({preEscaleta.duracionTotalSegundos}s)</span>
                        </p>
                      </div>
                      <div className="w-px h-8 bg-stone-200" />
                      <div>
                        <span className="text-[11px] font-medium text-stone-400 uppercase tracking-wider">Segmentos</span>
                        <p className="text-lg font-semibold text-stone-800">{preEscaleta.segmentos.length}</p>
                      </div>
                    </div>

                    <div className="border border-stone-200 rounded-lg overflow-hidden">
                      <table className="w-full text-sm">
                        <thead>
                          <tr className="bg-stone-50 text-stone-500 text-xs">
                            <th className="px-3 py-2 text-left font-medium">Segmento</th>
                            <th className="px-3 py-2 text-left font-medium">Tiempo</th>
                            <th className="px-3 py-2 text-right font-medium">Duración</th>
                            <th className="px-3 py-2 text-right font-medium">Planos</th>
                          </tr>
                        </thead>
                        <tbody className="divide-y divide-stone-100">
                          {preEscaleta.segmentos.map((seg) => (
                            <tr key={seg.segmentoId} className="hover:bg-stone-50/50">
                              <td className="px-3 py-2 font-medium text-stone-700">
                                <span className="text-stone-400 mr-1">{seg.segmentoId}</span>
                                {seg.nombre}
                              </td>
                              <td className="px-3 py-2 text-stone-500">{seg.tiempo}</td>
                              <td className="px-3 py-2 text-right text-stone-600">{seg.duracionSegundos}s</td>
                              <td className="px-3 py-2 text-right">
                                <span className="inline-flex items-center px-2 py-0.5 bg-indigo-50 text-indigo-700 rounded-full text-xs font-medium">
                                  {seg.totalPlanos}
                                </span>
                              </td>
                            </tr>
                          ))}
                        </tbody>
                      </table>
                    </div>
                  </div>
                )}
              </div>

              {/* Escaleta section */}
              {!preEscaleta ? (
                <div className="text-center py-8">
                  <p className="text-stone-400 text-sm">Genera la pre-escaleta primero para definir la estructura de planos.</p>
                </div>
              ) : !escaleta ? (
                <div className="text-center py-8">
                  <p className="text-stone-400 text-sm mb-5">La pre-escaleta define {preEscaleta.totalPlanos} planos. Ahora la IA rellenará cada plano con descripción detallada.</p>
                  <Button
                    onClick={handleGenerateEscaleta}
                    loading={generating}
                  >
                    <Sparkles className="w-4 h-4" />
                    Generar escaleta ({preEscaleta.totalPlanos} planos)
                  </Button>
                </div>
              ) : (
                <>
                  {/* Warning for characters without assets */}
                  {escaleta.planos && (() => {
                    const allIds = new Set<string>();
                    for (const p of escaleta.planos) {
                      for (const pd of p.personajesDetalle || []) {
                        if (!assetsMap[pd.personajeId]?.hasAssets) allIds.add(pd.nombre);
                      }
                    }
                    if (allIds.size === 0) return null;
                    return (
                      <div className="flex items-center gap-2.5 px-4 py-3 bg-amber-50 border border-amber-200 rounded-lg">
                        <AlertTriangle className="w-4 h-4 text-amber-500 flex-shrink-0" />
                        <span className="text-sm text-amber-800">
                          Personajes sin assets generados: <strong>{Array.from(allIds).join(', ')}</strong>
                        </span>
                        <Link
                          href={`/project/${slug}`}
                          className="ml-auto text-sm font-medium text-amber-600 hover:text-amber-800 transition-colors"
                        >
                          Ir a Personajes
                        </Link>
                      </div>
                    );
                  })()}

                  {/* Summary bar */}
                  {escaleta.planos && escaleta.planos.length > 0 && (() => {
                    const totalSeg = escaleta.planos.reduce((sum, p) => sum + (p.tiempoSegundos || 0), 0);
                    const mins = Math.floor(totalSeg / 60);
                    const secs = totalSeg % 60;
                    return (
                      <div className="flex items-center gap-6 px-4 py-3 bg-white border border-stone-200 rounded-lg">
                        <div>
                          <span className="text-[11px] font-medium text-stone-400 uppercase tracking-wider">Planos</span>
                          <p className="text-lg font-semibold text-stone-800">{escaleta.planos.length}</p>
                        </div>
                        <div className="w-px h-8 bg-stone-200" />
                        <div>
                          <span className="text-[11px] font-medium text-stone-400 uppercase tracking-wider">Duración total</span>
                          <p className="text-lg font-semibold text-stone-800">{mins}:{secs.toString().padStart(2, '0')} <span className="text-sm font-normal text-stone-400">({totalSeg}s)</span></p>
                        </div>
                        {escaleta.duracionTotalSegundos && totalSeg !== escaleta.duracionTotalSegundos && (
                          <>
                            <div className="w-px h-8 bg-stone-200" />
                            <div>
                              <span className="text-[11px] font-medium text-stone-400 uppercase tracking-wider">Objetivo</span>
                              <p className="text-lg font-semibold text-stone-800">{Math.floor(escaleta.duracionTotalSegundos / 60)}:{(escaleta.duracionTotalSegundos % 60).toString().padStart(2, '0')}</p>
                            </div>
                          </>
                        )}
                      </div>
                    );
                  })()}

                  <div className="bg-white rounded-lg border border-stone-200 overflow-hidden">
                    <div className="px-5 py-3 border-b border-stone-100 flex items-center justify-between">
                      <div className="flex items-center gap-3 min-w-0">
                        <span className="text-sm font-medium text-stone-800 truncate">{escaleta.capitulo || 'Escaleta'}</span>
                        {escaleta.virtudGanada && <span className="text-xs text-stone-400 flex-shrink-0">{escaleta.virtudGanada}</span>}
                      </div>
                      <div className="flex gap-1.5 flex-shrink-0">
                        {/* View toggle */}
                        <div className="flex border border-stone-200 rounded-md overflow-hidden mr-1">
                          <button
                            onClick={() => { setEscaletaView('cards'); setEditingEscaleta(false); }}
                            className={`px-2 py-1 text-xs font-medium transition-colors ${escaletaView === 'cards' ? 'bg-indigo-50 text-indigo-600' : 'text-stone-400 hover:text-stone-600'}`}
                          >
                            <LayoutGrid className="w-3.5 h-3.5" />
                          </button>
                          <button
                            onClick={() => { setEscaletaView('markdown'); setEditingEscaleta(false); }}
                            className={`px-2 py-1 text-xs font-medium transition-colors ${escaletaView === 'markdown' ? 'bg-indigo-50 text-indigo-600' : 'text-stone-400 hover:text-stone-600'}`}
                          >
                            <FileText className="w-3.5 h-3.5" />
                          </button>
                        </div>
                        {escaletaView === 'markdown' && !editingEscaleta && (
                          <Button variant="ghost" size="sm" onClick={() => { setEscaletaDraft(escaleta.contenidoMarkdown || ''); setEditingEscaleta(true); }}>
                            <Edit3 className="w-3.5 h-3.5" />
                            Editar
                          </Button>
                        )}
                        {editingEscaleta && (
                          <>
                            <Button variant="ghost" size="sm" onClick={() => setEditingEscaleta(false)}>
                              <Eye className="w-3.5 h-3.5" />
                              Vista previa
                            </Button>
                            <Button size="sm" onClick={handleSaveEscaletaMarkdown} loading={generating}>
                              <Save className="w-3.5 h-3.5" />
                              Guardar
                            </Button>
                          </>
                        )}
                        <Button variant="outline" size="sm" onClick={handleGenerateEscaleta} loading={generating}>
                          <Sparkles className="w-3.5 h-3.5" />
                          Regenerar
                        </Button>
                      </div>
                    </div>

                    {/* Cards view */}
                    {escaletaView === 'cards' && escaleta.planos && (
                      <div className="p-4 space-y-3">
                        {escaleta.planos.map((plano, i) => (
                          <PlanoCard
                            key={plano.id || i}
                            plano={plano}
                            index={i}
                            assetsMap={assetsMap}
                            slug={slug}
                          />
                        ))}
                      </div>
                    )}

                    {/* Markdown view */}
                    {escaletaView === 'markdown' && (
                      <div className="px-5 py-4">
                        {editingEscaleta ? (
                          <textarea
                            value={escaletaDraft}
                            onChange={(e) => setEscaletaDraft(e.target.value)}
                            className="w-full h-[500px] px-4 py-3 border border-stone-200 rounded-lg font-mono text-sm bg-white text-stone-900 focus:outline-none focus:ring-2 focus:ring-indigo-500/20 focus:border-indigo-400 resize-y transition-colors"
                          />
                        ) : escaleta.contenidoMarkdown ? (
                          <div className="prose prose-sm max-w-none prose-headings:text-stone-900 prose-h2:text-lg prose-h2:border-b prose-h2:border-stone-100 prose-h2:pb-2 prose-h3:text-base prose-p:text-stone-600 prose-table:text-sm prose-td:p-2 prose-th:p-2 prose-th:bg-stone-50 prose-th:text-stone-600">
                            <ReactMarkdown remarkPlugins={[remarkGfm]}>{escaleta.contenidoMarkdown}</ReactMarkdown>
                          </div>
                        ) : null}
                      </div>
                    )}
                  </div>
                </>
              )}
            </div>
          )}

          {/* Step 2: Prompts + Generar Imagen */}
          {currentStep === 2 && (
            <div className="space-y-6">
              <div>
                <h2 className="text-xl font-semibold text-stone-900">Prompts de Imágenes</h2>
                <p className="mt-1 text-stone-500 text-sm">Prompts con referencias visuales. Genera la imagen de cada plano aquí.</p>
              </div>

              {!imagenesData ? (
                <div className="text-center py-16">
                  <p className="text-stone-400 text-sm mb-5">
                    {!escaleta ? 'Primero debes generar la escaleta.' : promptsData ? 'Los prompts necesitan regenerarse para incluir datos estructurados.' : 'La IA creará prompts optimizados para cada plano.'}
                  </p>
                  <Button onClick={handleGeneratePrompts} loading={generating} disabled={!escaleta}>
                    <Sparkles className="w-4 h-4" />
                    {promptsData ? 'Regenerar prompts' : 'Generar prompts'}
                  </Button>
                </div>
              ) : (
                <>
                  {/* Header with stats + actions */}
                  <div className="bg-white rounded-lg border border-stone-200 px-5 py-3 flex items-center justify-between">
                    <div className="flex items-baseline gap-3">
                      <span className="text-xs font-medium text-stone-400 uppercase tracking-wide">Progreso</span>
                      <span className="text-sm font-medium text-stone-700">
                        {imagenesData.withPrompt ?? 0}/{imagenesData.total} prompts · {imagenesData.generated}/{imagenesData.total} imágenes
                        {imagenesData.total > 0 && (
                          <span className="text-stone-400 font-normal ml-1">
                            ({(imagenesData.total * 0.134).toFixed(2).replace('.', ',')}€)
                          </span>
                        )}
                      </span>
                      {imagenesData.total > 0 && (
                        <div className="w-24 h-1.5 bg-stone-100 rounded-full overflow-hidden">
                          <div
                            className="h-full bg-indigo-500 rounded-full transition-all"
                            style={{ width: `${(imagenesData.generated / imagenesData.total) * 100}%` }}
                          />
                        </div>
                      )}
                    </div>
                    <div className="flex items-center gap-2">
                      <Button variant="outline" size="sm" onClick={handleGeneratePrompts} loading={generating} disabled={!!generatingPlano}>
                        <Sparkles className="w-3.5 h-3.5" />
                        Regenerar prompts
                      </Button>
                      {(() => {
                        const pendingCount = imagenesData.planos.filter(p => p.promptImagen && !p.imagenUrl).length;
                        return (
                          <>
                            {pendingCount > 0 && (
                              <Button
                                size="sm"
                                onClick={() => handleGenerateAllImages(true)}
                                loading={generating}
                                disabled={!!generatingPlano}
                              >
                                <Image className="w-3.5 h-3.5" />
                                Generar pendientes ({pendingCount})
                              </Button>
                            )}
                            <Button
                              size="sm"
                              variant={pendingCount > 0 ? 'outline' : 'primary'}
                              onClick={() => handleGenerateAllImages(false)}
                              loading={generating}
                              disabled={!!generatingPlano || (imagenesData.withPrompt ?? 0) === 0}
                            >
                              <Image className="w-3.5 h-3.5" />
                              Regenerar todas
                            </Button>
                          </>
                        );
                      })()}
                    </div>
                  </div>

                  {/* Fondos de Referencia */}
                  {fondosData && fondosData.fondos.length > 0 && (
                    <div className="bg-white rounded-lg border border-stone-200 overflow-hidden">
                      <div className="px-5 py-3 border-b border-stone-100 flex items-center justify-between">
                        <div className="flex items-center gap-2.5">
                          <Palette className="w-4 h-4 text-indigo-500" />
                          <span className="text-sm font-medium text-stone-800">Fondos de Referencia</span>
                          <span className="text-xs text-stone-400">{fondosData.generated}/{fondosData.total} generados</span>
                        </div>
                        <div className="flex items-center gap-2">
                          <Button
                            size="sm"
                            variant="ghost"
                            onClick={handleRefreshFondos}
                            disabled={!!generatingFondo || generatingAllFondos}
                          >
                            <RefreshCw className="w-3.5 h-3.5" />
                            Sincronizar
                          </Button>
                          <Button
                            size="sm"
                            variant="outline"
                            onClick={handleGenerateAllFondos}
                            loading={generatingAllFondos}
                            disabled={!!generatingFondo}
                          >
                            <Sparkles className="w-3.5 h-3.5" />
                            {fondosData.generated === fondosData.total && fondosData.generated > 0 ? 'Regenerar todos' : 'Generar todos'}
                          </Button>
                        </div>
                      </div>
                      <div className="p-4 grid grid-cols-2 md:grid-cols-3 gap-3">
                        {fondosData.fondos.map((fondo) => (
                          <FondoCard
                            key={fondo.id}
                            fondo={fondo}
                            onGenerate={handleGenerateFondo}
                            onUpdateDescription={handleUpdateFondoDescription}
                            generating={generatingFondo === fondo.id}
                            disabled={!!generatingFondo || generatingAllFondos}
                            cacheKey={fondosVersion}
                          />
                        ))}
                      </div>
                    </div>
                  )}

                  {/* Plano cards with prompts + generate */}
                  <div className="space-y-4">
                    {imagenesData.planos.map((plano, i) => (
                      <div key={plano.id} id={`plano-${plano.id}`}>
                        <PlanoAssetCard
                          plano={plano}
                          index={i}
                          slug={slug}
                          capNumero={numero}
                          estiloVisual={imagenesData.estiloVisual || ''}
                          fondoImagenUrl={plano.fondoId ? fondoImagenMap[plano.fondoId] : undefined}
                          imgVersion={imgVersion}
                          personajes={personajesList}
                          onGeneratePrompt={handleGenerateSinglePrompt}
                          onGenerateImage={handleGeneratePlanoImage}
                          onSaveComposition={handleSaveComposition}
                          onSavePersonajes={handleSavePersonajes}
                          generatingPrompt={generatingPromptSingle}
                          generatingImage={generatingPlano}
                        />
                      </div>
                    ))}
                  </div>

                  {/* Fixed vertical plano navigator */}
                  <PlanoNavigator planos={imagenesData.planos} />
                </>
              )}
            </div>
          )}

          {/* Step 3: Assets (imágenes y videos) */}
          {currentStep === 3 && (
            <div className="space-y-6">
              <div>
                <h2 className="text-xl font-semibold text-stone-900">Assets</h2>
                <p className="mt-1 text-stone-500 text-sm">Imágenes y videos generados para el capítulo.</p>
              </div>

              {!imagenesData || imagenesData.generated === 0 ? (
                <div className="text-center py-16">
                  <Image className="w-8 h-8 text-stone-300 mx-auto mb-3" />
                  <p className="text-stone-400 text-sm mb-5">
                    {!imagenesData ? 'Primero genera los prompts en el paso anterior.' : 'Aún no hay imágenes generadas. Ve al paso Prompts para generarlas.'}
                  </p>
                  <Button variant="outline" size="sm" onClick={() => setCurrentStep(2)}>
                    Ir a Prompts
                  </Button>
                </div>
              ) : (
                <>
                  {/* Stats header with bulk generation */}
                  <div className="bg-white rounded-lg border border-stone-200 px-5 py-3 flex items-center justify-between">
                    <div className="flex items-baseline gap-3">
                      <span className="text-xs font-medium text-stone-400 uppercase tracking-wide">Progreso</span>
                      <span className="text-sm font-medium text-stone-700">
                        {imagenesData.generated}/{imagenesData.total} imágenes · {videosData?.generated || 0}/{imagenesData.generated} videos
                        {videosData && videosData.planos.length > 0 && (() => {
                          const totalTime = videosData.planos
                            .flatMap(p => p.videoVariations || [])
                            .reduce((sum, v) => sum + (v.generationTimeSeconds || 0), 0);
                          const totalCost = videosData.planos
                            .flatMap(p => p.videoVariations || [])
                            .reduce((sum, v) => sum + (v.costUsd || 0), 0);
                          return totalTime > 0 || totalCost > 0 ? (
                            <span className="text-stone-400 font-normal ml-2">
                              ({totalTime > 0 && `${totalTime.toFixed(0)}s`}
                              {totalTime > 0 && totalCost > 0 && ' · '}
                              {totalCost > 0 && `$${totalCost.toFixed(2)}`})
                            </span>
                          ) : null;
                        })()}
                      </span>
                    </div>
                    <div className="flex items-center gap-2">
                      {videosData && (() => {
                        const pendingCount = videosData.planos.filter(p => p.imagenUrl && (!p.videoVariations || p.videoVariations.length === 0)).length;
                        return (
                          <>
                            {pendingCount > 0 && (
                              <Button
                                size="sm"
                                onClick={() => handleGenerateAllVideos(true)}
                                loading={generating}
                                disabled={!!generatingVideo}
                              >
                                <Video className="w-3.5 h-3.5" />
                                Generar videos pendientes ({pendingCount})
                              </Button>
                            )}
                            <Button
                              size="sm"
                              variant={pendingCount > 0 ? 'outline' : 'primary'}
                              onClick={() => handleGenerateAllVideos(false)}
                              loading={generating}
                              disabled={!!generatingVideo || imagenesData.generated === 0}
                            >
                              <Video className="w-3.5 h-3.5" />
                              Generar variaciones en todos
                            </Button>
                            <Button
                              size="sm"
                              variant="secondary"
                              onClick={handleStartRender}
                              disabled={!videosData || videosData.generated === 0 || !!renderJobId}
                              loading={renderProgress?.phase === 'rendering' || renderProgress?.phase === 'bundling'}
                            >
                              <Film className="w-3.5 h-3.5" />
                              Renderizar Video Final
                            </Button>
                          </>
                        );
                      })()}
                    </div>
                  </div>

                  {/* Render progress indicator */}
                  {renderProgress && renderProgress.phase !== 'complete' && (
                    <div className="bg-indigo-50 border border-indigo-200 rounded-lg px-5 py-3">
                      <div className="flex items-center justify-between mb-2">
                        <span className="text-sm font-medium text-indigo-900">{renderProgress.message}</span>
                        <span className="text-sm font-semibold text-indigo-700">{renderProgress.progress}%</span>
                      </div>
                      <div className="w-full bg-indigo-100 rounded-full h-2 overflow-hidden">
                        <div
                          className="bg-indigo-600 h-2 rounded-full transition-all duration-300"
                          style={{ width: `${renderProgress.progress}%` }}
                        />
                      </div>
                    </div>
                  )}

                  {/* Video variations gallery */}
                  <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
                    {imagenesData.planos.filter(p => p.imagenUrl).map((plano) => {
                      const videoPlano = videosData?.planos.find(vp => vp.id === plano.id);
                      const mergedPlano = { ...plano, videoVariations: videoPlano?.videoVariations };

                      return (
                        <VideoVariationsCard
                          key={plano.id}
                          plano={mergedPlano}
                          onGenerateVariation={handleGeneratePlanoVideo}
                          onSelectVariation={handleSelectVideoVariation}
                          onDeleteVariation={handleDeleteVideoVariation}
                          generatingVideo={generatingVideo}
                        />
                      );
                    })}
                  </div>
                </>
              )}
            </div>
          )}
        </div>

        <div className="h-20" /> {/* spacer for fixed footer */}
      </main>

      <footer className="fixed bottom-0 left-0 right-0 z-10 border-t border-stone-200/60 bg-white/80 backdrop-blur-sm">
        <div className="max-w-5xl mx-auto px-6 py-3 flex justify-between">
          <Button
            variant="ghost"
            size="sm"
            onClick={() => setCurrentStep(Math.max(0, currentStep - 1))}
            disabled={currentStep === 0}
          >
            <ArrowLeft className="w-3.5 h-3.5" />
            Anterior
          </Button>
          <Button
            variant="outline"
            size="sm"
            onClick={() => setCurrentStep(Math.min(CAPITULO_STEPS.length - 1, currentStep + 1))}
            disabled={currentStep === CAPITULO_STEPS.length - 1}
          >
            Siguiente
            <ChevronRight className="w-3.5 h-3.5" />
          </Button>
        </div>
      </footer>
    </div>
  );
}

/** Fixed vertical sidebar for navigating between planos */
function PlanoNavigator({ planos }: { planos: PlanoConPrompt[] }) {
  const [activeId, setActiveId] = useState<string | null>(null);

  useEffect(() => {
    const observer = new IntersectionObserver(
      (entries) => {
        for (const entry of entries) {
          if (entry.isIntersecting) {
            setActiveId(entry.target.id.replace('plano-', ''));
            break;
          }
        }
      },
      { rootMargin: '-80px 0px -60% 0px', threshold: 0.1 }
    );
    for (const p of planos) {
      const el = document.getElementById(`plano-${p.id}`);
      if (el) observer.observe(el);
    }
    return () => observer.disconnect();
  }, [planos]);

  const scrollTo = (id: string) => {
    const el = document.getElementById(`plano-${id}`);
    if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' });
  };

  // Group planos by segment prefix (e.g. "1.1" from "1.1.3")
  const groups: { segment: string; items: PlanoConPrompt[] }[] = [];
  for (const p of planos) {
    const parts = (p.numeroPlano || '').split('.');
    const seg = parts.length >= 2 ? parts.slice(0, 2).join('.') : parts[0] || '?';
    const last = groups[groups.length - 1];
    if (last && last.segment === seg) {
      last.items.push(p);
    } else {
      groups.push({ segment: seg, items: [p] });
    }
  }

  return (
    <nav className="fixed right-3 top-1/2 -translate-y-1/2 z-20 flex flex-col items-end gap-0.5 max-h-[70vh] overflow-y-auto py-2 pr-1 scrollbar-thin">
      {groups.map((g) => (
        <div key={g.segment} className="flex flex-col items-end">
          <span className="text-[8px] font-semibold text-stone-400 uppercase tracking-wider mr-1 mb-0.5 mt-1.5 first:mt-0">
            {g.segment}
          </span>
          {g.items.map((p) => {
            const isActive = activeId === p.id;
            const hasImage = !!p.imagenUrl;
            const hasPrompt = !!p.promptImagen;
            const planoNum = (p.numeroPlano || '').split('.').pop() || '?';
            return (
              <button
                key={p.id}
                onClick={() => scrollTo(p.id)}
                title={`${p.numeroPlano} — ${p.segmentoGuion || ''}`}
                className={`
                  flex items-center gap-1 px-1.5 py-0.5 rounded text-[9px] font-medium transition-all
                  ${isActive
                    ? 'bg-indigo-100 text-indigo-700 scale-110'
                    : 'text-stone-400 hover:text-stone-600 hover:bg-stone-100'
                  }
                `}
              >
                <span
                  className={`w-1.5 h-1.5 rounded-full flex-shrink-0 ${
                    hasImage ? 'bg-emerald-400' : hasPrompt ? 'bg-indigo-400' : 'bg-stone-300'
                  }`}
                />
                {planoNum}
              </button>
            );
          })}
        </div>
      ))}
    </nav>
  );
}
