/* Módulos admin con CRUD — anuncios, eventos, productos, órdenes, etc. */

const AdminEntityShell = ({ theme, title, sub, icon, onAdd, addLabel, children }) => (
  <div className="tecos-page-shell" style={{ padding: 32 }}>
    <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 16, marginBottom: 20, flexWrap: 'wrap' }}>
      <div>
        <h2 style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 32, margin: 0, color: theme.text, letterSpacing: 1 }}>{title}</h2>
        <p style={{ color: theme.textDim, fontSize: 13, margin: '6px 0 0', fontFamily: 'Inter, sans-serif' }}>{sub}</p>
      </div>
      {onAdd && <Btn theme={theme} icon="plus" onClick={onAdd}>{addLabel || 'Agregar'}</Btn>}
    </div>
    {children}
  </div>
);

const SimpleFormModal = ({ open, onClose, theme, title, fields, values, onChange, onSave, saving, error }) => (
  <Modal open={open} onClose={onClose} theme={theme} title={title} width={520}>
    <div style={{ padding: '8px 24px 24px', display: 'grid', gap: 12 }}>
      {fields.map(f => (
        f.type === 'textarea' ? (
          <div key={f.key}>
            <label style={{ fontSize: 11, color: theme.textMute, fontWeight: 700, textTransform: 'uppercase', fontFamily: 'Inter, sans-serif' }}>{f.label}</label>
            <textarea value={values[f.key] || ''} onChange={e => onChange(f.key, e.target.value)} rows={4}
              style={{ width: '100%', marginTop: 6, padding: 12, borderRadius: 8, border: `1px solid ${theme.border}`, background: theme.bgInput, color: theme.text, fontFamily: 'Inter, sans-serif', resize: 'vertical' }} />
          </div>
        ) : f.type === 'select' ? (
          <div key={f.key}>
            <label style={{ fontSize: 11, color: theme.textMute, fontWeight: 700, textTransform: 'uppercase', fontFamily: 'Inter, sans-serif' }}>{f.label}</label>
            <select value={values[f.key] ?? f.default ?? ''} onChange={e => onChange(f.key, e.target.value)}
              style={{ width: '100%', marginTop: 6, padding: 12, borderRadius: 8, border: `1px solid ${theme.border}`, background: theme.bgInput, color: theme.text, fontFamily: 'Inter, sans-serif' }}>
              {(f.options || []).map(o => <option key={o.value} value={o.value}>{o.label}</option>)}
            </select>
          </div>
        ) : (
          <Input key={f.key} theme={theme} label={f.label} type={f.type || 'text'} value={values[f.key] || ''}
            onChange={e => onChange(f.key, e.target.value)} placeholder={f.placeholder} />
        )
      ))}
      {error && <div style={{ color: theme.danger, fontSize: 13 }}>{error}</div>}
    </div>
    <div style={{ padding: '16px 24px', borderTop: `1px solid ${theme.border}`, display: 'flex', justifyContent: 'flex-end', gap: 10 }}>
      <Btn theme={theme} kind="ghost" onClick={onClose}>Cancelar</Btn>
      <Btn theme={theme} icon="check" onClick={onSave} disabled={saving}>{saving ? 'Guardando…' : 'Guardar'}</Btn>
    </div>
  </Modal>
);

function useAdminList (loader, deps = []) {
  const { data, loading, error, needsAuth, reload } = useLiveData(loader, deps);
  return { rows: data, loading, error, needsAuth, reload };
}

const RowActions = ({ theme, onEdit, onDelete }) => (
  <div style={{ display: 'flex', gap: 4 }}>
    {onEdit && <button onClick={onEdit} style={iconBtnSmall(theme)} title="Editar"><Icon name="edit" size={14} /></button>}
    {onDelete && <button onClick={onDelete} style={iconBtnSmall(theme, theme.danger)} title="Eliminar"><Icon name="trash" size={14} /></button>}
  </div>
);

/* Anuncios: admin-announcements.jsx */


/* Eventos: admin-events.jsx */


/* Inventario: admin-product-inventory.jsx */

/* Entrenadores: admin-coach-form.jsx */

/* Galería: admin-gallery-albums.jsx */


/* ——— INTERESADOS (landing → Supabase) ——— */
const LEAD_STATUS_OPTS = [
  { value: 'nuevo', label: 'Nuevo', color: 'info' },
  { value: 'contactado', label: 'Contactado', color: 'warning' },
  { value: 'inscrito', label: 'Inscrito', color: 'success' },
  { value: 'descartado', label: 'Descartado', color: 'muted' },
];

const InteresadoDetailModal = ({ open, onClose, theme, lead, onSaved }) => {
  const [status, setStatus] = React.useState('nuevo');
  const [notes, setNotes] = React.useState('');
  const [saving, setSaving] = React.useState(false);
  const [err, setErr] = React.useState('');

  React.useEffect(() => {
    if (!open || !lead) return;
    setStatus(lead.status || 'nuevo');
    setNotes(lead.admin_notes || '');
    setErr('');
  }, [open, lead?.id]);

  if (!lead) return null;

  const leadWaMessage = lead.name
    ? `Hola ${lead.name}, gracias por su interés en Tecos Elite VOLLEYBALL. ¿En qué podemos ayudarle?`
    : 'Hola, gracias por su interés en Tecos Elite VOLLEYBALL.';
  const waUrl = lead.phone && lead.phone !== '—' && typeof buildWhatsAppUrl === 'function'
    ? buildWhatsAppUrl(lead.phone, leadWaMessage)
    : (lead.phone && lead.phone !== '—'
      ? `https://wa.me/${String(lead.phone).replace(/\D/g, '')}?text=${encodeURIComponent(leadWaMessage)}`
      : null);

  const save = async () => {
    setSaving(true); setErr('');
    try {
      await updateInterestedLead(lead.id, { status, admin_notes: notes });
      notifyLeadsChanged();
      onSaved?.();
      onClose();
    } catch (e) { setErr(e.message); }
    setSaving(false);
  };

  return (
    <Modal open={open} onClose={onClose} theme={theme} title="Interesado" width={560}>
      <div style={{ padding: 24, display: 'grid', gap: 14 }}>
        <div>
          <div>
            <div style={{ fontWeight: 700, fontSize: 18, color: theme.text }}>{lead.name}</div>
            <div style={{ fontSize: 12, color: theme.textMute, marginTop: 4 }}>{lead.createdLabel} · {lead.contextLabel || lead.source}</div>
          </div>
          <StatusPill status={lead.status} theme={theme} />
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
          {[
            ['Teléfono', lead.phone],
            ['Correo', lead.email],
            ['Alumno', lead.studentName],
            ['Edad', lead.studentAge],
          ].map(([l, v]) => (
            <div key={l} style={{ padding: 10, borderRadius: 8, background: theme.bgInput, border: `1px solid ${theme.border}` }}>
              <div style={{ fontSize: 10, color: theme.textMute, fontWeight: 700, textTransform: 'uppercase' }}>{l}</div>
              <div style={{ fontSize: 13, color: theme.text, fontWeight: 600, marginTop: 4 }}>{v}</div>
            </div>
          ))}
        </div>
        {(lead.announcementTitle || lead.eventTitle) && (
          <div style={{ padding: 12, borderRadius: 8, background: `${theme.primary}12`, border: `1px solid ${theme.primary}33` }}>
            <div style={{ fontSize: 10, color: theme.textMute, fontWeight: 700, textTransform: 'uppercase' }}>Vinculado a</div>
            <div style={{ fontSize: 15, fontWeight: 700, color: theme.text, marginTop: 6 }}>
              {lead.announcementTitle || lead.eventTitle}
            </div>
          </div>
        )}
        {lead.message && (
          <div style={{ padding: 12, borderRadius: 8, background: theme.bgInput, border: `1px solid ${theme.border}` }}>
            <div>Mensaje</div>
            <p style={{ fontSize: 13, color: theme.text, margin: '8px 0 0', whiteSpace: 'pre-wrap' }}>{lead.message}</p>
          </div>
        )}
        <div>
          <label style={{ fontSize: 11, color: theme.textMute, fontWeight: 700, textTransform: 'uppercase' }}>Estado de seguimiento</label>
          <select value={status} onChange={e => setStatus(e.target.value)}
            style={{ width: '100%', marginTop: 6, padding: 12, borderRadius: 8, border: `1px solid ${theme.border}`, background: theme.bgInput, color: theme.text }}>
            {LEAD_STATUS_OPTS.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}
          </select>
        </div>
        <div>
          <label style={{ fontSize: 11, color: theme.textMute, fontWeight: 700, textTransform: 'uppercase' }}>Notas internas</label>
          <textarea value={notes} onChange={e => setNotes(e.target.value)} rows={3} placeholder="Llamada, visita, próximo paso…"
            style={{ width: '100%', marginTop: 6, padding: 12, borderRadius: 8, border: `1px solid ${theme.border}`, background: theme.bgInput, color: theme.text, fontFamily: 'Inter, sans-serif', resize: 'vertical' }} />
        </div>
        {err && <div style={{ color: theme.danger, fontSize: 13 }}>{err}</div>}
      </div>
      <div style={{ padding: '16px 24px', borderTop: `1px solid ${theme.border}`, display: 'flex', gap: 8, justifyContent: 'flex-end', flexWrap: 'wrap' }}>
        <Btn theme={theme} kind="ghost" onClick={onClose}>Cerrar</Btn>
        {waUrl && (
          <Btn theme={theme} kind="wa" icon="wa" onClick={() => {
            if (typeof openWhatsAppChat === 'function') openWhatsAppChat(lead.phone, leadWaMessage);
            else window.open(waUrl, '_blank', 'noopener,noreferrer');
          }}>WhatsApp</Btn>
        )}
        <Btn theme={theme} icon="check" onClick={save} disabled={saving}>{saving ? 'Guardando…' : 'Guardar'}</Btn>
      </div>
    </Modal>
  );
};

const InteresadosAdminModule = ({ theme }) => {
  const { rows, loading, error, needsAuth, reload } = useAdminList(() => fetchInterestedLeads(), []);
  const [search, setSearch] = React.useState('');
  const [statusFilter, setStatusFilter] = React.useState('todos');
  const [detail, setDetail] = React.useState(null);
  const [pendingFocusId, setPendingFocusId] = React.useState(null);
  const [exportBusy, setExportBusy] = React.useState(false);

  const leadFilterLabels = {
    todos: 'Todos',
    nuevo: 'Nuevos',
    contactado: 'Contactados',
    inscrito: 'Inscritos',
    descartado: 'Descartados',
  };
  const leadFilterLabel = leadFilterLabels[statusFilter] || statusFilter;

  React.useEffect(() => {
    const onChange = () => reload();
    window.addEventListener('tecos:leads-changed', onChange);
    return () => window.removeEventListener('tecos:leads-changed', onChange);
  }, [reload]);

  React.useEffect(() => {
    const onFocus = (e) => {
      const d = e.detail;
      if (!d || d.view !== 'interesados' || !d.entityId) return;
      setPendingFocusId(d.entityId);
    };
    window.addEventListener('tecos:admin-focus-entity', onFocus);
    return () => window.removeEventListener('tecos:admin-focus-entity', onFocus);
  }, []);

  React.useEffect(() => {
    if (!pendingFocusId || loading) return;
    const row = (rows || []).find(r => r.id === pendingFocusId);
    if (row) {
      setDetail(row);
      setPendingFocusId(null);
    }
  }, [pendingFocusId, rows, loading]);

  React.useEffect(() => {
    if (!isSupabaseReady() || needsAuth) return;
    const sb = getSupabase();
    const ch = sb.channel('interested-leads-admin')
      .on('postgres_changes', { event: '*', schema: 'public', table: 'interested_leads' }, () => reload())
      .subscribe();
    return () => { sb.removeChannel(ch); };
  }, [needsAuth, reload]);

  const q = search.trim().toLowerCase();
  const filtered = (rows || []).filter(r => {
    if (statusFilter !== 'todos' && r.status !== statusFilter) return false;
    if (!q) return true;
    const hay = [r.name, r.phone, r.email, r.studentName, r.studentAge, r.message, r.source, r.contextLabel, r.announcementTitle, r.eventTitle].join(' ').toLowerCase();
    return hay.includes(q);
  });
  const countStatus = (s) => (rows || []).filter(r => r.status === s).length;
  const leadGroups = React.useMemo(() => (typeof groupInterestedLeads === 'function' ? groupInterestedLeads(filtered) : [{ key: 'all', kind: 'landing', title: 'Todos', icon: 'form', leads: filtered }]), [filtered]);

  const quickStatus = async (lead, newStatus, e) => {
    e?.stopPropagation?.();
    try {
      await updateInterestedLead(lead.id, { status: newStatus });
      notifyLeadsChanged();
      reload();
    } catch (err) { alert(err.message); }
  };

  const waLead = (lead, e) => {
    e?.stopPropagation?.();
    if (!lead.phone || lead.phone === '—') {
      alert('Sin teléfono registrado.');
      return;
    }
    const msg = lead.name
      ? `Hola ${lead.name}, gracias por su interés en Tecos Elite VOLLEYBALL. ¿En qué podemos ayudarle?`
      : 'Hola, gracias por su interés en Tecos Elite VOLLEYBALL.';
    if (typeof openWhatsAppChat === 'function') {
      openWhatsAppChat(lead.phone, msg);
      return;
    }
    const url = typeof buildWhatsAppUrl === 'function'
      ? buildWhatsAppUrl(lead.phone, msg)
      : `https://wa.me/${String(lead.phone).replace(/\D/g, '')}`;
    window.open(url, '_blank', 'noopener,noreferrer');
  };

  return (
    <AdminEntityShell theme={theme} title="INTERESADOS" sub="Contacto web, avisos y eventos — agrupados por origen con las personas interesadas." icon="form">
      <Card theme={theme} style={{ padding: 14, marginBottom: 16, display: 'flex', flexWrap: 'wrap', gap: 12, alignItems: 'center', fontFamily: 'Inter, sans-serif', fontSize: 12 }}>
        <span style={{ color: theme.textDim }}>Enlazado con la landing:</span>
        <button type="button" style={{ background: 'none', border: 'none', color: theme.primary, fontWeight: 600, cursor: 'pointer', fontFamily: 'inherit', fontSize: 'inherit', padding: 0 }}
          onClick={() => window.__goToPublicSection?.('contacto')}>
          Abrir formulario en la landing →
        </button>
        <span style={{ color: theme.textMute }}>|</span>
        <span><strong style={{ color: theme.primary }}>{countStatus('nuevo')}</strong> nuevos</span>
        <span><strong>{rows.length}</strong> total</span>
      </Card>

      <div style={{ display: 'flex', gap: 10, marginBottom: 14, flexWrap: 'wrap', alignItems: 'center' }}>
        <div style={{ flex: 1, minWidth: 200 }}>
          <Input theme={theme} placeholder="Buscar nombre, teléfono, alumno…" icon="search" value={search} onChange={e => setSearch(e.target.value)} />
        </div>
        <Btn theme={theme} kind="ghost" icon="download" size="sm" disabled={!filtered.length || exportBusy}
          onClick={() => exportInterestedLeadsCsv(filtered)}>CSV</Btn>
        <Btn theme={theme} kind="ghost" icon="download" size="sm" disabled={!filtered.length || exportBusy}
          onClick={async () => {
            setExportBusy(true);
            try { await exportInterestedLeadsExcel(filtered, leadFilterLabel); } catch (e) { alert(e.message); }
            setExportBusy(false);
          }}>Excel</Btn>
        <Btn theme={theme} kind="soft" icon="doc" size="sm" disabled={!filtered.length || exportBusy}
          onClick={async () => {
            setExportBusy(true);
            try { await exportInterestedLeadsPdf(filtered, leadFilterLabel, theme); } catch (e) { alert(e.message); }
            setExportBusy(false);
          }}>
          {exportBusy ? 'PDF…' : 'PDF + gráficas'}
        </Btn>
        <Btn theme={theme} kind="ghost" size="sm" onClick={reload}>Actualizar</Btn>
      </div>

      <div style={{ display: 'flex', gap: 6, marginBottom: 14, flexWrap: 'wrap' }}>
        {[{ id: 'todos', l: 'Todos', n: rows.length }, ...LEAD_STATUS_OPTS.map(o => ({ id: o.value, l: o.label, n: countStatus(o.value) }))].map(f => (
          <button type="button" key={f.id} onClick={() => setStatusFilter(f.id)} style={{
            padding: '8px 14px', borderRadius: 999, cursor: 'pointer', fontFamily: 'Inter, sans-serif', fontSize: 12, fontWeight: 600,
            border: `1px solid ${statusFilter === f.id ? theme.primary : theme.border}`,
            background: statusFilter === f.id ? `${theme.primary}22` : theme.bgInput,
            color: statusFilter === f.id ? theme.primary : theme.textDim,
          }}>{f.l} ({f.n})</button>
        ))}
      </div>

      {needsAuth ? <AuthRequired theme={theme} onRetry={reload} /> : loading ? <LoadingBlock theme={theme} /> : error ? (
        <EmptyState theme={theme} title="Error" message={error} icon="warning" action={<Btn theme={theme} size="sm" onClick={reload}>Reintentar</Btn>} />
      ) : filtered.length === 0 ? (
        <EmptyState theme={theme}
          title={rows.length === 0 ? 'Aún no hay solicitudes' : 'Sin resultados'}
          message={rows.length === 0
            ? 'Cuando alguien se registre desde un aviso, evento o el formulario de contacto, aparecerá aquí.'
            : 'Prueba otra búsqueda o filtro.'}
          icon="form"
        />
      ) : (
        <div style={{ display: 'grid', gap: 16 }}>
          {leadGroups.map(group => (
            <Card key={group.key} theme={theme} style={{ padding: 0, overflow: 'hidden' }}>
              <div style={{
                padding: '14px 16px', display: 'flex', alignItems: 'center', gap: 10,
                background: theme.bgInput, borderBottom: `1px solid ${theme.border}`,
                fontFamily: 'Inter, sans-serif',
              }}>
                <div style={{
                  width: 36, height: 36, borderRadius: 8, display: 'grid', placeItems: 'center',
                  background: `${theme.primary}22`, color: theme.primary,
                }}><Icon name={group.icon} size={18} /></div>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontWeight: 700, fontSize: 15, color: theme.text }}>{group.title}</div>
                  <div style={{ fontSize: 12, color: theme.textDim, marginTop: 2 }}>
                    {group.kind === 'anuncio' ? 'No alumnos desde aviso' : group.kind === 'evento' ? 'No alumnos desde evento' : 'Formulario de contacto'}
                    {' · '}{group.leads.length} persona{group.leads.length === 1 ? '' : 's'}
                  </div>
                </div>
              </div>
              <div className="tecos-table-scroll" style={{ overflowX: 'auto' }}>
                <table style={{ width: '100%', borderCollapse: 'collapse', fontFamily: 'Inter, sans-serif' }}>
                  <thead>
                    <tr style={{ borderBottom: `1px solid ${theme.border}` }}>
                      {['Fecha', 'Nombre', 'Teléfono', 'Estado', 'Acciones'].map(h => (
                        <th key={h} style={{ padding: '10px 14px', textAlign: 'left', fontSize: 10, fontWeight: 700, color: theme.textMute, letterSpacing: 1, textTransform: 'uppercase' }}>{h}</th>
                      ))}
                    </tr>
                  </thead>
                  <tbody>
                    {group.leads.map((r, i) => (
                      <tr key={r.id} style={{ borderBottom: i < group.leads.length - 1 ? `1px solid ${theme.border}` : 'none', cursor: 'pointer' }}
                        onClick={() => setDetail(r)}
                        onMouseEnter={e => e.currentTarget.style.background = theme.bgInput}
                        onMouseLeave={e => e.currentTarget.style.background = 'transparent'}
                      >
                        <td style={{ padding: '12px 14px', fontSize: 12, color: theme.textDim }}>{r.createdLabel}</td>
                        <td style={{ padding: '12px 14px', fontWeight: 600, color: theme.text }}>{r.name}</td>
                        <td style={{ padding: '12px 14px', fontSize: 13, color: theme.text }}>{r.phone}</td>
                        <td style={{ padding: '12px 14px' }}><StatusPill status={r.status} theme={theme} /></td>
                        <td style={{ padding: '12px 14px' }} onClick={e => e.stopPropagation()}>
                          <div style={{ display: 'flex', gap: 4 }}>
                            <button type="button" title="Ver detalle" onClick={() => setDetail(r)} style={iconBtnSmall(theme)}><Icon name="eye" size={14} /></button>
                            <button type="button" title="WhatsApp" onClick={(e) => waLead(r, e)} style={iconBtnSmall(theme, theme.wa)}><Icon name="wa" size={14} /></button>
                            {r.status === 'nuevo' && (
                              <button type="button" title="Marcar contactado" onClick={(e) => quickStatus(r, 'contactado', e)} style={iconBtnSmall(theme, theme.success)}><Icon name="check" size={14} /></button>
                            )}
                            <button type="button" title="Eliminar" onClick={async (e) => {
                              e.stopPropagation();
                              if (!confirm(`¿Eliminar solicitud de ${r.name}?`)) return;
                              try { await deleteInterestedLead(r.id); notifyLeadsChanged(); reload(); } catch (err) { alert(err.message); }
                            }} style={iconBtnSmall(theme, theme.danger)}><Icon name="trash" size={14} /></button>
                          </div>
                        </td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              </div>
            </Card>
          ))}
        </div>
      )}

      <InteresadoDetailModal open={!!detail} onClose={() => setDetail(null)} theme={theme} lead={detail} onSaved={reload} />
    </AdminEntityShell>
  );
};



/* Config: admin-config-module.jsx */

const UbicacionAdminModule = ({ theme }) => {
  const { data: settings, loading, error, needsAuth, reload } = useLiveData(() => fetchAcademySettings(), []);
  const [form, setForm] = React.useState({ ...DEFAULT_ACADEMY_SETTINGS });
  const [saving, setSaving] = React.useState(false);
  const [ok, setOk] = React.useState('');
  const [formErr, setFormErr] = React.useState('');
  const [uploadingMarker, setUploadingMarker] = React.useState(false);
  const [markerPreviewBlob, setMarkerPreviewBlob] = React.useState(null);
  const [markerCacheKey, setMarkerCacheKey] = React.useState(0);
  const markerRef = React.useRef(null);

  React.useEffect(() => {
    if (settings) setForm(normalizeAcademySettings(settings));
  }, [settings]);

  React.useEffect(() => () => {
    if (markerPreviewBlob) URL.revokeObjectURL(markerPreviewBlob);
  }, [markerPreviewBlob]);

  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));
  const previewLoc = normalizeAcademySettings(form);
  const markerPreview = markerPreviewBlob
    || getLocationMarkerUrl(previewLoc, `${previewLoc.map_marker_image_path || ''}-${markerCacheKey}`);

  const save = async () => {
    setSaving(true);
    setFormErr('');
    setOk('');
    try {
      await saveAcademySettings(form);
      notifySettingsChanged();
      setOk('Ubicación guardada. La landing se actualiza al instante.');
      reload();
    } catch (e) {
      setFormErr(e.message || 'No se pudo guardar');
    }
    setSaving(false);
  };

  const onMarkerFile = async (e) => {
    const input = e.target;
    const file = input?.files?.[0];
    if (!file) return;
    if (uploadingMarker) return;

    setUploadingMarker(true);
    setFormErr('');
    setOk('');
    let blobUrl = null;
    try {
      blobUrl = URL.createObjectURL(file);
      setMarkerPreviewBlob(blobUrl);
      const prevPath = form.map_marker_image_path;
      const path = await uploadLocationMarkerImage(file, prevPath);
      const nextForm = { ...form, map_marker_image_path: path };
      setForm(nextForm);
      setMarkerCacheKey(Date.now());
      if (blobUrl) URL.revokeObjectURL(blobUrl);
      blobUrl = null;
      setMarkerPreviewBlob(null);
      await saveAcademySettings(nextForm);
      notifySettingsChanged();
      setOk('Imagen del pin actualizada y guardada.');
      reload();
    } catch (err) {
      if (blobUrl) URL.revokeObjectURL(blobUrl);
      setMarkerPreviewBlob(null);
      setFormErr(err.message || 'Error al subir imagen del pin');
    } finally {
      setUploadingMarker(false);
      if (input) input.value = '';
    }
  };

  const field = (label, key, opts = {}) => (
    <div style={{ marginBottom: opts.full ? 0 : 12 }}>
      {label && <label style={{ display: 'block', fontSize: 11, fontWeight: 700, color: theme.textMute, letterSpacing: 1, textTransform: 'uppercase', marginBottom: 6, fontFamily: 'Inter, sans-serif' }}>{label}</label>}
      <input
        value={form[key] ?? ''}
        onChange={e => set(key, e.target.value)}
        placeholder={opts.placeholder}
        type={opts.type || 'text'}
        step={opts.step}
        style={{
          width: '100%', padding: '10px 12px', borderRadius: 8,
          border: `1px solid ${theme.border}`, background: theme.bgInput, color: theme.text,
          fontFamily: 'Inter, sans-serif', fontSize: 14, boxSizing: 'border-box',
        }}
      />
    </div>
  );

  return (
    <AdminEntityShell theme={theme}
      title="UBICACIÓN"
      sub="Sincronizado con la sección Encuéntranos del sitio público (Google Maps + pin personalizado)."
      icon="pin">
      {needsAuth ? <AuthRequired theme={theme} onRetry={reload} /> : loading ? <LoadingBlock theme={theme} /> : error ? (
        <EmptyState theme={theme} title="Error" message={error} action={<Btn theme={theme} size="sm" onClick={reload}>Reintentar</Btn>} />
      ) : (
        <div style={{ display: 'grid', gap: 20 }}>
          {ok && (
            <div style={{ padding: 12, borderRadius: 10, background: `${theme.success}18`, border: `1px solid ${theme.success}55`, color: theme.text, fontSize: 13, fontFamily: 'Inter, sans-serif' }}>{ok}</div>
          )}
          {formErr && (
            <div style={{ padding: 12, borderRadius: 10, background: `${theme.danger}18`, border: `1px solid ${theme.danger}55`, color: theme.danger, fontSize: 13, fontFamily: 'Inter, sans-serif' }}>{formErr}</div>
          )}

          <Card theme={theme} style={{ padding: 20 }}>
            <h3 style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 22, margin: '0 0 16px', color: theme.text, letterSpacing: 1 }}>VISTA PREVIA DEL MAPA</h3>
            <AcademyLocationMap theme={theme} location={previewLoc} height={360} />
            <p style={{ marginTop: 12, fontSize: 12, color: theme.textDim, fontFamily: 'Inter, sans-serif' }}>
              Pin con tu imagen y resplandor neón suave, sin marco (recomendado 128×128 px, PNG o WebP con fondo transparente).
              {!getGoogleMapsApiKey() && ' Sin API key de Google se usa mapa OpenStreetMap; el pin sigue las coordenadas al mover el mapa.'}
            </p>
          </Card>

          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 20 }}>
            <Card theme={theme} style={{ padding: 20 }}>
              <h3 style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 20, margin: '0 0 14px', color: theme.text }}>TEXTO EN LANDING</h3>
              {field('Kicker', 'location_kicker')}
              {field('Título', 'location_title')}
              {field('Subtítulo', 'location_subtitle')}
              {field('Nombre del club', 'venue_name')}
            </Card>

            <Card theme={theme} style={{ padding: 20 }}>
              <h3 style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 20, margin: '0 0 14px', color: theme.text }}>DIRECCIÓN Y MAPA</h3>
              {field('Calle y número', 'address_street')}
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
                {field('Ciudad', 'address_city', { full: true })}
                {field('Estado', 'address_state', { full: true })}
              </div>
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
                {field('C.P.', 'address_postal', { full: true })}
                {field('País', 'address_country', { full: true })}
              </div>
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 80px', gap: 10, marginTop: 12 }}>
                {field('Latitud', 'latitude', { type: 'number', step: 'any', full: true })}
                {field('Longitud', 'longitude', { type: 'number', step: 'any', full: true })}
                {field('Zoom', 'map_zoom', { type: 'number', step: '1', full: true })}
              </div>
              {field('Enlace Google Maps (opcional)', 'google_maps_url', { placeholder: 'https://maps.app.goo.gl/...' })}
              <p style={{ fontSize: 11, color: theme.textMute, margin: '8px 0 0', fontFamily: 'Inter, sans-serif' }}>
                Obtén coordenadas en Google Maps → clic derecho en el lugar → copiar latitud/longitud.
              </p>
            </Card>

            <Card theme={theme} style={{ padding: 20 }}>
              <h3 style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 20, margin: '0 0 14px', color: theme.text }}>HORARIOS Y CONTACTO</h3>
              {field('Entre semana', 'schedule_weekdays')}
              {field('Sábado', 'schedule_saturday')}
              {field('Teléfono', 'contact_phone')}
              {field('Correo', 'contact_email')}
            </Card>

            <Card theme={theme} style={{ padding: 20 }}>
              <h3 style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 20, margin: '0 0 14px', color: theme.text }}>IMAGEN DEL PIN</h3>
              <div style={{ display: 'flex', gap: 16, alignItems: 'center', flexWrap: 'wrap' }}>
                <div style={{
                  width: 72, height: 72, borderRadius: 8, overflow: 'hidden',
                  background: theme.bgInput, display: 'grid', placeItems: 'center',
                }}>
                  {markerPreview ? (
                    <img src={markerPreview} alt="" className="tecos-map-marker-neon" style={{ width: '100%', height: '100%', objectFit: 'contain' }} />
                  ) : (
                    <Icon name="pin" size={28} style={{ color: theme.primary }} />
                  )}
                </div>
                <div>
                  <input
                    id="tecos-marker-file"
                    ref={markerRef}
                    type="file"
                    accept="image/jpeg,image/png,image/webp"
                    style={{ position: 'absolute', width: 1, height: 1, opacity: 0, pointerEvents: uploadingMarker ? 'none' : 'auto' }}
                    onChange={onMarkerFile}
                  />
                  <label
                    htmlFor="tecos-marker-file"
                    style={{
                      display: 'inline-flex', alignItems: 'center', gap: 6,
                      padding: '7px 12px', borderRadius: 10, fontSize: 12, fontWeight: 600,
                      fontFamily: 'Inter, sans-serif', cursor: uploadingMarker ? 'wait' : 'pointer',
                      background: theme.bgInput, color: theme.text,
                      border: `1px solid ${theme.border}`,
                      opacity: uploadingMarker ? 0.65 : 1,
                    }}
                  >
                    <Icon name="upload" size={16} />
                    {uploadingMarker ? 'Subiendo…' : 'Cambiar imagen del pin'}
                  </label>
                  {form.map_marker_image_path && (
                    <button type="button" disabled={uploadingMarker} onClick={async () => {
                      setFormErr('');
                      setOk('');
                      try {
                        const nextForm = { ...form, map_marker_image_path: '' };
                        setForm(nextForm);
                        setMarkerCacheKey(Date.now());
                        setMarkerPreviewBlob(null);
                        await saveAcademySettings(nextForm);
                        notifySettingsChanged();
                        setOk('Imagen del pin quitada.');
                        reload();
                      } catch (err) {
                        setFormErr(err.message || 'No se pudo quitar la imagen');
                      }
                    }} style={{
                      display: 'block', marginTop: 8, background: 'none', border: 'none',
                      color: theme.danger, fontSize: 12, cursor: uploadingMarker ? 'not-allowed' : 'pointer',
                      fontFamily: 'Inter, sans-serif', opacity: uploadingMarker ? 0.5 : 1,
                    }}>Quitar imagen</button>
                  )}
                  <p style={{ margin: '8px 0 0', fontSize: 11, color: theme.textMute, fontFamily: 'Inter, sans-serif' }}>
                    JPG, PNG o WebP · máx. 5 MB. Se guarda al subir (reemplaza la anterior).
                  </p>
                </div>
              </div>
            </Card>
          </div>

          <div style={{ display: 'flex', gap: 12, flexWrap: 'wrap' }}>
            <Btn theme={theme} icon="check" onClick={save} disabled={saving}>{saving ? 'Guardando…' : 'Guardar ubicación'}</Btn>
            <Btn theme={theme} kind="ghost" onClick={() => window.__goToPublicSection?.('ubicacion')}>Ver en landing</Btn>
          </div>
        </div>
      )}
    </AdminEntityShell>
  );
};

function renderAdminEntityView (view, theme) {
  const map = {
    anuncios: <AnunciosAdminModule theme={theme} />,
    eventos: <EventosAdminModule theme={theme} />,
    inventario: <ProductInventoryModule theme={theme} />,
    'tienda-admin': <ProductTiendaAdminModule theme={theme} />,
    'nomina-gastos': typeof NominaGastosAdminModule === 'function'
      ? <NominaGastosAdminModule theme={theme} />
      : <EmptyState theme={theme} title="Nómina y gastos" message="Carga admin-nomina-gastos.jsx" icon="money" />,
    'galeria-admin': <GaleriaAlbumsAdminModule theme={theme} />,
    'entrenadores-admin': <EntrenadoresAdminModule theme={theme} />,
    interesados: <InteresadosAdminModule theme={theme} />,
    ubicacion: <UbicacionAdminModule theme={theme} />,
    config: <ConfigAdminModule theme={theme} />,
  };
  return map[view] || null;
}

Object.assign(window, { renderAdminEntityView });
