@extends('layouts.app')

@section('title', 'SAM 매뉴얼 영상 생성기')

@push('styles')
<style>
    .step-badge {
        display: inline-flex; align-items: center; justify-content: center;
        width: 2rem; height: 2rem; border-radius: 9999px; font-size: 0.875rem; font-weight: 700;
    }
    .step-active { background: #4f46e5; color: #fff; }
    .step-done { background: #22c55e; color: #fff; }
    .step-pending { background: #e5e7eb; color: #6b7280; }
    .progress-bar { height: 0.75rem; border-radius: 9999px; transition: all 0.5s ease-out; }
    .fade-in { animation: fadeIn 0.3s ease-in; }
    @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
    .upload-zone {
        border: 2px dashed #d1d5db; border-radius: 0.75rem; padding: 2rem;
        text-align: center; cursor: pointer; transition: all 0.2s ease;
    }
    .upload-zone:hover, .upload-zone.dragover { border-color: #818cf8; background: #eef2ff; }
    .upload-zone.paste-flash { border-color: #22c55e; background: #f0fdf4; transition: all 0.15s ease; }
    .upload-zone:focus { outline: 2px solid #818cf8; outline-offset: 2px; }
    .screenshot-list {
        display: flex; flex-wrap: wrap; gap: 0.75rem; min-height: 100px;
        padding: 0.5rem; border: 2px dashed transparent; border-radius: 0.75rem; transition: all 0.2s;
    }
    .screenshot-list.reorder-active { border-color: #818cf8; background: #f5f3ff; }
    .screenshot-thumb {
        position: relative; width: 140px; height: 105px; border-radius: 0.5rem;
        overflow: hidden; border: 2px solid #e5e7eb; cursor: grab; transition: all 0.2s;
        user-select: none; flex-shrink: 0;
    }
    .screenshot-thumb:hover { border-color: #818cf8; box-shadow: 0 2px 8px rgba(79,70,229,0.15); }
    .screenshot-thumb.dragging { opacity: 0.4; transform: scale(0.95); }
    .screenshot-thumb.drag-over { border-color: #4f46e5; border-style: dashed; }
    .screenshot-thumb img { width: 100%; height: 100%; object-fit: cover; pointer-events: none; }
    .screenshot-thumb .remove-btn {
        position: absolute; top: 4px; right: 4px; width: 22px; height: 22px;
        background: rgba(239,68,68,0.9); color: #fff; border: none; border-radius: 9999px;
        font-size: 12px; cursor: pointer; display: flex; align-items: center; justify-content: center;
        opacity: 0; transition: opacity 0.15s;
    }
    .screenshot-thumb:hover .remove-btn { opacity: 1; }
    .screenshot-thumb .order-badge {
        position: absolute; bottom: 4px; left: 4px; min-width: 22px; height: 22px; padding: 0 4px;
        background: rgba(79,70,229,0.9); color: #fff; border-radius: 9999px;
        font-size: 11px; font-weight: 700; display: flex; align-items: center; justify-content: center;
    }
    .screenshot-thumb .drag-handle {
        position: absolute; top: 4px; left: 4px; width: 22px; height: 22px;
        background: rgba(0,0,0,0.4); color: #fff; border-radius: 4px;
        font-size: 10px; display: flex; align-items: center; justify-content: center;
        opacity: 0; transition: opacity 0.15s; cursor: grab;
    }
    .screenshot-thumb:hover .drag-handle { opacity: 1; }
    .screenshot-add-btn {
        width: 140px; height: 105px; border: 2px dashed #d1d5db; border-radius: 0.5rem;
        display: flex; flex-direction: column; align-items: center; justify-content: center;
        cursor: pointer; transition: all 0.2s; color: #9ca3af; flex-shrink: 0;
    }
    .screenshot-add-btn:hover { border-color: #818cf8; color: #6366f1; background: #f5f3ff; }
    .progress-btn {
        position: relative; overflow: hidden; min-width: 280px;
        background: #4338ca !important; color: #fff !important;
        border: none !important; opacity: 1 !important;
    }
    .progress-btn .progress-fill {
        position: absolute; left: 0; top: 0; bottom: 0;
        background: rgba(255,255,255,0.2); transition: width 0.3s ease-out;
        pointer-events: none;
    }
    .progress-btn .btn-content {
        position: relative; z-index: 1; display: flex; align-items: center; justify-content: center; gap: 0.5rem;
    }
    .progress-btn.is-loading {
        background: #312e81 !important; cursor: wait !important;
    }
    .analysis-card {
        background: #fff; border: 1px solid #e5e7eb; border-radius: 0.75rem;
        padding: 1rem; margin-bottom: 0.75rem; box-shadow: 0 1px 2px rgba(0,0,0,.05);
    }
    .ui-element-tag {
        display: inline-flex; align-items: center; gap: 0.25rem;
        padding: 0.125rem 0.5rem; background: #f3f4f6; border-radius: 9999px;
        font-size: 0.75rem; color: #4b5563;
    }
</style>
@endpush

@section('content')
<meta name="csrf-token" content="{{ csrf_token() }}">
<div id="tutorial-root"></div>
@endsection

@push('scripts')
@include('partials.react-cdn')
@verbatim
<script type="text/babel">
const { useState, useEffect, useCallback, useRef } = React;

const CSRF = document.querySelector('meta[name="csrf-token"]')?.content;

const api = (url, options = {}) => {
    const headers = { 'X-CSRF-TOKEN': CSRF, 'Accept': 'application/json', ...options.headers };
    if (!(options.body instanceof FormData)) {
        headers['Content-Type'] = 'application/json';
    }
    return fetch(url, { headers, ...options }).then(async (res) => {
        const text = await res.text();
        let data;
        try {
            data = JSON.parse(text);
        } catch {
            // HTML 에러 페이지 반환 시 (504 Gateway Timeout 등)
            if (res.status === 504) throw new Error('서버 응답 시간 초과 (504). AI 분석에 시간이 오래 걸리고 있습니다. 잠시 후 다시 시도해주세요.');
            if (res.status === 502) throw new Error('서버 연결 오류 (502). 잠시 후 다시 시도해주세요.');
            throw new Error(`서버 오류 (HTTP ${res.status}). 잠시 후 다시 시도해주세요.`);
        }
        if (!res.ok) throw new Error(data.message || `HTTP ${res.status}`);
        return data;
    });
};

// ============================================================
// Step Indicator
// ============================================================
const StepIndicator = ({ currentStep }) => {
    const steps = [
        { num: 1, label: '업로드' },
        { num: 2, label: 'AI 분석' },
        { num: 3, label: '영상 생성' },
    ];
    return (
        <div className="flex items-center justify-center gap-2 mb-8">
            {steps.map((step, i) => (
                <React.Fragment key={step.num}>
                    <div className="flex flex-col items-center">
                        <div className={`step-badge ${currentStep > step.num ? 'step-done' : currentStep === step.num ? 'step-active' : 'step-pending'}`}>
                            {currentStep > step.num ? '\u2713' : step.num}
                        </div>
                        <span className={`text-xs mt-1 ${currentStep >= step.num ? 'text-indigo-600 font-medium' : 'text-gray-400'}`}>
                            {step.label}
                        </span>
                    </div>
                    {i < steps.length - 1 && (
                        <div className={`w-12 h-0.5 mt-[-12px] ${currentStep > step.num ? 'bg-green-500' : 'bg-gray-200'}`} />
                    )}
                </React.Fragment>
            ))}
        </div>
    );
};

// ============================================================
// Step 1: Screenshot Upload
// ============================================================
const ScreenshotUpload = ({ onUploadComplete, onAnalysisComplete, loading, setLoading }) => {
    const [files, setFiles] = useState([]);
    const [previews, setPreviews] = useState([]);
    const [pasteFlash, setPasteFlash] = useState(false);
    const [dragIdx, setDragIdx] = useState(null);
    const [dragOverIdx, setDragOverIdx] = useState(null);
    const [uploadProgress, setUploadProgress] = useState(0); // 0~100
    const [uploadPhase, setUploadPhase] = useState(''); // '', 'uploading', 'analyzing'
    const fileInputRef = useRef(null);
    const filesRef = useRef(files);
    const previewsRef = useRef(previews);

    // ref를 항상 최신 state와 동기화 (stale closure 방지)
    useEffect(() => { filesRef.current = files; }, [files]);
    useEffect(() => { previewsRef.current = previews; }, [previews]);

    // 파일 추가 (stale closure 없음 - ref 사용)
    const addFiles = useCallback((newFiles) => {
        const imageFiles = Array.from(newFiles).filter(f => f.type.startsWith('image/'));
        if (imageFiles.length === 0) return;

        const prev = filesRef.current;
        const remaining = 10 - prev.length;
        if (remaining <= 0) return;

        const toAdd = imageFiles.slice(0, remaining);
        const total = [...prev, ...toAdd];
        setFiles(total);

        // 새 파일들만 preview URL 생성하고 기존 것에 append
        const newUrls = toAdd.map(f => URL.createObjectURL(f));
        setPreviews([...previewsRef.current, ...newUrls]);
    }, []);

    // Ctrl+V 클립보드 붙여넣기
    useEffect(() => {
        const handlePaste = (e) => {
            const items = e.clipboardData?.items;
            if (!items) return;

            const imageFiles = [];
            for (const item of items) {
                if (item.type.startsWith('image/')) {
                    const file = item.getAsFile();
                    if (file) {
                        const ext = item.type.split('/')[1] || 'png';
                        const named = new File([file], `clipboard_${Date.now()}_${imageFiles.length + 1}.${ext}`, { type: item.type });
                        imageFiles.push(named);
                    }
                }
            }

            if (imageFiles.length > 0) {
                e.preventDefault();
                addFiles(imageFiles);
                setPasteFlash(true);
                setTimeout(() => setPasteFlash(false), 600);
            }
        };

        document.addEventListener('paste', handlePaste);
        return () => document.removeEventListener('paste', handlePaste);
    }, [addFiles]);

    const removeFile = (index) => {
        URL.revokeObjectURL(previews[index]);
        setFiles(prev => prev.filter((_, i) => i !== index));
        setPreviews(prev => prev.filter((_, i) => i !== index));
    };

    // 드래그 재정렬
    const handleThumbDragStart = (e, idx) => {
        setDragIdx(idx);
        e.dataTransfer.effectAllowed = 'move';
    };

    const handleThumbDragOver = (e, idx) => {
        e.preventDefault();
        e.dataTransfer.dropEffect = 'move';
        if (idx !== dragOverIdx) setDragOverIdx(idx);
    };

    const handleThumbDrop = (e, toIdx) => {
        e.preventDefault();
        if (dragIdx === null || dragIdx === toIdx) { setDragIdx(null); setDragOverIdx(null); return; }

        setFiles(prev => {
            const arr = [...prev];
            const [moved] = arr.splice(dragIdx, 1);
            arr.splice(toIdx, 0, moved);
            return arr;
        });
        setPreviews(prev => {
            const arr = [...prev];
            const [moved] = arr.splice(dragIdx, 1);
            arr.splice(toIdx, 0, moved);
            return arr;
        });
        setDragIdx(null);
        setDragOverIdx(null);
    };

    // XHR 업로드 (진행률 지원) + AI 분석까지 한번에
    const handleUpload = async () => {
        if (files.length === 0) return;
        setLoading(true);
        setUploadPhase('uploading');
        setUploadProgress(0);

        try {
            // Phase 1: 파일 업로드 (0~50%)
            const formData = new FormData();
            files.forEach(f => formData.append('screenshots[]', f));

            const uploadResult = await new Promise((resolve, reject) => {
                const xhr = new XMLHttpRequest();
                xhr.open('POST', '/video/tutorial/upload');
                xhr.setRequestHeader('X-CSRF-TOKEN', CSRF);
                xhr.setRequestHeader('Accept', 'application/json');

                xhr.upload.onprogress = (e) => {
                    if (e.lengthComputable) {
                        const pct = Math.round((e.loaded / e.total) * 50);
                        setUploadProgress(pct);
                    }
                };

                xhr.onload = () => {
                    if (xhr.status >= 200 && xhr.status < 300) {
                        resolve(JSON.parse(xhr.responseText));
                    } else {
                        try {
                            const err = JSON.parse(xhr.responseText);
                            reject(new Error(err.message || `HTTP ${xhr.status}`));
                        } catch {
                            reject(new Error(`HTTP ${xhr.status}`));
                        }
                    }
                };
                xhr.onerror = () => reject(new Error('네트워크 오류'));
                xhr.send(formData);
            });

            // Phase 2: AI 분석 (50~100%)
            setUploadPhase('analyzing');
            setUploadProgress(55);

            // 분석은 시간이 걸리므로 진행률 시뮬레이션
            const analyzeTimer = setInterval(() => {
                setUploadProgress(prev => prev < 90 ? prev + 3 : prev);
            }, 800);

            const analyzeResult = await api('/video/tutorial/analyze', {
                method: 'POST',
                body: JSON.stringify({ paths: uploadResult.paths }),
            });

            clearInterval(analyzeTimer);
            setUploadProgress(100);

            // 완료 후 잠시 대기 (100% 표시)
            await new Promise(r => setTimeout(r, 400));

            onAnalysisComplete(uploadResult.paths, analyzeResult.analysis);
        } catch (err) {
            alert(err.message);
        } finally {
            setLoading(false);
            setUploadPhase('');
            setUploadProgress(0);
        }
    };

    const isEmpty = files.length === 0;

    return (
        <div className="fade-in max-w-3xl mx-auto">
            <div className="text-center mb-6">
                <h2 className="text-2xl font-bold text-gray-800">SAM 화면 스크린샷 업로드</h2>
                <p className="text-gray-500 mt-2">시나리오 순서대로 스크린샷을 추가하세요. 드래그로 순서 변경 가능합니다.</p>
            </div>

            {isEmpty ? (
                <div
                    className={`upload-zone ${pasteFlash ? 'paste-flash' : ''}`}
                    onClick={() => fileInputRef.current?.click()}
                    onDragOver={(e) => e.preventDefault()}
                    onDrop={(e) => { e.preventDefault(); addFiles(e.dataTransfer.files); }}
                    tabIndex={0}
                >
                    <input ref={fileInputRef} type="file" multiple accept="image/*" className="hidden"
                        onChange={(e) => { addFiles(e.target.files); e.target.value = ''; }} />
                    <svg className="mx-auto h-12 w-12 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                        <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5m-13.5-9L12 3m0 0l4.5 4.5M12 3v13.5" />
                    </svg>
                    <p className="mt-2 text-sm text-gray-600">
                        클릭, 드래그 또는 <kbd className="px-1.5 py-0.5 bg-gray-100 border border-gray-300 rounded text-xs font-mono">Ctrl+V</kbd> 붙여넣기
                    </p>
                    <p className="text-xs text-gray-400 mt-1">PNG, JPG, GIF (최대 10장, 각 10MB) | 화면 캡처 후 바로 붙여넣기 가능</p>
                </div>
            ) : (
                <div>
                    <div className="flex items-center justify-between mb-2">
                        <div className="text-sm font-medium text-gray-700">
                            스크린샷 순서 ({files.length}/10)
                            {files.length >= 2 && <span className="text-xs text-gray-400 ml-2">드래그로 순서 변경</span>}
                        </div>
                        <div className={`text-xs px-2 py-1 rounded-full transition-all ${pasteFlash ? 'bg-green-100 text-green-700' : 'bg-gray-100 text-gray-500'}`}>
                            <kbd className="font-mono">Ctrl+V</kbd> 로 추가
                        </div>
                    </div>

                    <div className={`screenshot-list ${dragIdx !== null ? 'reorder-active' : ''}`}
                        onDragOver={(e) => e.preventDefault()}
                        onDrop={(e) => { e.preventDefault(); addFiles(e.dataTransfer.files); }}
                    >
                        {previews.map((src, i) => (
                            <div
                                key={`${i}-${files[i]?.name}`}
                                className={`screenshot-thumb ${dragIdx === i ? 'dragging' : ''} ${dragOverIdx === i ? 'drag-over' : ''}`}
                                draggable
                                onDragStart={(e) => handleThumbDragStart(e, i)}
                                onDragOver={(e) => handleThumbDragOver(e, i)}
                                onDrop={(e) => { e.stopPropagation(); handleThumbDrop(e, i); }}
                                onDragEnd={() => { setDragIdx(null); setDragOverIdx(null); }}
                            >
                                <img src={src} alt={`Screenshot ${i+1}`} />
                                <div className="drag-handle" title="드래그로 순서 변경">&#9776;</div>
                                <button className="remove-btn" onClick={(e) => { e.stopPropagation(); removeFile(i); }} title="삭제">
                                    &times;
                                </button>
                                <div className="order-badge">{i + 1}</div>
                            </div>
                        ))}

                        {files.length < 10 && (
                            <div className="screenshot-add-btn" onClick={() => fileInputRef.current?.click()} title="스크린샷 추가">
                                <svg className="w-8 h-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                                    <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M12 4.5v15m7.5-7.5h-15" />
                                </svg>
                                <span className="text-xs mt-1">추가</span>
                            </div>
                        )}
                    </div>

                    <input ref={fileInputRef} type="file" multiple accept="image/*" className="hidden"
                        onChange={(e) => { addFiles(e.target.files); e.target.value = ''; }} />
                </div>
            )}

            {files.length > 0 && (
                <div className="mt-6 text-center">
                    <button
                        onClick={() => { if (!loading) handleUpload(); }}
                        className={`progress-btn px-6 py-3 rounded-lg font-medium ${loading ? 'is-loading' : 'hover:opacity-90'}`}
                    >
                        {loading && <div className="progress-fill" style={{ width: `${uploadProgress}%` }} />}
                        <span className="btn-content">
                            {loading ? (
                                <>
                                    <svg className="animate-spin h-4 w-4 flex-shrink-0" viewBox="0 0 24 24"><circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" fill="none"/><path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"/></svg>
                                    <span>
                                        {uploadPhase === 'uploading' && `업로드 중... ${uploadProgress}%`}
                                        {uploadPhase === 'analyzing' && `AI 분석 중... ${uploadProgress}%`}
                                    </span>
                                </>
                            ) : (
                                `${files.length}장 업로드 & AI 분석 시작`
                            )}
                        </span>
                    </button>
                </div>
            )}
        </div>
    );
};

// ============================================================
// Step 2: Analysis Review
// ============================================================
const AnalysisReview = ({ analysis, paths, onConfirm, onBack, loading, setLoading }) => {
    const [editedAnalysis, setEditedAnalysis] = useState(analysis);
    const [title, setTitle] = useState('SAM 사용자 매뉴얼');

    const updateStepNarration = (screenIndex, stepIndex, newNarration) => {
        const updated = [...editedAnalysis];
        const steps = [...(updated[screenIndex].steps || [])];
        steps[stepIndex] = { ...steps[stepIndex], narration: newNarration };
        updated[screenIndex] = { ...updated[screenIndex], steps };
        setEditedAnalysis(updated);
    };

    const handleGenerate = () => {
        onConfirm(editedAnalysis, title);
    };

    // 전체 예상 시간 계산
    const totalDuration = editedAnalysis.reduce((sum, screen) => {
        const screenDur = (screen.steps || []).reduce((s, step) => s + (step.duration || 6), 0);
        return sum + screenDur;
    }, 6); // +6 for intro+outro

    const totalSteps = editedAnalysis.reduce((sum, screen) => sum + (screen.steps || []).length, 0);

    return (
        <div className="fade-in max-w-3xl mx-auto">
            <div className="text-center mb-6">
                <h2 className="text-2xl font-bold text-gray-800">AI 분석 결과 확인</h2>
                <p className="text-gray-500 mt-2">단계별 나레이션을 편집하고 영상을 생성하세요</p>
                <div className="mt-2 inline-flex items-center gap-3 px-4 py-1.5 bg-indigo-50 rounded-full text-sm">
                    <span className="text-indigo-700 font-medium">총 {totalSteps}단계</span>
                    <span className="text-gray-300">|</span>
                    <span className="text-indigo-600">예상 {totalDuration}초 ({Math.floor(totalDuration/60)}분 {totalDuration%60}초)</span>
                </div>
            </div>

            <div className="mb-4">
                <label className="block text-sm font-medium text-gray-700 mb-1">영상 제목</label>
                <input
                    type="text"
                    value={title}
                    onChange={(e) => setTitle(e.target.value)}
                    className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"
                    placeholder="영상 제목을 입력하세요"
                />
            </div>

            {editedAnalysis.map((screen, i) => {
                const steps = screen.steps || [];
                const screenDuration = steps.reduce((s, step) => s + (step.duration || 6), 0);

                return (
                    <div key={i} className="analysis-card">
                        <div className="flex items-center justify-between mb-3">
                            <div className="flex items-center gap-3">
                                <span className="step-badge step-active">{screen.screen_number || i + 1}</span>
                                <div>
                                    <div className="font-medium text-gray-800">{screen.title}</div>
                                    <div className="text-xs text-gray-400">
                                        {steps.length}단계 &middot; 예상 {screenDuration}초
                                    </div>
                                </div>
                            </div>
                        </div>

                        <div className="space-y-3 ml-2 border-l-2 border-indigo-100 pl-4">
                            {steps.map((step, j) => (
                                <div key={j} className="relative">
                                    <div className="flex items-center gap-2 mb-1">
                                        <span className="inline-flex items-center justify-center w-5 h-5 rounded-full bg-indigo-100 text-indigo-700 text-xs font-bold">
                                            {step.step_number || j + 1}
                                        </span>
                                        <span className="text-xs text-gray-500 font-medium">
                                            {step.focused_element?.label || `단계 ${j + 1}`}
                                        </span>
                                        <span className="text-xs text-gray-300 ml-auto">{step.duration || 6}초</span>
                                    </div>
                                    <textarea
                                        value={step.narration || ''}
                                        onChange={(e) => updateStepNarration(i, j, e.target.value)}
                                        rows={2}
                                        className="w-full px-3 py-2 border border-gray-200 rounded-lg text-sm focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 resize-none"
                                    />
                                    <div className="text-xs text-gray-400 mt-0.5 text-right">{(step.narration || '').length}자</div>
                                </div>
                            ))}
                        </div>
                    </div>
                );
            })}

            <div className="flex gap-3 mt-6 justify-center">
                <button
                    onClick={onBack}
                    className="px-5 py-2.5 border border-gray-300 text-gray-700 rounded-lg font-medium hover:bg-gray-50 transition-colors"
                >
                    이전
                </button>
                <button
                    onClick={handleGenerate}
                    disabled={loading}
                    className="px-6 py-2.5 bg-indigo-600 text-white rounded-lg font-medium hover:bg-indigo-700 disabled:opacity-50 transition-colors"
                >
                    {loading ? '생성 시작 중...' : `영상 생성 시작 (${totalSteps}단계, ~${totalDuration}초)`}
                </button>
            </div>
        </div>
    );
};

// ============================================================
// Step 3: Generation Progress
// ============================================================
const GenerationProgress = ({ tutorialId, onComplete, onReset }) => {
    const [status, setStatus] = useState(null);
    const [polling, setPolling] = useState(true);
    const [videoSrc, setVideoSrc] = useState(null);
    const intervalRef = useRef(null);

    useEffect(() => {
        if (!tutorialId || !polling) return;

        const poll = async () => {
            try {
                const data = await api(`/video/tutorial/status/${tutorialId}`);
                setStatus(data);
                if (data.status === 'completed' || data.status === 'failed') {
                    setPolling(false);
                    if (data.status === 'completed') {
                        // GCS 서명URL 가져오기 (CORS 회피), 실패 시 로컬 스트리밍 fallback
                        try {
                            const urlData = await api(`/video/tutorial/preview/${tutorialId}?url=1`);
                            setVideoSrc(urlData.url);
                        } catch {
                            setVideoSrc(`/video/tutorial/preview/${tutorialId}`);
                        }
                        onComplete?.();
                    }
                }
            } catch (err) {
                console.error('Polling error:', err);
            }
        };

        poll();
        intervalRef.current = setInterval(poll, 2000);
        return () => clearInterval(intervalRef.current);
    }, [tutorialId, polling]);

    const isCompleted = status?.status === 'completed';
    const isFailed = status?.status === 'failed';
    const progress = status?.progress || 0;

    return (
        <div className="fade-in max-w-lg mx-auto text-center">
            {!isCompleted && !isFailed && (
                <>
                    <div className="mb-4">
                        <svg className="animate-spin h-8 w-8 text-indigo-600 mx-auto" viewBox="0 0 24 24">
                            <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" fill="none"/>
                            <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"/>
                        </svg>
                    </div>
                    <h3 className="text-lg font-semibold text-gray-800 mb-2">영상 생성 중...</h3>
                    <p className="text-sm text-gray-500 mb-4">{status?.current_step || '대기 중...'}</p>
                </>
            )}

            <div className="w-full bg-gray-200 rounded-full mb-2">
                <div
                    className={`progress-bar ${isCompleted ? 'bg-green-500' : isFailed ? 'bg-red-500' : 'bg-indigo-600'}`}
                    style={{ width: `${progress}%` }}
                />
            </div>
            <div className="text-sm text-gray-500 mb-6">{progress}%</div>

            {isCompleted && videoSrc && (
                <div className="fade-in">
                    <div className="text-green-600 text-lg font-bold mb-4">영상 생성 완료!</div>

                    <div className="mb-6 rounded-xl overflow-hidden border border-gray-200 shadow-sm">
                        <video
                            controls
                            className="w-full"
                            src={videoSrc}
                        />
                    </div>

                    <div className="flex gap-3 justify-center">
                        <a
                            href={`/video/tutorial/download/${tutorialId}`}
                            className="px-5 py-2.5 bg-indigo-600 text-white rounded-lg font-medium hover:bg-indigo-700 transition-colors inline-flex items-center gap-2"
                        >
                            <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"/></svg>
                            다운로드
                        </a>
                        <button
                            onClick={onReset}
                            className="px-5 py-2.5 border border-gray-300 text-gray-700 rounded-lg font-medium hover:bg-gray-50 transition-colors"
                        >
                            새로 만들기
                        </button>
                    </div>

                    {status?.cost_usd > 0 && (
                        <div className="mt-3 text-xs text-gray-400">예상 비용: ${Number(status.cost_usd).toFixed(4)}</div>
                    )}
                </div>
            )}

            {isFailed && (
                <div className="fade-in">
                    <div className="text-red-600 text-lg font-bold mb-2">생성 실패</div>
                    <p className="text-sm text-red-500 mb-4">{status?.error_message || '알 수 없는 오류'}</p>
                    <button
                        onClick={onReset}
                        className="px-5 py-2.5 bg-red-600 text-white rounded-lg font-medium hover:bg-red-700 transition-colors"
                    >
                        다시 시도
                    </button>
                </div>
            )}
        </div>
    );
};

// ============================================================
// History Detail Panel (행 클릭 시 표시)
// ============================================================
const HistoryDetail = ({ detail, onClose }) => {
    if (!detail) return null;

    const analysis = detail.analysis_data || [];
    const totalSteps = analysis.reduce((sum, s) => sum + (s.steps || []).length, 0);
    const totalDuration = analysis.reduce((sum, s) =>
        sum + (s.steps || []).reduce((d, step) => d + (step.duration || 6), 0), 6
    );

    return (
        <tr>
            <td colSpan="8" className="px-0 py-0">
                <div className="bg-indigo-50 border-t border-b border-indigo-200 px-4 py-4">
                    <div className="flex items-center justify-between mb-3">
                        <div className="flex items-center gap-3">
                            <h4 className="font-bold text-gray-800">{detail.title || '제목 없음'}</h4>
                            <span className="text-xs px-2 py-0.5 bg-indigo-100 text-indigo-700 rounded-full">
                                {totalSteps}단계 / 예상 {totalDuration}초
                            </span>
                        </div>
                        <button onClick={onClose} className="text-gray-400 hover:text-gray-600 text-lg leading-none">&times;</button>
                    </div>

                    {analysis.length === 0 ? (
                        <p className="text-sm text-gray-500">분석 데이터가 없습니다.</p>
                    ) : (
                        <div className="space-y-3 max-h-96 overflow-y-auto">
                            {analysis.map((screen, i) => {
                                const steps = screen.steps || [];
                                return (
                                    <div key={i} className="bg-white rounded-lg border border-gray-200 p-3">
                                        <div className="flex items-center gap-2 mb-2">
                                            <span className="inline-flex items-center justify-center w-6 h-6 rounded-full bg-indigo-600 text-white text-xs font-bold">
                                                {screen.screen_number || i + 1}
                                            </span>
                                            <span className="font-medium text-gray-800 text-sm">{screen.title || `화면 ${i + 1}`}</span>
                                            <span className="text-xs text-gray-400">{steps.length}단계</span>
                                        </div>
                                        <div className="space-y-1.5 ml-3 border-l-2 border-indigo-100 pl-3">
                                            {steps.map((step, j) => (
                                                <div key={j} className="flex items-start gap-2">
                                                    <span className="inline-flex items-center justify-center w-4 h-4 rounded-full bg-indigo-100 text-indigo-700 text-[10px] font-bold mt-0.5 flex-shrink-0">
                                                        {step.step_number || j + 1}
                                                    </span>
                                                    <div className="flex-1 min-w-0">
                                                        <div className="flex items-center gap-2">
                                                            <span className="text-xs font-medium text-gray-600">
                                                                {step.focused_element?.label || `단계 ${j + 1}`}
                                                            </span>
                                                            <span className="text-[10px] text-gray-400">{step.duration || 6}초</span>
                                                        </div>
                                                        <p className="text-xs text-gray-500 mt-0.5 leading-relaxed">{step.narration || '-'}</p>
                                                    </div>
                                                </div>
                                            ))}
                                        </div>
                                    </div>
                                );
                            })}
                        </div>
                    )}
                </div>
            </td>
        </tr>
    );
};

// ============================================================
// History Table
// ============================================================
const HistoryTable = ({ refreshKey }) => {
    const [items, setItems] = useState([]);
    const [selected, setSelected] = useState([]);
    const [loading, setLoading] = useState(false);
    const [refreshing, setRefreshing] = useState(false);
    const [expandedId, setExpandedId] = useState(null);
    const [detailData, setDetailData] = useState({});
    const [detailLoading, setDetailLoading] = useState(null);

    const fetchHistory = async (showSpinner) => {
        if (showSpinner) setRefreshing(true);
        try {
            const data = await api('/video/tutorial/history');
            setItems(data.data || []);
        } catch (err) {
            console.error(err);
        } finally {
            if (showSpinner) setRefreshing(false);
        }
    };

    useEffect(() => { fetchHistory(false); }, [refreshKey]);

    const handleRefresh = () => { fetchHistory(true); };

    const toggleSelect = (id) => {
        setSelected(prev => prev.includes(id) ? prev.filter(x => x !== id) : [...prev, id]);
    };

    const toggleAll = () => {
        setSelected(selected.length === items.length ? [] : items.map(i => i.id));
    };

    const deleteSelected = async () => {
        if (selected.length === 0 || !confirm(`${selected.length}개 항목을 삭제하시겠습니까?`)) return;
        setLoading(true);
        try {
            await api('/video/tutorial/history', {
                method: 'DELETE',
                body: JSON.stringify({ ids: selected }),
            });
            setSelected([]);
            fetchHistory(false);
        } catch (err) {
            alert(err.message);
        } finally {
            setLoading(false);
        }
    };

    const toggleDetail = async (id) => {
        if (expandedId === id) {
            setExpandedId(null);
            return;
        }

        setExpandedId(id);

        if (!detailData[id]) {
            setDetailLoading(id);
            try {
                const data = await api(`/video/tutorial/detail/${id}`);
                setDetailData(prev => ({ ...prev, [id]: data.data }));
            } catch (err) {
                console.error('상세 조회 실패:', err);
            } finally {
                setDetailLoading(null);
            }
        }
    };

    const statusLabel = (status) => {
        const map = {
            pending: { label: '대기', cls: 'bg-gray-100 text-gray-600' },
            analyzing: { label: '분석중', cls: 'bg-blue-100 text-blue-700' },
            generating_slides: { label: '슬라이드', cls: 'bg-blue-100 text-blue-700' },
            generating_tts: { label: 'TTS', cls: 'bg-blue-100 text-blue-700' },
            generating_bgm: { label: 'BGM', cls: 'bg-blue-100 text-blue-700' },
            assembling: { label: '합성중', cls: 'bg-yellow-100 text-yellow-700' },
            completed: { label: '완료', cls: 'bg-green-100 text-green-700' },
            failed: { label: '실패', cls: 'bg-red-100 text-red-700' },
        };
        const s = map[status] || { label: status, cls: 'bg-gray-100 text-gray-600' };
        return <span className={`px-2 py-0.5 rounded-full text-xs font-medium ${s.cls}`}>{s.label}</span>;
    };

    if (items.length === 0) return null;

    return (
        <div className="mt-10 max-w-4xl mx-auto">
            <div className="flex items-center justify-between mb-3">
                <div className="flex items-center gap-2">
                    <h3 className="text-lg font-bold text-gray-800">생성 이력</h3>
                    <button
                        onClick={handleRefresh}
                        disabled={refreshing}
                        className="p-1.5 text-gray-400 hover:text-indigo-600 hover:bg-indigo-50 rounded-lg transition-colors disabled:opacity-50"
                        title="새로고침"
                    >
                        <svg className={`w-4 h-4 ${refreshing ? 'animate-spin' : ''}`} fill="none" viewBox="0 0 24 24" stroke="currentColor">
                            <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
                        </svg>
                    </button>
                </div>
                {selected.length > 0 && (
                    <button
                        onClick={deleteSelected}
                        disabled={loading}
                        className="px-3 py-1.5 bg-red-600 text-white text-sm rounded-lg hover:bg-red-700 disabled:opacity-50"
                    >
                        {selected.length}개 삭제
                    </button>
                )}
            </div>

            <div className="bg-white rounded-xl border border-gray-200 overflow-hidden shadow-sm">
                <table className="w-full text-sm">
                    <thead className="bg-gray-50 text-gray-600">
                        <tr>
                            <th className="px-3 py-2.5 text-left w-8">
                                <input type="checkbox" checked={selected.length === items.length && items.length > 0} onChange={toggleAll} className="rounded" />
                            </th>
                            <th className="px-3 py-2.5 text-left">제목</th>
                            <th className="px-3 py-2.5 text-center">상태</th>
                            <th className="px-3 py-2.5 text-center">진행</th>
                            <th className="px-3 py-2.5 text-center">비용</th>
                            <th className="px-3 py-2.5 text-center">제작시간</th>
                            <th className="px-3 py-2.5 text-center">생성일</th>
                            <th className="px-3 py-2.5 text-center">액션</th>
                        </tr>
                    </thead>
                    <tbody className="divide-y divide-gray-100">
                        {items.map(item => (
                            <React.Fragment key={item.id}>
                                <tr
                                    className={`hover:bg-gray-50 cursor-pointer transition-colors ${expandedId === item.id ? 'bg-indigo-50' : ''}`}
                                    onClick={(e) => {
                                        if (e.target.tagName === 'INPUT' || e.target.tagName === 'A' || e.target.closest('a')) return;
                                        toggleDetail(item.id);
                                    }}
                                >
                                    <td className="px-3 py-2">
                                        <input type="checkbox" checked={selected.includes(item.id)} onChange={() => toggleSelect(item.id)} className="rounded" />
                                    </td>
                                    <td className="px-3 py-2 text-gray-800 font-medium">
                                        <div className="flex items-center gap-1.5">
                                            <svg className={`w-3.5 h-3.5 text-gray-400 transition-transform flex-shrink-0 ${expandedId === item.id ? 'rotate-90' : ''}`} fill="none" viewBox="0 0 24 24" stroke="currentColor">
                                                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
                                            </svg>
                                            {item.title || `#${item.id}`}
                                        </div>
                                    </td>
                                    <td className="px-3 py-2 text-center">{statusLabel(item.status)}</td>
                                    <td className="px-3 py-2 text-center text-gray-500">{item.progress}%</td>
                                    <td className="px-3 py-2 text-center text-gray-500">${Number(item.cost_usd || 0).toFixed(3)}</td>
                                    <td className="px-3 py-2 text-center text-gray-400 text-xs">
                                        {(() => {
                                            if (!item.created_at || !item.updated_at || item.status === 'pending') return '-';
                                            const sec = Math.round((new Date(item.updated_at) - new Date(item.created_at)) / 1000);
                                            if (sec < 60) return `${sec}초`;
                                            const m = Math.floor(sec / 60), s = sec % 60;
                                            if (m < 60) return s > 0 ? `${m}분 ${s}초` : `${m}분`;
                                            const h = Math.floor(m / 60), rm = m % 60;
                                            return rm > 0 ? `${h}시간 ${rm}분` : `${h}시간`;
                                        })()}
                                    </td>
                                    <td className="px-3 py-2 text-center text-gray-400 text-xs">
                                        {item.created_at ? new Date(item.created_at).toLocaleDateString('ko-KR', { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }) : '-'}
                                    </td>
                                    <td className="px-3 py-2 text-center">
                                        {item.status === 'completed' && (
                                            <div className="flex gap-1 justify-center">
                                                <a href={`/video/tutorial/preview/${item.id}`} target="_blank" className="text-indigo-600 hover:text-indigo-800 text-xs font-medium">보기</a>
                                                <span className="text-gray-300">|</span>
                                                <a href={`/video/tutorial/download/${item.id}`} className="text-indigo-600 hover:text-indigo-800 text-xs font-medium">저장</a>
                                            </div>
                                        )}
                                    </td>
                                </tr>
                                {expandedId === item.id && (
                                    detailLoading === item.id ? (
                                        <tr><td colSpan="8" className="px-4 py-6 text-center text-gray-500 text-sm">
                                            <svg className="animate-spin h-5 w-5 text-indigo-600 mx-auto mb-2" viewBox="0 0 24 24"><circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" fill="none"/><path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"/></svg>
                                            스크립트 불러오는 중...
                                        </td></tr>
                                    ) : (
                                        <HistoryDetail
                                            detail={detailData[item.id]}
                                            onClose={() => setExpandedId(null)}
                                        />
                                    )
                                )}
                            </React.Fragment>
                        ))}
                    </tbody>
                </table>
            </div>
        </div>
    );
};

// ============================================================
// Main App
// ============================================================
const TutorialApp = () => {
    const [step, setStep] = useState(1);
    const [loading, setLoading] = useState(false);
    const [paths, setPaths] = useState([]);
    const [analysis, setAnalysis] = useState(null);
    const [tutorialId, setTutorialId] = useState(null);
    const [refreshKey, setRefreshKey] = useState(0);

    const handleAnalysisComplete = (uploadedPaths, analysisData) => {
        setPaths(uploadedPaths);
        setAnalysis(analysisData);
        setStep(2);
    };

    const handleGenerate = async (editedAnalysis, title) => {
        setLoading(true);
        try {
            const data = await api('/video/tutorial/generate', {
                method: 'POST',
                body: JSON.stringify({ paths, analysis: editedAnalysis, title }),
            });
            setTutorialId(data.id);
            setStep(3);
        } catch (err) {
            alert('생성 시작 실패: ' + err.message);
        } finally {
            setLoading(false);
        }
    };

    const handleReset = () => {
        setStep(1);
        setPaths([]);
        setAnalysis(null);
        setTutorialId(null);
        setRefreshKey(k => k + 1);
    };

    return (
        <div className="max-w-5xl mx-auto py-6 px-4">
            <div className="text-center mb-8">
                <h1 className="text-3xl font-bold text-gray-900">SAM 매뉴얼 영상 생성기</h1>
                <p className="text-gray-500 mt-1">스크린샷을 업로드하면 AI가 자동으로 튜토리얼 영상을 만들어 드립니다</p>
            </div>

            <StepIndicator currentStep={step} />

            <div className="bg-white rounded-2xl shadow-sm border border-gray-200 p-6 mb-8">
                {step === 1 && (
                    <ScreenshotUpload
                        onAnalysisComplete={handleAnalysisComplete}
                        loading={loading}
                        setLoading={setLoading}
                    />
                )}
                {step === 2 && analysis && (
                    <AnalysisReview
                        analysis={analysis}
                        paths={paths}
                        onConfirm={handleGenerate}
                        onBack={() => setStep(1)}
                        loading={loading}
                        setLoading={setLoading}
                    />
                )}
                {step === 3 && tutorialId && (
                    <GenerationProgress
                        tutorialId={tutorialId}
                        onComplete={() => setRefreshKey(k => k + 1)}
                        onReset={handleReset}
                    />
                )}
            </div>

            <HistoryTable refreshKey={refreshKey} />
        </div>
    );
};

const root = ReactDOM.createRoot(document.getElementById('tutorial-root'));
root.render(<TutorialApp />);
</script>
@endverbatim
@endpush
