@extends('layouts.app')

@section('title', 'SAM E-Sign - 템플릿 관리')

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

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

const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content || '';
const getHeaders = () => ({
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]')?.content || csrfToken,
});

const CATEGORIES = ['영업파트너', '고객'];

// ─── TemplateToast ───
const TemplateToast = ({ message, type = 'success', onClose }) => {
    useEffect(() => {
        const t = setTimeout(onClose, 3000);
        return () => clearTimeout(t);
    }, []);

    const bg = type === 'success' ? 'bg-green-500' : type === 'error' ? 'bg-red-500' : 'bg-blue-500';
    return (
        <div className={`fixed top-4 right-4 z-50 ${bg} text-white px-4 py-2.5 rounded-lg shadow-lg text-sm flex items-center gap-2 animate-slide-in`}>
            <span>{message}</span>
            <button onClick={onClose} className="text-white/80 hover:text-white ml-1">&times;</button>
        </div>
    );
};

const FIELD_TYPE_MAP = {
    signature: { label: '서명', color: 'bg-blue-100 text-blue-700' },
    stamp: { label: '도장', color: 'bg-purple-100 text-purple-700' },
    text: { label: '텍스트', color: 'bg-gray-100 text-gray-700' },
    date: { label: '날짜', color: 'bg-green-100 text-green-700' },
    checkbox: { label: '체크박스', color: 'bg-yellow-100 text-yellow-700' },
};

// ─── EditTemplateModal ───
const EditTemplateModal = ({ open, template, onClose, onUpdate }) => {
    const [tab, setTab] = useState('info');
    const [name, setName] = useState('');
    const [description, setDescription] = useState('');
    const [category, setCategory] = useState('');
    const [saving, setSaving] = useState(false);
    const [tpl, setTpl] = useState(null); // 로컬 상태 (PDF/필드 변경 반영)
    const [uploading, setUploading] = useState(false);
    const fileInputRef = useRef(null);

    useEffect(() => {
        if (template) {
            setName(template.name || '');
            setDescription(template.description || '');
            setCategory(template.category || '');
            setTpl(template);
            setTab('info');
        }
    }, [template]);

    if (!open || !tpl) return null;

    const handleSave = async () => {
        if (!name.trim()) { alert('템플릿 이름을 입력해주세요.'); return; }
        setSaving(true);
        try {
            const res = await fetch(`/esign/contracts/templates/${tpl.id}`, {
                method: 'PUT', headers: { ...getHeaders(), 'Content-Type': 'application/json' },
                body: JSON.stringify({ name: name.trim(), description: description.trim(), category: category || null }),
            });
            const json = await res.json();
            if (json.success) { onUpdate(json.data); onClose(); }
            else alert(json.message || '수정 실패');
        } catch (_) { alert('서버 오류'); }
        setSaving(false);
    };

    const handleUploadPdf = async (e) => {
        const file = e.target.files[0];
        if (!file) return;
        setUploading(true);
        try {
            const fd = new FormData();
            fd.append('file', file);
            const res = await fetch(`/esign/contracts/templates/${tpl.id}/upload-pdf`, {
                method: 'POST',
                headers: { 'Accept': 'application/json', 'X-CSRF-TOKEN': csrfToken },
                body: fd,
            });
            const json = await res.json();
            if (json.success) { setTpl(json.data); onUpdate(json.data); }
            else alert(json.message || '업로드 실패');
        } catch (_) { alert('서버 오류'); }
        setUploading(false);
        if (fileInputRef.current) fileInputRef.current.value = '';
    };

    const handleRemovePdf = async () => {
        if (!confirm('PDF 파일을 제거하시겠습니까?')) return;
        try {
            const res = await fetch(`/esign/contracts/templates/${tpl.id}/remove-pdf`, {
                method: 'DELETE', headers: getHeaders(),
            });
            const json = await res.json();
            if (json.success) { setTpl(json.data); onUpdate(json.data); }
            else alert(json.message || '제거 실패');
        } catch (_) { alert('서버 오류'); }
    };

    const handleDeleteItem = async (itemId) => {
        if (!confirm('이 필드를 삭제하시겠습니까?')) return;
        try {
            const res = await fetch(`/esign/contracts/templates/${tpl.id}/items/${itemId}`, {
                method: 'DELETE', headers: getHeaders(),
            });
            const json = await res.json();
            if (json.success) { setTpl(json.data); onUpdate(json.data); }
            else alert(json.message || '삭제 실패');
        } catch (_) { alert('서버 오류'); }
    };

    const tabs = [
        { key: 'info', label: '기본 정보' },
        { key: 'pdf', label: 'PDF 파일' },
        { key: 'fields', label: `서식 필드 (${tpl.items?.length || 0})` },
    ];

    return (
        <div className="fixed inset-0 bg-black/40 flex items-center justify-center z-50" onClick={onClose}>
            <div className="bg-white rounded-xl shadow-2xl w-[680px] max-h-[85vh] flex flex-col" onClick={e => e.stopPropagation()}>
                {/* 헤더 + 탭 */}
                <div className="px-6 pt-5 pb-0 flex-shrink-0">
                    <div className="flex items-center justify-between mb-4">
                        <h3 className="text-base font-semibold">템플릿 편집</h3>
                        <button onClick={onClose} className="text-gray-400 hover:text-gray-600 text-lg leading-none">&times;</button>
                    </div>
                    <div className="flex gap-1 border-b -mx-6 px-6">
                        {tabs.map(t => (
                            <button key={t.key} onClick={() => setTab(t.key)}
                                    className={`px-3 py-2 text-sm font-medium border-b-2 -mb-px transition-colors ${tab === t.key ? 'border-blue-600 text-blue-600' : 'border-transparent text-gray-500 hover:text-gray-700'}`}>
                                {t.label}
                            </button>
                        ))}
                    </div>
                </div>

                {/* 탭 콘텐츠 */}
                <div className="px-6 py-5 flex-1 overflow-y-auto">
                    {/* 기본 정보 탭 */}
                    {tab === 'info' && (
                        <div className="space-y-3">
                            <div>
                                <label className="text-xs text-gray-500 block mb-1">템플릿 이름 *</label>
                                <input type="text" value={name} onChange={e => setName(e.target.value)}
                                       className="w-full border rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-blue-200 focus:border-blue-400 outline-none" autoFocus />
                            </div>
                            <div>
                                <label className="text-xs text-gray-500 block mb-1">설명</label>
                                <textarea value={description} onChange={e => setDescription(e.target.value)}
                                          rows={3} className="w-full border rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-blue-200 focus:border-blue-400 outline-none resize-none" />
                            </div>
                            <div>
                                <label className="text-xs text-gray-500 block mb-1">카테고리</label>
                                <select value={category} onChange={e => setCategory(e.target.value)}
                                        className="w-full border rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-blue-200 focus:border-blue-400 outline-none">
                                    <option value="">카테고리 없음</option>
                                    {CATEGORIES.map(c => <option key={c} value={c}>{c}</option>)}
                                </select>
                            </div>
                            <div className="grid grid-cols-2 gap-3 pt-1">
                                <div className="bg-gray-50 rounded-lg p-3">
                                    <p className="text-[11px] text-gray-400 mb-0.5">서명자 수</p>
                                    <p className="text-sm font-medium">{tpl.signer_count}명</p>
                                </div>
                                <div className="bg-gray-50 rounded-lg p-3">
                                    <p className="text-[11px] text-gray-400 mb-0.5">필드 수</p>
                                    <p className="text-sm font-medium">{tpl.items_count ?? tpl.items?.length ?? 0}개</p>
                                </div>
                            </div>
                        </div>
                    )}

                    {/* PDF 파일 탭 */}
                    {tab === 'pdf' && (
                        <div className="space-y-4">
                            {tpl.file_path ? (
                                <div className="border rounded-lg p-4">
                                    <div className="flex items-center gap-3">
                                        <div className="w-10 h-10 bg-red-50 rounded-lg flex items-center justify-center flex-shrink-0">
                                            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#EF4444" strokeWidth="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
                                        </div>
                                        <div className="flex-1 min-w-0">
                                            <p className="text-sm font-medium text-gray-900 truncate">{tpl.file_name || 'PDF 파일'}</p>
                                            <p className="text-xs text-gray-400">{tpl.file_size ? `${(tpl.file_size / 1024 / 1024).toFixed(2)} MB` : ''}</p>
                                        </div>
                                        <a href={`/esign/contracts/templates/${tpl.id}/download`} target="_blank"
                                           className="px-3 py-1.5 text-xs border rounded-lg text-gray-600 hover:bg-gray-50">다운로드</a>
                                    </div>
                                    <div className="flex gap-2 mt-3 pt-3 border-t">
                                        <button onClick={() => fileInputRef.current?.click()} disabled={uploading}
                                                className="px-3 py-1.5 text-xs bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50">
                                            {uploading ? '업로드 중...' : 'PDF 교체'}
                                        </button>
                                        <button onClick={handleRemovePdf}
                                                className="px-3 py-1.5 text-xs border border-red-300 text-red-600 rounded-lg hover:bg-red-50">
                                            PDF 제거
                                        </button>
                                    </div>
                                </div>
                            ) : (
                                <div className="border-2 border-dashed rounded-lg p-8 text-center">
                                    <svg className="mx-auto mb-3 text-gray-300" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
                                        <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="12" y1="18" x2="12" y2="12"/><line x1="9" y1="15" x2="15" y2="15"/>
                                    </svg>
                                    <p className="text-sm text-gray-500 mb-3">연결된 PDF 파일이 없습니다</p>
                                    <button onClick={() => fileInputRef.current?.click()} disabled={uploading}
                                            className="px-4 py-2 text-sm bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50">
                                        {uploading ? '업로드 중...' : 'PDF 업로드'}
                                    </button>
                                </div>
                            )}
                            <input ref={fileInputRef} type="file" accept=".pdf,.doc,.docx" onChange={handleUploadPdf} className="hidden" />
                            <p className="text-xs text-gray-400">PDF 파일은 이 템플릿으로 계약 생성 시 자동 사용됩니다. 최대 20MB.</p>
                        </div>
                    )}

                    {/* 서식 필드 탭 */}
                    {tab === 'fields' && (
                        <div>
                            {/* 비주얼 편집 버튼 */}
                            {tpl.file_path && (
                                <div className="mb-4 p-3 bg-teal-50 rounded-lg flex items-center justify-between">
                                    <div>
                                        <p className="text-sm font-medium text-teal-800">PDF에서 시각적으로 필드를 편집할 수 있습니다</p>
                                        <p className="text-xs text-teal-600 mt-0.5">드래그 앤 드롭으로 서명 위치를 설정하세요</p>
                                    </div>
                                    <a href={`/esign/templates/${tpl.id}/fields`}
                                       className="px-4 py-2 bg-teal-600 text-white rounded-lg hover:bg-teal-700 text-sm font-medium flex-shrink-0 transition-colors"
                                       onClick={e => e.stopPropagation()}>
                                        비주얼 편집
                                    </a>
                                </div>
                            )}
                            {(!tpl.items || tpl.items.length === 0) ? (
                                <div className="text-center py-8 text-gray-400">
                                    <p className="text-sm mb-1">서식 필드가 없습니다.</p>
                                    <p className="text-xs">{tpl.file_path ? '위의 "비주얼 편집" 버튼으로 PDF에서 필드를 추가하세요.' : '계약의 필드 에디터에서 "템플릿으로 저장" 시 필드가 포함됩니다.'}</p>
                                </div>
                            ) : (
                                <div className="border rounded-lg overflow-hidden">
                                    <table className="w-full">
                                        <thead className="bg-gray-50">
                                            <tr>
                                                <th className="px-3 py-2 text-left text-[11px] font-medium text-gray-500">유형</th>
                                                <th className="px-3 py-2 text-left text-[11px] font-medium text-gray-500">라벨</th>
                                                <th className="px-3 py-2 text-center text-[11px] font-medium text-gray-500">서명자</th>
                                                <th className="px-3 py-2 text-center text-[11px] font-medium text-gray-500">페이지</th>
                                                <th className="px-3 py-2 text-center text-[11px] font-medium text-gray-500">위치 (X, Y)</th>
                                                <th className="px-3 py-2 text-center text-[11px] font-medium text-gray-500">필수</th>
                                                <th className="px-3 py-2 w-10"></th>
                                            </tr>
                                        </thead>
                                        <tbody className="divide-y">
                                            {tpl.items.map(item => {
                                                const ft = FIELD_TYPE_MAP[item.field_type] || { label: item.field_type, color: 'bg-gray-100 text-gray-700' };
                                                return (
                                                    <tr key={item.id} className="hover:bg-gray-50">
                                                        <td className="px-3 py-2">
                                                            <span className={`inline-block px-2 py-0.5 rounded text-[11px] font-medium ${ft.color}`}>{ft.label}</span>
                                                        </td>
                                                        <td className="px-3 py-2 text-sm text-gray-700">{item.field_label || '-'}</td>
                                                        <td className="px-3 py-2 text-center text-xs text-gray-500">#{item.signer_order}</td>
                                                        <td className="px-3 py-2 text-center text-xs text-gray-500">{item.page_number}</td>
                                                        <td className="px-3 py-2 text-center text-xs text-gray-400 font-mono">{Number(item.position_x).toFixed(0)}, {Number(item.position_y).toFixed(0)}</td>
                                                        <td className="px-3 py-2 text-center text-xs">{item.is_required ? <span className="text-green-600">&#10003;</span> : <span className="text-gray-300">-</span>}</td>
                                                        <td className="px-3 py-2 text-center">
                                                            <button onClick={() => handleDeleteItem(item.id)} title="필드 삭제"
                                                                    className="text-gray-400 hover:text-red-500 transition-colors">
                                                                <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
                                                            </button>
                                                        </td>
                                                    </tr>
                                                );
                                            })}
                                        </tbody>
                                    </table>
                                </div>
                            )}
                        </div>
                    )}
                </div>

                {/* 하단 버튼 */}
                <div className="px-6 py-4 border-t flex-shrink-0 flex justify-end gap-2">
                    <button onClick={onClose} className="px-4 py-1.5 text-sm border rounded-lg hover:bg-gray-50">닫기</button>
                    {tab === 'info' && (
                        <button onClick={handleSave} disabled={saving}
                                className="px-4 py-1.5 text-sm bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50">
                            {saving ? '저장 중...' : '저장'}
                        </button>
                    )}
                </div>
            </div>
        </div>
    );
};

// ─── TemplateCard ───
const TemplateCard = ({ template, onEdit, onDuplicate, onDelete, onPdfReplaced }) => {
    const [menuOpen, setMenuOpen] = useState(false);
    const [uploading, setUploading] = useState(false);
    const menuRef = useRef(null);
    const cardFileRef = useRef(null);

    useEffect(() => {
        const handler = (e) => { if (menuRef.current && !menuRef.current.contains(e.target)) setMenuOpen(false); };
        document.addEventListener('mousedown', handler);
        return () => document.removeEventListener('mousedown', handler);
    }, []);

    const handleCardFileChange = async (e) => {
        const file = e.target.files[0];
        if (!file) return;
        setUploading(true);
        try {
            const fd = new FormData();
            fd.append('file', file);
            const res = await fetch(`/esign/contracts/templates/${template.id}/upload-pdf`, {
                method: 'POST',
                headers: { 'Accept': 'application/json', 'X-CSRF-TOKEN': csrfToken },
                body: fd,
            });
            const json = await res.json();
            if (json.success) {
                onPdfReplaced(json.data);
            } else {
                alert(json.message || '업로드 실패');
            }
        } catch (_) { alert('서버 오류'); }
        setUploading(false);
        if (cardFileRef.current) cardFileRef.current.value = '';
    };

    const timeAgo = (dateStr) => {
        if (!dateStr) return '';
        const diff = Date.now() - new Date(dateStr).getTime();
        const mins = Math.floor(diff / 60000);
        if (mins < 1) return '방금';
        if (mins < 60) return `${mins}분 전`;
        const hours = Math.floor(mins / 60);
        if (hours < 24) return `${hours}시간 전`;
        const days = Math.floor(hours / 24);
        if (days < 30) return `${days}일 전`;
        const months = Math.floor(days / 30);
        return `${months}개월 전`;
    };

    return (
        <div className="bg-white border rounded-xl p-4 hover:shadow-md transition-shadow relative group">
            {/* 메뉴 버튼 */}
            <div className="absolute top-3 right-3" ref={menuRef}>
                <button onClick={() => setMenuOpen(o => !o)}
                        className="w-7 h-7 flex items-center justify-center rounded-md text-gray-400 hover:text-gray-600 hover:bg-gray-100 opacity-0 group-hover:opacity-100 transition-opacity">
                    <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
                        <circle cx="8" cy="3" r="1.5"/><circle cx="8" cy="8" r="1.5"/><circle cx="8" cy="13" r="1.5"/>
                    </svg>
                </button>
                {menuOpen && (
                    <div className="absolute right-0 top-full mt-1 w-32 bg-white border rounded-lg shadow-lg z-10 py-1">
                        <button onClick={() => { setMenuOpen(false); onEdit(template); }}
                                className="w-full text-left px-3 py-1.5 text-sm hover:bg-gray-50">편집</button>
                        {template.file_path && (
                            <a href={`/esign/templates/${template.id}/fields`}
                               className="block w-full text-left px-3 py-1.5 text-sm hover:bg-gray-50 text-teal-700">필드 편집</a>
                        )}
                        <button onClick={() => { setMenuOpen(false); onDuplicate(template.id); }}
                                className="w-full text-left px-3 py-1.5 text-sm hover:bg-gray-50">복제</button>
                        <div className="border-t my-1"></div>
                        <button onClick={() => { setMenuOpen(false); onDelete(template.id, template.name); }}
                                className="w-full text-left px-3 py-1.5 text-sm text-red-600 hover:bg-red-50">삭제</button>
                    </div>
                )}
            </div>

            {/* 카테고리 뱃지 */}
            <div className="flex items-start gap-2 mb-3">
                <div className="w-9 h-9 bg-blue-50 rounded-lg flex items-center justify-center text-blue-500 text-lg flex-shrink-0">
                    <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                        <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/>
                    </svg>
                </div>
                <div className="flex-1 min-w-0 pr-6">
                    <h3 className="font-semibold text-gray-900 text-sm truncate">{template.name}</h3>
                    {template.category && (
                        <span className="inline-block mt-1 px-2 py-0.5 bg-gray-100 text-gray-600 text-[11px] rounded-full">{template.category}</span>
                    )}
                </div>
            </div>

            {template.description && (
                <p className="text-xs text-gray-500 mb-3 line-clamp-2">{template.description}</p>
            )}

            {/* PDF 포함 표시 + 교체 버튼 */}
            {template.file_path ? (
                <div className="flex items-center gap-1.5 mb-2 px-2 py-1.5 bg-blue-50 rounded-lg">
                    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#3B82F6" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="flex-shrink-0">
                        <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/>
                    </svg>
                    <span className="text-[11px] text-blue-600 font-medium truncate flex-1">{template.file_name || 'PDF 포함'}</span>
                    <button onClick={(e) => { e.stopPropagation(); cardFileRef.current?.click(); }}
                            disabled={uploading}
                            className="text-[10px] text-blue-500 hover:text-blue-700 font-medium flex-shrink-0 px-1.5 py-0.5 rounded hover:bg-blue-100 transition-colors disabled:opacity-50"
                            title="PDF/Word 파일 교체">
                        {uploading ? '...' : '교체'}
                    </button>
                </div>
            ) : (
                <button onClick={(e) => { e.stopPropagation(); cardFileRef.current?.click(); }}
                        disabled={uploading}
                        className="flex items-center gap-1.5 mb-2 px-2 py-1.5 bg-gray-50 rounded-lg w-full hover:bg-gray-100 transition-colors border border-dashed border-gray-300 disabled:opacity-50">
                    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#9CA3AF" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="flex-shrink-0">
                        <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="12" y1="18" x2="12" y2="12"/><line x1="9" y1="15" x2="15" y2="15"/>
                    </svg>
                    <span className="text-[11px] text-gray-400 font-medium">{uploading ? '업로드 중...' : 'PDF/Word 파일 추가'}</span>
                </button>
            )}
            <input ref={cardFileRef} type="file" accept=".pdf,.doc,.docx" onChange={handleCardFileChange} className="hidden" />

            {/* 메타 정보 */}
            <div className="flex items-center gap-3 text-[11px] text-gray-400">
                <span>서명자 {template.signer_count}명</span>
                <span className="w-1 h-1 bg-gray-300 rounded-full"></span>
                <span>필드 {template.items_count ?? template.items?.length ?? 0}개</span>
            </div>

            {/* 필드 편집 버튼 (PDF가 있을 때) */}
            {template.file_path && (
                <a href={`/esign/templates/${template.id}/fields`}
                   hx-boost="false"
                   className="block w-full text-center mt-2 px-3 py-1.5 bg-teal-50 text-teal-700 rounded-lg hover:bg-teal-100 text-xs font-medium transition-colors">
                    필드 편집
                </a>
            )}

            {/* 하단: 생성자 + 시간 */}
            <div className="flex items-center justify-between mt-3 pt-3 border-t">
                <span className="text-[11px] text-gray-400">
                    {template.creator?.name || '알 수 없음'}
                </span>
                <span className="text-[11px] text-gray-400">
                    {timeAgo(template.created_at)}
                </span>
            </div>
        </div>
    );
};

// ─── CreateTemplateModal ───
const CreateTemplateModal = ({ open, onClose, onCreated, showToast }) => {
    const [name, setName] = useState('');
    const [description, setDescription] = useState('');
    const [category, setCategory] = useState('');
    const [customCategory, setCustomCategory] = useState('');
    const [useCustomCategory, setUseCustomCategory] = useState(false);
    const [signerCount, setSignerCount] = useState(2);
    const [pdfFile, setPdfFile] = useState(null);
    const [creating, setCreating] = useState(false);
    const fileInputRef = useRef(null);

    useEffect(() => {
        if (open) {
            setName(''); setDescription(''); setCategory(''); setCustomCategory('');
            setUseCustomCategory(false); setSignerCount(2); setPdfFile(null);
            if (fileInputRef.current) fileInputRef.current.value = '';
        }
    }, [open]);

    if (!open) return null;

    const finalCategory = useCustomCategory ? customCategory.trim() : category;

    const handleSubmit = async () => {
        if (!name.trim()) { alert('템플릿 이름을 입력해주세요.'); return; }
        setCreating(true);
        try {
            // 1. 빈 템플릿 생성
            const res = await fetch('/esign/contracts/templates', {
                method: 'POST',
                headers: { ...getHeaders(), 'Content-Type': 'application/json' },
                body: JSON.stringify({
                    name: name.trim(),
                    description: description.trim() || null,
                    category: finalCategory || null,
                    signer_count: signerCount,
                }),
            });
            const json = await res.json();
            if (!json.success) { alert(json.message || '생성 실패'); setCreating(false); return; }

            const templateId = json.data.id;

            // 2. PDF 파일이 있으면 업로드
            if (pdfFile) {
                const fd = new FormData();
                fd.append('file', pdfFile);
                const uploadRes = await fetch(`/esign/contracts/templates/${templateId}/upload-pdf`, {
                    method: 'POST',
                    headers: { 'Accept': 'application/json', 'X-CSRF-TOKEN': csrfToken },
                    body: fd,
                });
                const uploadJson = await uploadRes.json();
                if (!uploadJson.success) {
                    showToast('템플릿은 생성되었으나 PDF 업로드에 실패했습니다.', 'error');
                }
            }

            // 3. 필드 에디터로 이동
            showToast('템플릿이 생성되었습니다. 필드 에디터로 이동합니다.');
            setTimeout(() => { location.href = `/esign/templates/${templateId}/fields`; }, 500);
        } catch (e) {
            alert('서버 오류가 발생했습니다.');
            setCreating(false);
        }
    };

    const handleFileChange = (e) => {
        const file = e.target.files[0];
        if (file) setPdfFile(file);
    };

    return (
        <div className="fixed inset-0 bg-black/40 flex items-center justify-center z-50" onClick={onClose}>
            <div className="bg-white rounded-xl shadow-2xl w-[520px] max-h-[85vh] flex flex-col" onClick={e => e.stopPropagation()}>
                {/* 헤더 */}
                <div className="px-6 pt-5 pb-4 border-b flex-shrink-0">
                    <div className="flex items-center justify-between">
                        <h3 className="text-base font-semibold">새 템플릿 생성</h3>
                        <button onClick={onClose} className="text-gray-400 hover:text-gray-600 text-lg leading-none">&times;</button>
                    </div>
                    <p className="text-xs text-gray-400 mt-1">템플릿 기본 정보를 입력한 후 필드 에디터에서 서명 위치를 설정합니다.</p>
                </div>

                {/* 본문 */}
                <div className="px-6 py-5 flex-1 overflow-y-auto space-y-4">
                    {/* 이름 */}
                    <div>
                        <label className="text-xs text-gray-500 block mb-1">템플릿 이름 <span className="text-red-500">*</span></label>
                        <input type="text" value={name} onChange={e => setName(e.target.value)}
                               placeholder="예: 영업파트너 계약서 v2"
                               className="w-full border rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-blue-200 focus:border-blue-400 outline-none" autoFocus />
                    </div>

                    {/* 설명 */}
                    <div>
                        <label className="text-xs text-gray-500 block mb-1">설명</label>
                        <textarea value={description} onChange={e => setDescription(e.target.value)}
                                  rows={2} placeholder="템플릿에 대한 간단한 설명"
                                  className="w-full border rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-blue-200 focus:border-blue-400 outline-none resize-none" />
                    </div>

                    {/* 카테고리 */}
                    <div>
                        <label className="text-xs text-gray-500 block mb-1">카테고리</label>
                        {!useCustomCategory ? (
                            <div className="flex gap-2">
                                <select value={category} onChange={e => {
                                    if (e.target.value === '__custom__') { setUseCustomCategory(true); setCategory(''); }
                                    else setCategory(e.target.value);
                                }} className="flex-1 border rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-blue-200 focus:border-blue-400 outline-none">
                                    <option value="">카테고리 없음</option>
                                    {CATEGORIES.map(c => <option key={c} value={c}>{c}</option>)}
                                    <option value="__custom__">직접 입력...</option>
                                </select>
                            </div>
                        ) : (
                            <div className="flex gap-2">
                                <input type="text" value={customCategory} onChange={e => setCustomCategory(e.target.value)}
                                       placeholder="카테고리 이름 입력"
                                       className="flex-1 border rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-blue-200 focus:border-blue-400 outline-none" />
                                <button onClick={() => { setUseCustomCategory(false); setCustomCategory(''); }}
                                        className="px-3 py-2 text-xs border rounded-lg text-gray-500 hover:bg-gray-50">취소</button>
                            </div>
                        )}
                    </div>

                    {/* 서명자 수 */}
                    <div>
                        <label className="text-xs text-gray-500 block mb-1">서명자 수</label>
                        <div className="flex items-center gap-2">
                            {[1,2,3,4,5,6].map(n => (
                                <button key={n} onClick={() => setSignerCount(n)}
                                        className={`w-10 h-10 rounded-lg text-sm font-medium transition-colors ${signerCount === n ? 'bg-blue-600 text-white' : 'border text-gray-600 hover:bg-gray-50'}`}>
                                    {n}
                                </button>
                            ))}
                            <span className="text-xs text-gray-400 ml-1">명</span>
                        </div>
                    </div>

                    {/* PDF 파일 */}
                    <div>
                        <label className="text-xs text-gray-500 block mb-1">PDF 파일 <span className="text-gray-400">(선택)</span></label>
                        {pdfFile ? (
                            <div className="flex items-center gap-2 border rounded-lg px-3 py-2">
                                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#EF4444" strokeWidth="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
                                <span className="flex-1 text-sm text-gray-700 truncate">{pdfFile.name}</span>
                                <span className="text-xs text-gray-400">{(pdfFile.size / 1024 / 1024).toFixed(1)}MB</span>
                                <button onClick={() => { setPdfFile(null); if (fileInputRef.current) fileInputRef.current.value = ''; }}
                                        className="text-gray-400 hover:text-red-500 text-sm">&times;</button>
                            </div>
                        ) : (
                            <button onClick={() => fileInputRef.current?.click()}
                                    className="w-full border-2 border-dashed rounded-lg px-3 py-4 text-sm text-gray-400 hover:border-blue-300 hover:text-blue-500 transition-colors">
                                클릭하여 PDF 파일 선택 (나중에 업로드 가능)
                            </button>
                        )}
                        <input ref={fileInputRef} type="file" accept=".pdf,.doc,.docx" onChange={handleFileChange} className="hidden" />
                    </div>
                </div>

                {/* 하단 버튼 */}
                <div className="px-6 py-4 border-t flex-shrink-0 flex justify-end gap-2">
                    <button onClick={onClose} disabled={creating}
                            className="px-4 py-2 text-sm border rounded-lg hover:bg-gray-50 disabled:opacity-50">취소</button>
                    <button onClick={handleSubmit} disabled={creating || !name.trim()}
                            className="px-5 py-2 text-sm bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50 font-medium flex items-center gap-1.5">
                        {creating ? (
                            <><svg className="animate-spin h-4 w-4" 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 12h4z"/></svg> 생성 중...</>
                        ) : '생성 및 필드 편집'}
                    </button>
                </div>
            </div>
        </div>
    );
};

// ─── App ───
const App = () => {
    const [templates, setTemplates] = useState([]);
    const [loading, setLoading] = useState(true);
    const [search, setSearch] = useState('');
    const [category, setCategory] = useState('');
    const [signerCount, setSignerCount] = useState('');
    const [editModal, setEditModal] = useState({ open: false, template: null });
    const [createModal, setCreateModal] = useState(false);
    const [toast, setToast] = useState(null);
    const searchTimeout = useRef(null);

    const showToast = (message, type = 'success') => setToast({ message, type });

    const fetchTemplates = useCallback(async (params = {}) => {
        setLoading(true);
        const qs = new URLSearchParams();
        if (params.search || search) qs.set('search', params.search ?? search);
        if (params.category || category) qs.set('category', params.category ?? category);
        if (params.signerCount || signerCount) qs.set('signer_count', params.signerCount ?? signerCount);

        try {
            const res = await fetch(`/esign/contracts/templates?${qs}`, { headers: getHeaders() });
            const json = await res.json();
            if (json.success) setTemplates(json.data);
        } catch (_) {}
        setLoading(false);
    }, [search, category, signerCount]);

    useEffect(() => { fetchTemplates(); }, []);

    const handleSearchChange = (val) => {
        setSearch(val);
        clearTimeout(searchTimeout.current);
        searchTimeout.current = setTimeout(() => fetchTemplates({ search: val }), 300);
    };

    const handleCategoryChange = (val) => {
        setCategory(val);
        fetchTemplates({ category: val });
    };

    const handleSignerCountChange = (val) => {
        setSignerCount(val);
        fetchTemplates({ signerCount: val });
    };

    const handleEdit = async (template) => {
        // 상세 데이터(items 포함) 로드
        try {
            const res = await fetch(`/esign/contracts/templates/${template.id}`, { headers: getHeaders() });
            const json = await res.json();
            if (json.success) {
                setEditModal({ open: true, template: json.data });
            } else {
                setEditModal({ open: true, template });
            }
        } catch (_) {
            setEditModal({ open: true, template });
        }
    };

    const handleUpdate = (updatedTemplate) => {
        setTemplates(prev => prev.map(t => t.id === updatedTemplate.id
            ? { ...t, ...updatedTemplate, items_count: updatedTemplate.items?.length ?? t.items_count }
            : t
        ));
        showToast('템플릿이 수정되었습니다.');
    };

    const handleDuplicate = async (id) => {
        try {
            const res = await fetch(`/esign/contracts/templates/${id}/duplicate`, {
                method: 'POST', headers: getHeaders(),
            });
            const json = await res.json();
            if (json.success) {
                setTemplates(prev => [json.data, ...prev]);
                showToast('템플릿이 복제되었습니다.');
            } else {
                alert(json.message || '복제 실패');
            }
        } catch (_) { alert('서버 오류'); }
    };

    const handleDelete = async (id, name) => {
        if (!confirm(`"${name}" 템플릿을 삭제하시겠습니까?`)) return;
        try {
            const res = await fetch(`/esign/contracts/templates/${id}`, {
                method: 'DELETE', headers: getHeaders(),
            });
            const json = await res.json();
            if (json.success) {
                setTemplates(prev => prev.filter(t => t.id !== id));
                showToast('템플릿이 삭제되었습니다.');
            }
        } catch (_) { alert('서버 오류'); }
    };

    const handleNewTemplate = () => {
        setCreateModal(true);
    };

    // 카테고리 목록 (데이터에서 동적으로 추출 + 기본값)
    const allCategories = [...new Set([...CATEGORIES, ...templates.map(t => t.category).filter(Boolean)])].sort();

    return (
        <div className="p-4 sm:p-6">
            {toast && <TemplateToast message={toast.message} type={toast.type} onClose={() => setToast(null)} />}

            {/* 헤더 */}
            <div className="flex items-center justify-between mb-5">
                <div className="flex items-center gap-3">
                    <a href="/esign" className="text-gray-400 hover:text-gray-600 text-lg" hx-boost="false">&larr;</a>
                    <div>
                        <h1 className="text-xl font-bold text-gray-900">템플릿 관리</h1>
                        <p className="text-xs text-gray-400 mt-0.5">전자계약 필드 템플릿을 관리합니다</p>
                    </div>
                    <button onClick={() => fetchTemplates()} title="새로고침"
                            className="w-8 h-8 flex items-center justify-center rounded-lg text-gray-400 hover:text-gray-600 hover:bg-gray-100 transition-colors">
                        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                            <polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0114.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0020.49 15"/>
                        </svg>
                    </button>
                </div>
                <button onClick={handleNewTemplate}
                        className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 text-sm font-medium transition-colors flex items-center gap-1.5">
                    <span className="text-base leading-none">+</span> 새 템플릿
                </button>
            </div>

            {/* 필터 바 */}
            <div className="flex items-center gap-3 mb-5 flex-wrap">
                <div className="relative flex-1 min-w-[200px] max-w-[320px]">
                    <input type="text" value={search} onChange={e => handleSearchChange(e.target.value)}
                           placeholder="템플릿 검색..."
                           className="w-full border rounded-lg pl-9 pr-3 py-2 text-sm focus:ring-2 focus:ring-blue-200 focus:border-blue-400 outline-none" />
                    <svg className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
                        <circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/>
                    </svg>
                </div>
                <select value={category} onChange={e => handleCategoryChange(e.target.value)}
                        className="border rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-blue-200 focus:border-blue-400 outline-none min-w-[130px]">
                    <option value="">전체 카테고리</option>
                    {allCategories.map(c => <option key={c} value={c}>{c}</option>)}
                </select>
                <select value={signerCount} onChange={e => handleSignerCountChange(e.target.value)}
                        className="border rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-blue-200 focus:border-blue-400 outline-none min-w-[130px]">
                    <option value="">전체 서명자 수</option>
                    <option value="1">서명자 1명</option>
                    <option value="2">서명자 2명</option>
                    <option value="3">서명자 3명</option>
                </select>
                {(search || category || signerCount) && (
                    <button onClick={() => { setSearch(''); setCategory(''); setSignerCount(''); fetchTemplates({ search: '', category: '', signerCount: '' }); }}
                            className="text-xs text-gray-500 hover:text-gray-700 underline">초기화</button>
                )}
            </div>

            {/* 템플릿 그리드 */}
            {loading ? (
                <div className="flex items-center justify-center py-20 text-gray-400">
                    <svg className="animate-spin mr-2 h-5 w-5" 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 12h4z"/>
                    </svg>
                    불러오는 중...
                </div>
            ) : templates.length === 0 ? (
                <div className="text-center py-20">
                    <div className="text-gray-300 text-5xl mb-3">
                        <svg className="mx-auto" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
                            <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/>
                        </svg>
                    </div>
                    <p className="text-gray-500 text-sm mb-1">
                        {search || category || signerCount ? '조건에 맞는 템플릿이 없습니다.' : '아직 템플릿이 없습니다.'}
                    </p>
                    <p className="text-gray-400 text-xs">필드 에디터에서 "템플릿으로 저장"으로 만들 수 있습니다.</p>
                </div>
            ) : (
                <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
                    {templates.map(t => (
                        <TemplateCard
                            key={t.id}
                            template={t}
                            onEdit={handleEdit}
                            onDuplicate={handleDuplicate}
                            onDelete={handleDelete}
                            onPdfReplaced={(updated) => {
                                setTemplates(prev => prev.map(x => x.id === updated.id
                                    ? { ...x, ...updated, items_count: updated.items?.length ?? x.items_count }
                                    : x
                                ));
                                showToast('파일이 교체되었습니다.');
                            }}
                        />
                    ))}
                </div>
            )}

            {/* 생성 모달 */}
            <CreateTemplateModal
                open={createModal}
                onClose={() => setCreateModal(false)}
                onCreated={(tpl) => { setTemplates(prev => [tpl, ...prev]); }}
                showToast={showToast}
            />

            {/* 편집 모달 */}
            <EditTemplateModal
                open={editModal.open}
                template={editModal.template}
                onClose={() => setEditModal({ open: false, template: null })}
                onUpdate={handleUpdate}
            />

            <style>{`
                @keyframes slide-in {
                    from { transform: translateX(100px); opacity: 0; }
                    to { transform: translateX(0); opacity: 1; }
                }
                .animate-slide-in { animation: slide-in 0.3s ease-out; }
                .line-clamp-2 {
                    display: -webkit-box;
                    -webkit-line-clamp: 2;
                    -webkit-box-orient: vertical;
                    overflow: hidden;
                }
            `}</style>
        </div>
    );
};

ReactDOM.createRoot(document.getElementById('esign-templates-root')).render(<App />);
</script>
@endverbatim
@endpush
