/* ============================================================
   Dashboard: Gestión · Subida · Vista previa
   ============================================================ */

/* ——————————————————— Gestión de fotos ——————————————————— */
function PhotoManagement({ go, photos, onToggle, onDelete, onReplace, onEdit }) {
  const [q, setQ] = React.useState('');
  const [cat, setCat] = React.useState('todas');
  const [status, setStatus] = React.useState('todas');
  const [confirm, setConfirm] = React.useState(null);
  const [editingPhoto, setEditingPhoto] = React.useState(null);
  const [replacingPhoto, setReplacingPhoto] = React.useState(null);

  const list = photos.filter(p => {
    if (status !== 'todas' && p.status !== status) return false;
    if (cat !== 'todas' && p.cat !== cat) return false;
    if (q && !p.title.toLowerCase().includes(q.toLowerCase())) return false;
    return true;
  });
  const tempIds = ['p13', 'p14'];

  return (
    <DashLayout go={go} route="manage" title="Fotografías" sub={`${photos.length} publicaciones en tu archivo`}
      action={<button className="btn btn-primary" onClick={() => go('upload')}><Icon name="plus" size={17} /> Subir foto</button>}>

      {/* Controles */}
      <div className="manage-controls" style={{ display: 'flex', gap: 12, marginBottom: 22, flexWrap: 'wrap', alignItems: 'center' }}>
        <div style={{ position: 'relative', flex: '1 1 240px', minWidth: 200 }}>
          <Icon name="search" size={17} style={{ position: 'absolute', left: 14, top: 13, color: 'var(--ink-3)' }} />
          <input className="input" style={{ paddingLeft: 42 }} placeholder="Buscar por título…" value={q} onChange={e => setQ(e.target.value)} />
        </div>
        <select className="select" style={{ width: 'auto', minWidth: 160 }} value={cat} onChange={e => setCat(e.target.value)}>
          <option value="todas">Todas las categorías</option>
          {window.CATEGORIES.map(c => <option key={c.id} value={c.id}>{c.label}</option>)}
        </select>
        <div style={{ display: 'flex', gap: 6, background: 'var(--surface)', border: '1px solid var(--line-2)', borderRadius: 'var(--r-pill)', padding: 4 }}>
          {[['todas','Todas'],['published','Publicadas'],['draft','Borradores']].map(([id,l]) => (
            <button key={id} onClick={() => setStatus(id)} style={{ border: 'none', borderRadius: 999, padding: '7px 14px', fontSize: 13, fontWeight: 600,
              background: status===id ? 'var(--ink)' : 'transparent', color: status===id ? '#fff' : 'var(--ink-2)', transition: 'all .2s' }}>{l}</button>
          ))}
        </div>
      </div>

      {/* Lista */}
      {list.length === 0 ? (
        <div className="card"><EmptyState title="Sin resultados" sub="No encontramos fotografías con esos filtros. Prueba a ajustar la búsqueda." action={<button className="btn btn-soft" onClick={() => { setQ(''); setCat('todas'); setStatus('todas'); }}>Limpiar filtros</button>} /></div>
      ) : (
        <div className="manage-grid" style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(290px, 1fr))', gap: 18 }}>
          {list.map((p, i) => {
            const isTemp = tempIds.includes(p.id);
            return (
              <div key={p.id} className="card manage-card stag" style={{ overflow: 'hidden', display: 'flex', flexDirection: 'column', '--d': `${Math.min(i * .05, .35)}s` }}>
                <div style={{ position: 'relative' }}>
                  <PhotoFrame photo={p} label={false} temp={isTemp} wmStyle="white" rounded="0" />
                  <span style={{ position: 'absolute', top: 10, right: 10 }}><StatusBadge status={p.status} /></span>
                </div>
                <div style={{ padding: '14px 16px', flex: 1, display: 'flex', flexDirection: 'column' }}>
                  <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: 8 }}>
                    <span className="serif" style={{ fontSize: 19, fontStyle: 'italic', color: 'var(--ink)' }}>{p.title}</span>
                    <span style={{ fontSize: 11, color: 'var(--ink-3)', textTransform: 'uppercase', letterSpacing: '.08em', whiteSpace: 'nowrap' }}>{catLabel(p.cat)}</span>
                  </div>
                  {/* Meta técnica */}
                  <div style={{ display: 'flex', gap: 14, marginTop: 10, fontSize: 11.5, color: 'var(--ink-3)', flexWrap: 'wrap' }}>
                    <span style={{ display: 'flex', alignItems: 'center', gap: 4 }}><Icon name="image" size={13} /> {p.res}</span>
                    <span style={{ display: 'flex', alignItems: 'center', gap: 4 }}><Icon name="database" size={13} /> {p.kb} KB</span>
                    <span style={{ display: 'flex', alignItems: 'center', gap: 4, color: 'var(--published)' }}><Icon name="check" size={13} stroke={2.4} /> Optimizada</span>
                  </div>
                  <div style={{ height: 1, background: 'var(--line)', margin: '14px 0' }} />
                  {/* Acciones */}
                  <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginTop: 'auto' }}>
                    <IconBtn icon="edit" label="Editar" onClick={() => setEditingPhoto(p)} />
                    {p.status === 'published'
                      ? <IconBtn icon="eyeOff" label="Ocultar" onClick={() => onToggle(p.id)} />
                      : <IconBtn icon="upload" label="Publicar" tone="rose" onClick={() => onToggle(p.id)} />}
                    <IconBtn icon="replace" label="Reemplazar" onClick={() => setReplacingPhoto(p)} />
                    <IconBtn icon="trash" label="Eliminar" tone="danger" onClick={() => setConfirm(p)} />
                  </div>
                </div>
              </div>
            );
          })}
        </div>
      )}

      {confirm && <ConfirmDelete photo={confirm} onCancel={() => setConfirm(null)} onConfirm={() => { onDelete(confirm.id); setConfirm(null); }} />}
      {editingPhoto && (
        <EditPhotoModal
          photo={editingPhoto}
          onClose={() => setEditingPhoto(null)}
          onSave={(updated) => { onEdit(updated); setEditingPhoto(null); }}
        />
      )}
      {replacingPhoto && (
        <ReplaceImageModal
          photo={replacingPhoto}
          onClose={() => setReplacingPhoto(null)}
          onSave={(fields) => { onReplace(replacingPhoto.id, fields); setReplacingPhoto(null); }}
        />
      )}
    </DashLayout>
  );
}

function IconBtn({ icon, label, onClick, tone }) {
  const color = tone === 'danger' ? 'var(--danger)' : tone === 'rose' ? 'var(--rose-deep)' : 'var(--ink-2)';
  const bg = tone === 'danger' ? 'var(--danger-soft)' : tone === 'rose' ? 'var(--rose-soft)' : 'var(--surface-2)';
  return (
    <button onClick={onClick} className="iconbtn" style={{ display: 'inline-flex', alignItems: 'center', gap: 6, border: 'none', borderRadius: 'var(--r-sm)', padding: '8px 11px', fontSize: 12.5, fontWeight: 600, background: bg, color, transition: 'all .18s', cursor: 'pointer' }}>
      <Icon name={icon} size={15} /> {label}
    </button>
  );
}

/* ——————————————————— Modal de eliminación ——————————————————— */
function ConfirmDelete({ photo, onCancel, onConfirm }) {
  React.useEffect(() => {
    const scrollY = window.scrollY || window.pageYOffset || 0;
    const prev = { overflow: document.body.style.overflow, position: document.body.style.position, top: document.body.style.top, width: document.body.style.width };
    document.body.style.overflow = 'hidden';
    document.body.style.position = 'fixed';
    document.body.style.top = `-${scrollY}px`;
    document.body.style.width = '100%';
    return () => {
      document.body.style.overflow = prev.overflow;
      document.body.style.position = prev.position;
      document.body.style.top = prev.top;
      document.body.style.width = prev.width;
      window.scrollTo(0, scrollY);
    };
  }, []);
  return (
    <div className="modal-backdrop animate-fadeIn" onClick={onCancel} role="dialog" aria-modal="true" aria-labelledby="confirm-delete-title" style={{ position: 'fixed', inset: 0, background: 'rgba(44,40,48,.45)', backdropFilter: 'blur(4px)', zIndex: 150, display: 'grid', placeItems: 'center', padding: 20 }}>
      <div className="animate-scaleIn" onClick={e => e.stopPropagation()} style={{ background: 'var(--surface)', borderRadius: 'var(--r-lg)', boxShadow: 'var(--sh-lg)', maxWidth: 420, width: '100%', padding: 32, textAlign: 'center' }}>
        <div style={{ width: 60, height: 60, borderRadius: '50%', background: 'var(--danger-soft)', display: 'grid', placeItems: 'center', margin: '0 auto 18px' }}>
          <Icon name="trash" size={26} style={{ color: 'var(--danger)' }} />
        </div>
        <h3 id="confirm-delete-title" className="serif" style={{ margin: 0, fontSize: 26, fontWeight: 500, color: 'var(--ink)' }}>¿Eliminar fotografía?</h3>
        <p style={{ margin: '10px 0 24px', color: 'var(--ink-2)', fontSize: 14.5, lineHeight: 1.6 }}>
          Estás por eliminar <strong>"{photo.title}"</strong> de tu archivo. Esta acción no se puede deshacer.
        </p>
        <div style={{ display: 'flex', gap: 12 }}>
          <button className="btn btn-soft btn-block" onClick={onCancel} autoFocus>Cancelar</button>
          <button className="btn btn-block" style={{ background: 'var(--danger-text)', color: '#fff' }} onClick={onConfirm}>Sí, eliminar</button>
        </div>
      </div>
    </div>
  );
}

/* ——————————————————— Modal: editar metadatos ——————————————————— */
function EditPhotoModal({ photo, onClose, onSave }) {
  const [form, setForm] = React.useState({
    title: photo.title || '',
    desc: photo.desc || '',
    cat: photo.cat || (window.CATEGORIES[0]?.id || ''),
    col: photo.col || window.COLLECTIONS[0] || '',
    status: photo.status || 'draft',
    fav: Boolean(photo.fav),
    alt: photo.alt || '',
  });
  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));
  React.useEffect(() => {
    const scrollY = window.scrollY || window.pageYOffset || 0;
    const previousOverflow = document.body.style.overflow;
    const previousPosition = document.body.style.position;
    const previousTop = document.body.style.top;
    const previousWidth = document.body.style.width;
    document.body.style.overflow = 'hidden';
    document.body.style.position = 'fixed';
    document.body.style.top = `-${scrollY}px`;
    document.body.style.width = '100%';
    return () => {
      document.body.style.overflow = previousOverflow;
      document.body.style.position = previousPosition;
      document.body.style.top = previousTop;
      document.body.style.width = previousWidth;
      window.scrollTo(0, scrollY);
    };
  }, []);

  return (
    <div className="modal-backdrop animate-fadeIn" onClick={onClose} role="dialog" aria-modal="true" aria-labelledby="edit-photo-title" style={{ position: 'fixed', inset: 0, background: 'rgba(44,40,48,.45)', backdropFilter: 'blur(4px)', zIndex: 150, display: 'grid', placeItems: 'center', padding: 20, overflowY: 'auto' }}>
      <div className="animate-scaleIn" onClick={e => e.stopPropagation()} style={{ background: 'var(--surface)', borderRadius: 'var(--r-lg)', boxShadow: 'var(--sh-lg)', maxWidth: 560, width: '100%', overflow: 'hidden', margin: 'auto' }}>
        {/* Header */}
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '18px 24px', borderBottom: '1px solid var(--line)' }}>
          <div>
            <h3 id="edit-photo-title" className="serif" style={{ margin: 0, fontSize: 22, fontWeight: 500, color: 'var(--ink)' }}>Editar fotografía</h3>
            <p style={{ margin: '2px 0 0', fontSize: 12.5, color: 'var(--ink-3)' }}>Metadatos de "{photo.title}"</p>
          </div>
          <button onClick={onClose} aria-label="Cerrar" style={{ background: 'var(--surface-2)', border: 'none', borderRadius: '50%', width: 36, height: 36, display: 'grid', placeItems: 'center', color: 'var(--ink-2)', cursor: 'pointer' }}>
            <Icon name="close" size={18} />
          </button>
        </div>

        {/* Body */}
        <div style={{ padding: 24, display: 'flex', flexDirection: 'column', gap: 16 }}>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
            <div>
              <label className="field-label">Título</label>
              <input className="input" value={form.title} onChange={e => set('title', e.target.value)} />
            </div>
            <div>
              <label className="field-label">Texto alternativo</label>
              <input className="input" placeholder="Para lectores de pantalla" value={form.alt} onChange={e => set('alt', e.target.value)} />
            </div>
          </div>

          <div>
            <label className="field-label">Descripción</label>
            <textarea className="textarea" rows={3} value={form.desc} onChange={e => set('desc', e.target.value)} />
          </div>

          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
            <div>
              <label className="field-label">Categoría</label>
              <select className="select" value={form.cat} onChange={e => set('cat', e.target.value)}>
                {window.CATEGORIES.map(c => <option key={c.id} value={c.id}>{c.label}</option>)}
              </select>
            </div>
            <div>
              <label className="field-label">Colección</label>
              <select className="select" value={form.col} onChange={e => set('col', e.target.value)}>
                {window.COLLECTIONS.map(c => <option key={c} value={c}>{c}</option>)}
              </select>
            </div>
          </div>

          <div className="edit-photo-state-grid" style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 1fr) minmax(116px, 140px)', gap: 14, alignItems: 'end' }}>
            <div>
              <label className="field-label">Estado</label>
              <div style={{ display: 'flex', gap: 8 }}>
                {[['published','Publicada','check'],['draft','Borrador','edit']].map(([id,l,ic]) => (
                  <button key={id} onClick={() => set('status', id)} style={{ flex: '1 1 0', minWidth: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 7, padding: '10px 8px', borderRadius: 'var(--r-sm)', fontSize: 13.5, fontWeight: 600, transition: 'all .2s', border: `1.5px solid ${form.status===id ? 'var(--rose)' : 'var(--line-2)'}`, background: form.status===id ? 'var(--rose-tint)' : 'var(--surface)', color: form.status===id ? 'var(--rose-deep)' : 'var(--ink-2)', cursor: 'pointer', whiteSpace: 'nowrap' }}>
                    <Icon name={ic} size={15} /> {l}
                  </button>
                ))}
              </div>
            </div>
            <div>
              <label className="field-label">Favorita</label>
              <button onClick={() => set('fav', !form.fav)} style={{ width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8, padding: '10px 10px', borderRadius: 'var(--r-sm)', fontSize: 13.5, fontWeight: 600, border: `1.5px solid ${form.fav ? 'var(--rose)' : 'var(--line-2)'}`, background: form.fav ? 'var(--rose-tint)' : 'var(--surface)', color: form.fav ? 'var(--rose-deep)' : 'var(--ink-2)', transition: 'all .2s', cursor: 'pointer', whiteSpace: 'nowrap' }}>
                <Icon name="heart" size={15} fill={form.fav ? 'var(--rose)' : 'none'} /> {form.fav ? 'Favorita' : 'Normal'}
              </button>
            </div>
          </div>
        </div>

        {/* Footer */}
        <div style={{ padding: '16px 24px', borderTop: '1px solid var(--line)', display: 'flex', gap: 10, justifyContent: 'flex-end' }}>
          <button className="btn btn-soft" onClick={onClose}>Cancelar</button>
          <button className="btn btn-primary" onClick={() => onSave({ ...photo, ...form })}>
            <Icon name="check" size={16} stroke={2.4} /> Guardar cambios
          </button>
        </div>
      </div>
    </div>
  );
}

/* ——————————————————— Modal: reemplazar imagen ——————————————————— */
function ReplaceImageModal({ photo, onClose, onSave }) {
  const [selectedFile, setSelectedFile] = React.useState(null);
  const [previewUrl, setPreviewUrl] = React.useState('');
  const [dragOver, setDragOver] = React.useState(false);
  const [step, setStep] = React.useState('idle'); // idle | optimizing | uploading | error
  const [progress, setProgress] = React.useState(0);
  const [error, setError] = React.useState('');
  const fileInputRef = React.useRef(null);
  const isProcessing = step === 'optimizing' || step === 'uploading';

  React.useEffect(() => {
    if (!selectedFile) { setPreviewUrl(''); return; }
    const url = URL.createObjectURL(selectedFile);
    setPreviewUrl(url);
    return () => URL.revokeObjectURL(url);
  }, [selectedFile]);

  React.useEffect(() => {
    const scrollY = window.scrollY || window.pageYOffset || 0;
    const prev = { overflow: document.body.style.overflow, position: document.body.style.position, top: document.body.style.top, width: document.body.style.width };
    document.body.style.overflow = 'hidden';
    document.body.style.position = 'fixed';
    document.body.style.top = `-${scrollY}px`;
    document.body.style.width = '100%';
    return () => {
      document.body.style.overflow = prev.overflow;
      document.body.style.position = prev.position;
      document.body.style.top = prev.top;
      document.body.style.width = prev.width;
      window.scrollTo(0, scrollY);
    };
  }, []);

  const pickFile = (file) => {
    if (!file) return;
    if (!file.type.startsWith('image/')) { setError('Solo se permiten imágenes (JPEG, PNG, WebP…)'); return; }
    if (file.size > 25 * 1024 * 1024) { setError(`Imagen demasiado grande (${Math.round(file.size / 1024 / 1024)} MB). Máximo 25 MB.`); return; }
    setError('');
    setSelectedFile(file);
    setStep('idle');
  };

  const handleReplace = async () => {
    if (!selectedFile) { setError('Selecciona una imagen primero'); return; }
    setError('');
    setProgress(0);
    try {
      setStep('optimizing');
      const { file: optimized, width, height, o } = await window.MPM_R2.optimizeImage(selectedFile);
      setStep('uploading');
      const uploaded = await window.MPM_R2.uploadImage(optimized, p => setProgress(p));
      if (photo.image_key) {
        await window.MPM_R2.deleteImage(photo.image_key).catch(() => {});
      }
      onSave({
        image_key: uploaded.key,
        image_url: uploaded.url,
        kb: Math.round(optimized.size / 1024),
        res: `${width}x${height}`,
        o: o || 'v',
      });
    } catch (err) {
      setStep('error');
      setError(err.message || 'Error al reemplazar la imagen');
    }
  };

  return (
    <div className="modal-backdrop animate-fadeIn" onClick={!isProcessing ? onClose : undefined} style={{ position: 'fixed', inset: 0, background: 'rgba(44,40,48,.45)', backdropFilter: 'blur(4px)', zIndex: 150, display: 'grid', placeItems: 'center', padding: 20, overflowY: 'auto' }}>
      <div className="animate-scaleIn" onClick={e => e.stopPropagation()} style={{ background: 'var(--surface)', borderRadius: 'var(--r-lg)', boxShadow: 'var(--sh-lg)', maxWidth: 680, width: '100%', overflow: 'hidden', margin: 'auto' }}>
        {/* Header */}
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '18px 24px', borderBottom: '1px solid var(--line)' }}>
          <div>
            <h3 className="serif" style={{ margin: 0, fontSize: 22, fontWeight: 500, color: 'var(--ink)' }}>Reemplazar imagen</h3>
            <p style={{ margin: '2px 0 0', fontSize: 12.5, color: 'var(--ink-3)' }}>La nueva imagen se optimizará a WebP y subirá a R2</p>
          </div>
          {!isProcessing && (
            <button onClick={onClose} style={{ background: 'var(--surface-2)', border: 'none', borderRadius: '50%', width: 36, height: 36, display: 'grid', placeItems: 'center', color: 'var(--ink-2)', cursor: 'pointer' }}>
              <Icon name="close" size={18} />
            </button>
          )}
        </div>

        {/* Body: actual vs nueva */}
        <div style={{ padding: 24, display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 20 }}>
          <div>
            <p style={{ margin: '0 0 10px', fontSize: 12, fontWeight: 700, color: 'var(--ink-3)', textTransform: 'uppercase', letterSpacing: '.08em' }}>Imagen actual</p>
            <PhotoFrame photo={photo} ratio="4/5" label={false} temp={false} rounded="var(--r-md)" />
            <p style={{ margin: '8px 0 0', fontSize: 12, color: 'var(--ink-3)', textAlign: 'center' }}>{photo.kb ? `${photo.kb} KB · ${photo.res}` : '—'}</p>
          </div>
          <div>
            <p style={{ margin: '0 0 10px', fontSize: 12, fontWeight: 700, color: 'var(--ink-3)', textTransform: 'uppercase', letterSpacing: '.08em' }}>Nueva imagen</p>
            {previewUrl ? (
              <div style={{ position: 'relative', borderRadius: 'var(--r-md)', overflow: 'hidden', aspectRatio: '4/5', background: 'var(--surface-2)' }}>
                <img src={previewUrl} style={{ width: '100%', height: '100%', objectFit: 'cover' }} alt="Vista previa nueva imagen" />
                {!isProcessing && (
                  <button onClick={() => { setSelectedFile(null); setStep('idle'); setError(''); }} style={{ position: 'absolute', top: 8, right: 8, background: 'rgba(0,0,0,.6)', border: 'none', borderRadius: '50%', width: 30, height: 30, display: 'grid', placeItems: 'center', color: '#fff', cursor: 'pointer' }}>
                    <Icon name="close" size={16} />
                  </button>
                )}
              </div>
            ) : (
              <div
                onDragOver={e => { e.preventDefault(); setDragOver(true); }}
                onDragLeave={() => setDragOver(false)}
                onDrop={e => { e.preventDefault(); setDragOver(false); pickFile(e.dataTransfer.files?.[0]); }}
                onClick={() => fileInputRef.current?.click()}
                style={{ aspectRatio: '4/5', border: `2px dashed ${dragOver ? 'var(--rose)' : 'var(--line-2)'}`, borderRadius: 'var(--r-md)', background: dragOver ? 'var(--rose-tint)' : 'var(--surface)', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 10, cursor: 'pointer', transition: 'all .2s' }}>
                <Icon name="upload" size={28} style={{ color: 'var(--rose-deep)' }} />
                <p style={{ margin: 0, fontSize: 13, color: 'var(--ink-2)', textAlign: 'center', lineHeight: 1.5 }}>Arrastra o haz clic<br /><span style={{ color: 'var(--ink-3)', fontSize: 12 }}>Máx. 25 MB · JPEG / PNG / WebP</span></p>
                <input ref={fileInputRef} type="file" accept="image/*" style={{ display: 'none' }} onChange={e => pickFile(e.target.files?.[0])} />
              </div>
            )}
          </div>
        </div>

        {/* Estado */}
        {(error || step === 'optimizing' || step === 'uploading') && (
          <div style={{ margin: '0 24px 20px' }}>
            {error && (
              <div style={{ display: 'flex', gap: 8, alignItems: 'flex-start', padding: '11px 14px', background: '#fef2f2', border: '1px solid #fecaca', borderRadius: 'var(--r-sm)' }}>
                <Icon name="alert" size={16} style={{ color: '#dc2626', flexShrink: 0, marginTop: 1 }} />
                <p style={{ margin: 0, fontSize: 13, color: '#dc2626' }}>{error}</p>
              </div>
            )}
            {step === 'optimizing' && (
              <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '11px 14px', background: 'var(--rose-tint)', borderRadius: 'var(--r-sm)' }}>
                <div className="skeleton" style={{ width: 16, height: 16, borderRadius: '50%', flexShrink: 0 }} />
                <p style={{ margin: 0, fontSize: 13, color: 'var(--rose-deep)' }}>Comprimiendo y convirtiendo a WebP…</p>
              </div>
            )}
            {step === 'uploading' && (
              <div style={{ padding: '11px 14px', background: 'var(--surface-2)', borderRadius: 'var(--r-sm)' }}>
                <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 7, fontSize: 12.5, color: 'var(--ink-2)' }}>
                  <span>Subiendo a R2…</span><span style={{ fontWeight: 700 }}>{progress}%</span>
                </div>
                <div style={{ height: 5, background: 'var(--line-2)', borderRadius: 999 }}>
                  <div style={{ height: '100%', width: '100%', background: 'var(--rose)', borderRadius: 999, transform: `scaleX(${progress / 100})`, transformOrigin: 'left center', transition: 'transform .3s' }} />
                </div>
              </div>
            )}
          </div>
        )}

        {/* Footer */}
        <div style={{ padding: '16px 24px', borderTop: '1px solid var(--line)', display: 'flex', gap: 10, justifyContent: 'flex-end' }}>
          <button className="btn btn-soft" onClick={onClose} disabled={isProcessing}>Cancelar</button>
          <button className="btn btn-primary" onClick={handleReplace} disabled={!selectedFile || isProcessing}>
            <Icon name="replace" size={16} />
            {step === 'optimizing' ? 'Optimizando…' : step === 'uploading' ? 'Subiendo…' : 'Reemplazar imagen'}
          </button>
        </div>
      </div>
    </div>
  );
}

/* ——————————————————— Subir nueva fotografía ——————————————————— */
function Upload({ go, onPublish, signature, onSaveSignature }) {
  const [form, setForm] = React.useState({ title: '', desc: '', cat: 'retratos', col: window.COLLECTIONS[0], status: 'published', alt: '' });
  const [wmPos, setWmPos] = React.useState('br');
  const [wmStyle, setWmStyle] = React.useState('white');
  const [dragOver, setDragOver] = React.useState(false);
  const [hasImg, setHasImg] = React.useState(true);
  const [preview, setPreview] = React.useState(false);
  const [selectedFile, setSelectedFile] = React.useState(null);
  const [previewUrl, setPreviewUrl] = React.useState('');
  const [uploading, setUploading] = React.useState(false);
  const [optimizing, setOptimizing] = React.useState(false);
  const [uploadProgress, setUploadProgress] = React.useState(0);
  const [uploadError, setUploadError] = React.useState('');
  const [signatureBusy, setSignatureBusy] = React.useState(false);
  const [signatureError, setSignatureError] = React.useState('');
  const fileInputRef = React.useRef(null);
  const signatureInputRef = React.useRef(null);
  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));
  const tone = (window.CATEGORIES.find(c => c.id === form.cat) || {}).tone || 'rose';
  const demoPhoto = { tone, o: 'v', cat: form.cat, title: form.title || 'Sin título' };
  const displayPhoto = { ...demoPhoto, image_url: previewUrl };

  React.useEffect(() => {
    if (!selectedFile) { setPreviewUrl(''); return; }
    const url = URL.createObjectURL(selectedFile);
    setPreviewUrl(url);
    return () => URL.revokeObjectURL(url);
  }, [selectedFile]);

  const setFile = (file) => {
    if (!file) return;
    if (!file.type.startsWith('image/')) {
      setUploadError('Solo se permiten imágenes (JPEG, PNG, WebP, etc.)');
      return;
    }
    if (file.size > 25 * 1024 * 1024) {
      setUploadError(`Imagen demasiado grande (${Math.round(file.size / 1024 / 1024)} MB). El límite es 25 MB.`);
      return;
    }
    setUploadError('');
    setSelectedFile(file);
    setHasImg(true);
  };

  const buildPhoto = async (status) => {
    setUploadError('');
    setUploadProgress(0);
    setUploading(true);

    const base = {
      id: `p${Date.now()}`,
      title: form.title || 'Sin titulo',
      desc: form.desc || '',
      cat: form.cat,
      col: form.col,
      status,
      tone,
      o: 'v',
      fav: false,
      date: new Date().toISOString().slice(0, 10),
      alt: form.alt || form.title || '',
    };

    try {
      if (selectedFile && window.MPM_R2?.enabled) {
        setOptimizing(true);
        const { file: optimized, width, height, o: detectedO } = await window.MPM_R2.optimizeImage(selectedFile);
        setOptimizing(false);
        const uploaded = await window.MPM_R2.uploadImage(optimized, p => setUploadProgress(p));
        return {
          ...base,
          o: detectedO || 'v',
          kb: Math.round(optimized.size / 1024),
          res: `${width}x${height}`,
          image_key: uploaded.key,
          image_url: uploaded.url,
        };
      }
      return { ...base, kb: selectedFile ? Math.round(selectedFile.size / 1024) : 0, res: '—', image_key: null, image_url: null };
    } catch (err) {
      setUploadError(err.message || 'Error al subir la imagen');
      return null;
    } finally {
      setUploading(false);
      setOptimizing(false);
      setUploadProgress(0);
    }
  };

  const posOptions = [['tl','Sup. izq.'],['tr','Sup. der.'],['bl','Inf. izq.'],['br','Inf. der.']];
  const styleOptions = [['white','Blanca'],['black','Negra'],['semi','Semitransp.']];

  const fileToDataUrl = (file) => new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve(reader.result);
    reader.onerror = () => reject(new Error('No se pudo leer la firma'));
    reader.readAsDataURL(file);
  });

  const saveSignatureFile = async (file) => {
    if (!file) return;
    setSignatureBusy(true);
    setSignatureError('');
    try {
      const extracted = await window.MPM_R2.extractSignature(file);
      let saved;
      if (window.MPM_R2?.enabled) {
        const uploaded = await window.MPM_R2.uploadImage(extracted.file);
        saved = {
          url: uploaded.url,
          key: uploaded.key,
          width: extracted.width,
          height: extracted.height,
          updated_at: new Date().toISOString(),
        };
      } else {
        saved = {
          url: await fileToDataUrl(extracted.file),
          key: null,
          width: extracted.width,
          height: extracted.height,
          updated_at: new Date().toISOString(),
        };
      }
      URL.revokeObjectURL(extracted.url);
      await onSaveSignature?.(saved);
    } catch (err) {
      setSignatureError(err.message || 'No se pudo procesar la firma');
    } finally {
      setSignatureBusy(false);
      if (signatureInputRef.current) signatureInputRef.current.value = '';
    }
  };

  return (
    <DashLayout go={go} route="upload" title="Subir fotografía" sub="Publica una versión optimizada para web">
      <div className="upload-grid" style={{ display: 'grid', gridTemplateColumns: '1fr 380px', gap: 24, alignItems: 'start' }}>
        {/* Columna izquierda: dropzone + form */}
        <div style={{ display: 'flex', flexDirection: 'column', gap: 20 }}>
          {/* Dropzone */}
          <div
            onDragOver={e => { e.preventDefault(); setDragOver(true); }}
            onDragLeave={() => setDragOver(false)}
            onDrop={e => { e.preventDefault(); setDragOver(false); setFile(e.dataTransfer.files?.[0]); }}
            className="card" style={{ padding: 28, border: `2px dashed ${dragOver ? 'var(--rose)' : 'var(--line-2)'}`, background: dragOver ? 'var(--rose-tint)' : 'var(--surface)', transition: 'all .2s', textAlign: 'center', cursor: 'pointer' }}>
            <div style={{ width: 56, height: 56, borderRadius: '50%', background: 'var(--rose-tint)', display: 'grid', placeItems: 'center', margin: '0 auto 14px' }}>
              <Icon name="upload" size={26} style={{ color: 'var(--rose-deep)' }} />
            </div>
            <p style={{ margin: 0, fontSize: 15.5, fontWeight: 700, color: 'var(--ink)' }}>Arrastra tu fotografía aquí</p>
            <p style={{ margin: '6px 0 14px', fontSize: 13, color: 'var(--ink-3)' }}>o haz clic para seleccionar · JPEG / PNG / WebP · máx. 25 MB</p>
            <input ref={fileInputRef} type="file" accept="image/*" style={{ display: 'none' }} onChange={e => setFile(e.target.files?.[0])} />
            <button className="btn btn-soft btn-sm" type="button" onClick={() => fileInputRef.current?.click()}>Seleccionar archivo</button>
            {selectedFile && <p style={{ margin: '10px 0 0', fontSize: 12.5, color: 'var(--green-deep)' }}>{selectedFile.name} · {Math.round(selectedFile.size / 1024)} KB</p>}
          </div>

          {/* Aviso */}
          <div style={{ display: 'flex', gap: 12, padding: '14px 16px', background: 'var(--sand-tint)', borderRadius: 'var(--r-md)', border: '1px solid var(--sand-soft)' }}>
            <Icon name="info" size={18} style={{ color: 'var(--draft)', flexShrink: 0, marginTop: 1 }} />
            <p style={{ margin: 0, fontSize: 13, color: 'var(--ink-2)', lineHeight: 1.55 }}>
              Publica una versión <strong>optimizada para web</strong> (WebP, máx. 2400 px). Conserva el original en disco, Drive o iCloud.
            </p>
          </div>

          {/* Campos */}
          <div className="card" style={{ padding: 24 }}>
            <div style={{ marginBottom: 16 }}>
              <label className="field-label">Título</label>
              <input className="input" placeholder="Ej. Luz de la tarde" value={form.title} onChange={e => set('title', e.target.value)} />
            </div>
            <div style={{ marginBottom: 16 }}>
              <label className="field-label">Descripción</label>
              <textarea className="textarea" placeholder="Describe el momento, la luz o la intención…" value={form.desc} onChange={e => set('desc', e.target.value)} />
            </div>
            <div style={{ marginBottom: 16 }}>
              <label className="field-label">Texto alternativo</label>
              <input className="input" placeholder="Describe la imagen para lectores de pantalla" value={form.alt} onChange={e => set('alt', e.target.value)} />
            </div>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14, marginBottom: 16 }}>
              <div>
                <label className="field-label">Categoría</label>
                <select className="select" value={form.cat} onChange={e => set('cat', e.target.value)}>
                  {window.CATEGORIES.map(c => <option key={c.id} value={c.id}>{c.label}</option>)}
                </select>
              </div>
              <div>
                <label className="field-label">Colección</label>
                <select className="select" value={form.col} onChange={e => set('col', e.target.value)}>
                  {window.COLLECTIONS.map(c => <option key={c} value={c}>{c}</option>)}
                </select>
              </div>
            </div>
            <div>
              <label className="field-label">Estado</label>
              <div style={{ display: 'flex', gap: 10 }}>
                {[['published','Publicada','check'],['draft','Borrador','edit']].map(([id,l,ic]) => (
                  <button key={id} onClick={() => set('status', id)} style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8, padding: '12px', borderRadius: 'var(--r-sm)', fontSize: 14, fontWeight: 600, transition: 'all .2s',
                    border: `1.5px solid ${form.status===id ? 'var(--rose)' : 'var(--line-2)'}`, background: form.status===id ? 'var(--rose-tint)' : 'var(--surface)', color: form.status===id ? 'var(--rose-deep)' : 'var(--ink-2)' }}>
                    <Icon name={ic} size={16} /> {l}
                  </button>
                ))}
              </div>
            </div>
          </div>
        </div>

        {/* Columna derecha: preview + marca de agua + estado */}
        <div style={{ position: 'sticky', top: 92, display: 'flex', flexDirection: 'column', gap: 16 }}>
          <div className="card" style={{ padding: 18 }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 }}>
              <h3 style={{ margin: 0, fontSize: 14, fontWeight: 700, color: 'var(--ink)' }}>Vista previa</h3>
              <span className="badge badge-temp">temporal</span>
            </div>
            {hasImg
              ? <PhotoFrame photo={displayPhoto} ratio="4/5" label temp={false} wmPos={wmPos} wmStyle={wmStyle} rounded="var(--r-md)" />
              : <div className="skeleton" style={{ aspectRatio: '4/5', borderRadius: 'var(--r-md)' }} />}
            <p style={{ margin: '12px 0 0', fontSize: 12, color: 'var(--ink-3)', textAlign: 'center', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 6 }}>
              <Icon name="info" size={13} /> Así se verá la marca de agua en la web
            </p>
          </div>

          {/* Marca de agua */}
          <div className="card" style={{ padding: 18 }}>
            <h3 style={{ margin: '0 0 12px', fontSize: 14, fontWeight: 700, color: 'var(--ink)' }}>Marca de agua</h3>
            <div style={{ marginBottom: 16, padding: 12, border: '1px solid var(--line)', borderRadius: 'var(--r-sm)', background: 'var(--surface-2)' }}>
              <label className="field-label">Firma personalizada</label>
              <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
                <div style={{ width: 86, height: 42, borderRadius: 'var(--r-sm)', background: '#fff', border: '1px solid var(--line-2)', display: 'grid', placeItems: 'center', overflow: 'hidden', padding: 8 }}>
                  {signature?.url
                    ? <img src={signature.url} alt="Firma actual" style={{ maxWidth: '100%', maxHeight: '100%', objectFit: 'contain' }} />
                    : <Signature text="MP" size={24} color="var(--ink-3)" />}
                </div>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <input ref={signatureInputRef} type="file" accept="image/*" style={{ display: 'none' }} onChange={e => saveSignatureFile(e.target.files?.[0])} />
                  <button className="btn btn-soft btn-sm" type="button" disabled={signatureBusy} onClick={() => signatureInputRef.current?.click()}>
                    <Icon name="upload" size={14} /> {signatureBusy ? 'Procesando...' : 'Subir firma'}
                  </button>
                  <p style={{ margin: '7px 0 0', fontSize: 11.5, color: 'var(--ink-3)', lineHeight: 1.35 }}>Foto en papel blanco. Se recorta y limpia automaticamente.</p>
                </div>
              </div>
              {signatureError && <p style={{ margin: '9px 0 0', fontSize: 12, color: 'var(--danger-text)' }}>{signatureError}</p>}
            </div>
            <label className="field-label">Posición</label>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 7, marginBottom: 16 }}>
              {posOptions.map(([id,l]) => (
                <button key={id} onClick={() => setWmPos(id)} style={{ padding: '9px', fontSize: 12.5, fontWeight: 600, borderRadius: 'var(--r-sm)', transition: 'all .18s',
                  border: `1.5px solid ${wmPos===id ? 'var(--rose)' : 'var(--line-2)'}`, background: wmPos===id ? 'var(--rose-tint)' : 'var(--surface)', color: wmPos===id ? 'var(--rose-deep)' : 'var(--ink-2)' }}>{l}</button>
              ))}
            </div>
            <label className="field-label">Estilo</label>
            <div style={{ display: 'flex', gap: 7 }}>
              {styleOptions.map(([id,l]) => (
                <button key={id} onClick={() => setWmStyle(id)} style={{ flex: 1, padding: '9px 4px', fontSize: 11.5, fontWeight: 600, borderRadius: 'var(--r-sm)', transition: 'all .18s',
                  border: `1.5px solid ${wmStyle===id ? 'var(--rose)' : 'var(--line-2)'}`, background: wmStyle===id ? 'var(--rose-tint)' : 'var(--surface)', color: wmStyle===id ? 'var(--rose-deep)' : 'var(--ink-2)' }}>{l}</button>
              ))}
            </div>
          </div>

          {/* Estado de subida */}
          {(uploadError || optimizing || (uploading && uploadProgress > 0)) && (
            <div className="card" style={{ padding: 16 }}>
              {uploadError && (
                <div style={{ display: 'flex', gap: 8, alignItems: 'flex-start' }}>
                  <Icon name="alert" size={16} style={{ color: '#dc2626', flexShrink: 0, marginTop: 1 }} />
                  <p style={{ margin: 0, fontSize: 13, color: '#dc2626', lineHeight: 1.4 }}>{uploadError}</p>
                </div>
              )}
              {optimizing && (
                <p style={{ margin: 0, fontSize: 13, color: 'var(--rose-deep)' }}>Comprimiendo a WebP…</p>
              )}
              {uploading && !optimizing && uploadProgress > 0 && (
                <>
                  <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 7, fontSize: 12.5, color: 'var(--ink-2)' }}>
                    <span>Subiendo imagen…</span>
                    <span style={{ fontWeight: 700 }}>{uploadProgress}%</span>
                  </div>
                  <div style={{ height: 5, background: 'var(--line-2)', borderRadius: 999 }}>
                    <div style={{ height: '100%', width: '100%', background: 'var(--rose)', borderRadius: 999, transform: `scaleX(${uploadProgress / 100})`, transformOrigin: 'left center', transition: 'transform .3s' }} />
                  </div>
                </>
              )}
            </div>
          )}

          {/* Acciones */}
          <button className="btn btn-primary btn-block btn-lg" disabled={uploading} onClick={() => setPreview(true)}>
            <Icon name="eye" size={17} /> Vista previa y publicar
          </button>
          <button className="btn btn-soft btn-block" disabled={uploading} onClick={async () => {
            const photo = await buildPhoto('draft');
            if (photo) { onPublish(photo, 'Borrador guardado'); go('manage'); }
          }}>
            {uploading ? (optimizing ? 'Optimizando…' : `Subiendo ${uploadProgress}%…`) : 'Guardar como borrador'}
          </button>
        </div>
      </div>

      {preview && (
        <PreviewModal
          form={form}
          photo={displayPhoto}
          wmPos={wmPos}
          wmStyle={wmStyle}
          onClose={() => setPreview(false)}
          onConfirm={async () => {
            setPreview(false);
            const photo = await buildPhoto(form.status);
            if (photo) {
              onPublish(photo, form.status === 'published' ? 'Fotografía publicada' : 'Borrador guardado');
              go('manage');
            }
          }}
        />
      )}
    </DashLayout>
  );
}

/* ——————————————————— Vista previa antes de publicar ——————————————————— */
function PreviewModal({ form, photo, wmPos, wmStyle, onClose, onConfirm }) {
  const [tab, setTab] = React.useState('gallery');
  React.useEffect(() => {
    const scrollY = window.scrollY || window.pageYOffset || 0;
    const prev = { overflow: document.body.style.overflow, position: document.body.style.position, top: document.body.style.top, width: document.body.style.width };
    document.body.style.overflow = 'hidden';
    document.body.style.position = 'fixed';
    document.body.style.top = `-${scrollY}px`;
    document.body.style.width = '100%';
    return () => {
      document.body.style.overflow = prev.overflow;
      document.body.style.position = prev.position;
      document.body.style.top = prev.top;
      document.body.style.width = prev.width;
      window.scrollTo(0, scrollY);
    };
  }, []);
  return (
    <div className="modal-backdrop animate-fadeIn" onClick={onClose} style={{ position: 'fixed', inset: 0, background: 'rgba(44,40,48,.5)', backdropFilter: 'blur(5px)', zIndex: 150, display: 'grid', placeItems: 'center', padding: 20, overflowY: 'auto' }}>
      <div className="animate-scaleIn preview-modal" onClick={e => e.stopPropagation()} style={{ background: 'var(--bg)', borderRadius: 'var(--r-lg)', boxShadow: 'var(--sh-lg)', maxWidth: 760, width: '100%', overflow: 'hidden', margin: 'auto' }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '18px 24px', borderBottom: '1px solid var(--line)', background: 'var(--surface)' }}>
          <div>
            <h3 className="serif" style={{ margin: 0, fontSize: 22, fontWeight: 500, color: 'var(--ink)' }}>Vista previa</h3>
            <p style={{ margin: '2px 0 0', fontSize: 12.5, color: 'var(--ink-3)' }}>Revisa antes de publicar</p>
          </div>
          <button onClick={onClose} style={{ background: 'var(--surface-2)', border: 'none', borderRadius: '50%', width: 36, height: 36, display: 'grid', placeItems: 'center', color: 'var(--ink-2)' }}><Icon name="close" size={18} /></button>
        </div>

        {/* Tabs */}
        <div style={{ display: 'flex', gap: 6, padding: '14px 24px 0' }}>
          {[['gallery','En galería'],['detail','En detalle']].map(([id,l]) => (
            <button key={id} onClick={() => setTab(id)} style={{ border: 'none', borderRadius: 999, padding: '8px 16px', fontSize: 13, fontWeight: 600, background: tab===id ? 'var(--ink)' : 'var(--surface-2)', color: tab===id ? '#fff' : 'var(--ink-2)' }}>{l}</button>
          ))}
        </div>

        <div style={{ padding: 24, maxHeight: '52vh', overflowY: 'auto' }}>
          {tab === 'gallery' ? (
            <div style={{ maxWidth: 280, margin: '0 auto' }}>
              <PhotoFrame photo={photo} ratio="4/5" temp={false} wmPos={wmPos} wmStyle={wmStyle} rounded="var(--r-md)" hover />
              <div style={{ padding: '12px 4px 0', display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
                <span className="serif" style={{ fontSize: 18, fontStyle: 'italic' }}>{form.title || 'Sin título'}</span>
                <span style={{ fontSize: 11, color: 'var(--ink-3)', textTransform: 'uppercase', letterSpacing: '.1em' }}>{catLabel(form.cat)}</span>
              </div>
            </div>
          ) : (
            <div style={{ display: 'grid', gridTemplateColumns: '1.3fr 1fr', gap: 24, alignItems: 'start' }}>
              <PhotoFrame photo={photo} ratio="4/5" temp={false} wmPos={wmPos} wmStyle={wmStyle} rounded="var(--r-md)" />
              <div>
                <span className="badge" style={{ background: 'var(--rose-soft)', color: 'var(--rose-deep)' }}>{catLabel(form.cat)}</span>
                <h2 className="serif" style={{ margin: '12px 0 0', fontSize: 28, fontWeight: 500, color: 'var(--ink)' }}>{form.title || 'Sin título'}</h2>
                <p style={{ margin: '10px 0 0', fontSize: 14, lineHeight: 1.6, color: 'var(--ink-2)' }}>{form.desc || 'Sin descripción.'}</p>
                <div style={{ marginTop: 16, fontSize: 13, color: 'var(--ink-3)' }}>{form.col}</div>
              </div>
            </div>
          )}
        </div>

        {/* Resumen + confirmar */}
        <div className="preview-footer" style={{ borderTop: '1px solid var(--line)', padding: '16px 24px', background: 'var(--surface)', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 16, flexWrap: 'wrap' }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 16, fontSize: 13, color: 'var(--ink-2)' }}>
            <span style={{ display: 'flex', alignItems: 'center', gap: 6 }}>{form.status === 'published' ? <StatusBadge status="published" /> : <StatusBadge status="draft" />}</span>
            <span>Marca: <strong style={{ color: 'var(--ink)' }}>{({tl:'Sup. izq.',tr:'Sup. der.',bl:'Inf. izq.',br:'Inf. der.'})[wmPos]}</strong></span>
          </div>
          <div className="preview-actions" style={{ display: 'flex', gap: 10 }}>
            <button className="btn btn-soft" onClick={onClose}>Seguir editando</button>
            <button className="btn btn-primary" onClick={onConfirm}><Icon name="check" size={16} stroke={2.4} /> Confirmar publicación</button>
          </div>
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { PhotoManagement, Upload, PreviewModal, ConfirmDelete, IconBtn, EditPhotoModal, ReplaceImageModal });
