/* Admin modules: Alumnos, Pagos, WhatsApp, plus stubs for other modules */

const AlumnosModule = ({ theme, onWhatsApp }) => {
  const [selected, setSelected] = React.useState(null);
  const [showAdd, setShowAdd] = React.useState(false);
  const [editing, setEditing] = React.useState(null);
  const [search, setSearch] = React.useState('');
  const [statusFilter, setStatusFilter] = React.useState('todos');
  const [showFilters, setShowFilters] = React.useState(false);
  const [exportBusy, setExportBusy] = React.useState(false);
  const { data: alumnos, loading, error, needsAuth, reload } = useLiveData(
    () => fetchStudentsAdmin(),
    []
  );
  const { data: docCounts, reload: reloadDocCounts } = useLiveData(
    () => (typeof fetchStudentsDocumentCounts === 'function'
      ? fetchStudentsDocumentCounts()
      : Promise.resolve({})),
    []
  );
  const docCatalogTotal = (typeof STUDENT_DOCUMENT_TYPES !== 'undefined' ? STUDENT_DOCUMENT_TYPES.length : 10);
  const [pendingFocusId, setPendingFocusId] = React.useState(null);

  const onDocChange = React.useCallback(() => {
    reloadDocCounts();
  }, [reloadDocCounts]);

  React.useEffect(() => {
    const onChange = () => reload();
    window.addEventListener('tecos:academy-data-sync', onChange);
    window.addEventListener('tecos:payments-changed', onChange);
    window.addEventListener('tecos:comprobantes-changed', onChange);
    window.addEventListener('tecos:students-changed', onChange);
    window.addEventListener('tecos:settings-changed', onChange);
    window.addEventListener('tecos:student-documents-changed', onDocChange);
    return () => {
      window.removeEventListener('tecos:academy-data-sync', onChange);
      window.removeEventListener('tecos:payments-changed', onChange);
      window.removeEventListener('tecos:comprobantes-changed', onChange);
      window.removeEventListener('tecos:students-changed', onChange);
      window.removeEventListener('tecos:settings-changed', onChange);
      window.removeEventListener('tecos:student-documents-changed', onDocChange);
    };
  }, [reload, onDocChange]);

  React.useEffect(() => {
    const onFocus = (e) => {
      const d = e.detail;
      if (!d || d.view !== 'alumnos') return;
      if (d.searchQuery) setSearch(String(d.searchQuery));
      if (d.statusFilter || d.alumnosFilter) {
        setStatusFilter(String(d.statusFilter || d.alumnosFilter));
        setShowFilters(true);
      }
      if (d.entityId) 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 = (alumnos || []).find(a => a._uuid === pendingFocusId);
    if (row) {
      setSelected(row);
      setPendingFocusId(null);
    }
  }, [pendingFocusId, alumnos, loading]);

  const realAlumnos = (alumnos || []).filter(a => !isDemoStudentCode(a.id));
  const q = search.trim().toLowerCase();
  const filtered = realAlumnos.filter(a => {
    if (statusFilter === 'activo' && a.status !== 'activo') return false;
    if (statusFilter === 'inactivo' && a.status === 'activo') return false;
    if (statusFilter === 'adeudo' && !(a.adeudo > 0)) return false;
    if (!q) return true;
    const hay = [a.name, a.id, a.tutor, a.phone, a.email, a.cat].join(' ').toLowerCase();
    return hay.includes(q);
  });

  const now = new Date();
  const nuevosMes = realAlumnos.filter(a => {
    if (!a.joined_at) return false;
    const j = new Date(a.joined_at);
    return j.getMonth() === now.getMonth() && j.getFullYear() === now.getFullYear();
  }).length;

  const activos = realAlumnos.filter(a => a.status === 'activo').length;
  const inactivos = realAlumnos.length - activos;
  const conAdeudo = realAlumnos.filter(a => a.adeudo > 0).length;
  const demoCount = (alumnos || []).filter(a => isDemoStudentCode(a.id)).length;
  const exportFilterLabel = typeof studentsFilterLabel === 'function'
    ? studentsFilterLabel(statusFilter)
    : statusFilter;

  const openEdit = (a) => {
    setSelected(null);
    setEditing(a);
  };

  const removeDemos = async () => {
    const demos = (alumnos || []).filter(a => isDemoStudentCode(a.id));
    if (!demos.length) { alert('No hay cuentas demo en la base de datos.'); return; }
    if (!confirm(`¿Eliminar ${demos.length} cuenta(s) demo (${demos.map(d => d.id).join(', ')})?`)) return;
    try {
      for (const d of demos) await deleteStudent(d._uuid);
      reload();
    } catch (e) { alert(e.message); }
  };

  return (
    <div className="tecos-page-shell" style={{ padding: 32 }}>
      <div style={{ display: 'flex', gap: 12, marginBottom: 18, alignItems: 'center', flexWrap: 'wrap' }}>
        <div style={{ flex: 1, minWidth: 240 }}>
          <Input theme={theme} placeholder="Buscar por nombre, ID, tutor..." icon="search"
            value={search} onChange={e => setSearch(e.target.value)} />
        </div>
        <Btn theme={theme} kind={showFilters ? 'primary' : 'ghost'} icon="filter" size="md"
          onClick={() => setShowFilters(v => !v)}>Filtros</Btn>
        <Btn theme={theme} kind="ghost" icon="download" size="md"
          onClick={() => exportStudentsCsv(filtered)} disabled={!filtered.length || exportBusy}>CSV</Btn>
        <Btn theme={theme} kind="ghost" icon="download" size="md" disabled={!filtered.length || exportBusy}
          onClick={async () => {
            setExportBusy(true);
            try { await exportStudentsExcel(filtered, exportFilterLabel, theme); } catch (e) { alert(e.message); }
            setExportBusy(false);
          }}>
          {exportBusy ? 'Excel…' : 'Excel'}
        </Btn>
        <Btn theme={theme} kind="soft" icon="doc" size="md" disabled={!filtered.length || exportBusy}
          onClick={async () => {
            setExportBusy(true);
            try { await exportStudentsPdf(filtered, exportFilterLabel, theme); } catch (e) { alert(e.message); }
            setExportBusy(false);
          }}>
          {exportBusy ? 'PDF…' : 'PDF'}
        </Btn>
        {demoCount > 0 && (
          <Btn theme={theme} kind="ghost" icon="trash" size="md" onClick={removeDemos}>
            Quitar demo ({demoCount})
          </Btn>
        )}
        <Btn theme={theme} icon="plus" size="md" onClick={() => setShowAdd(true)}>Agregar alumno</Btn>
      </div>

      {showFilters && (
        <div style={{ display: 'flex', gap: 8, marginBottom: 14, flexWrap: 'wrap' }}>
          {[
            { id: 'todos', l: 'Todos' },
            { id: 'activo', l: 'Activos' },
            { id: 'inactivo', l: 'Inactivos' },
            { id: 'adeudo', l: 'Con adeudo' },
          ].map(f => (
            <button key={f.id} onClick={() => setStatusFilter(f.id)} style={{
              padding: '8px 14px', borderRadius: 8, 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}</button>
          ))}
        </div>
      )}

      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(5, 1fr)', gap: 12, marginBottom: 18 }}>
        {[
          { l: 'Total', v: String(realAlumnos.length), c: theme.primary },
          { l: 'Activos', v: String(activos), c: theme.success },
          { l: 'Inactivos', v: String(inactivos), c: theme.textMute },
          { l: 'Con adeudo', v: String(conAdeudo), c: theme.danger },
          { l: 'Nuevos este mes', v: String(nuevosMes), c: theme.info },
        ].map(s => (
          <Card key={s.l} theme={theme} style={{ padding: 14, display: 'flex', alignItems: 'center', gap: 12 }}>
            <div style={{ width: 4, height: 36, borderRadius: 2, background: s.c }}></div>
            <div>
              <div style={{ fontSize: 10, color: theme.textMute, letterSpacing: 1.2, fontWeight: 700, textTransform: 'uppercase', fontFamily: 'Inter, sans-serif' }}>{s.l}</div>
              <div style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 26, color: theme.text, letterSpacing: 0.5, marginTop: 2, fontWeight: 400 }}>{s.v}</div>
            </div>
          </Card>
        ))}
      </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>} />
      ) : (
      <Card theme={theme} style={{ padding: 0, overflow: 'hidden' }}>
        {filtered.length === 0 ? (
          <EmptyState theme={theme}
            title={realAlumnos.length === 0 ? 'Aún no hay alumnos' : 'Sin resultados'}
            message={realAlumnos.length === 0
              ? 'Usa «Agregar alumno» para registrar el primero en el sistema.'
              : 'Prueba otra búsqueda o cambia los filtros.'}
            icon="users"
            action={realAlumnos.length === 0 ? <Btn theme={theme} icon="plus" onClick={() => setShowAdd(true)}>Agregar alumno</Btn> : null}
          />
        ) : (
        <>
        <div className="tecos-table-scroll" style={{ overflowX: 'auto' }}>
          <table style={{ width: '100%', borderCollapse: 'collapse', fontFamily: 'Inter, sans-serif' }}>
            <thead>
              <tr style={{ background: theme.bgInput, borderBottom: `1px solid ${theme.border}` }}>
                {['Alumno', 'ID', 'Edad', 'Categoría', 'Papelería', 'Tutor', 'Teléfono', 'Estado', 'Adeudo (mes)', 'Acciones'].map(h => (
                  <th key={h} style={{
                    padding: '14px 16px', textAlign: 'left',
                    fontSize: 10, fontWeight: 700, color: theme.textMute,
                    letterSpacing: 1, textTransform: 'uppercase',
                  }}>{h}</th>
                ))}
              </tr>
            </thead>
            <tbody>
              {filtered.map((a, i) => (
                <tr key={a._uuid || a.id} style={{
                  borderBottom: i < filtered.length - 1 ? `1px solid ${theme.border}` : 'none',
                }}
                  onMouseEnter={e => e.currentTarget.style.background = theme.bgInput}
                  onMouseLeave={e => e.currentTarget.style.background = 'transparent'}
                >
                  <td style={tdStyle(theme)}>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                      {a.photoUrl ? (
                        <img src={a.photoUrl} alt="" style={{ width: 36, height: 36, borderRadius: '50%', objectFit: 'cover' }} />
                      ) : (
                        <Avatar name={a.name} size={36} theme={theme} />
                      )}
                      <div>
                        <div style={{ color: theme.text, fontWeight: 600, fontSize: 13 }}>{a.name}</div>
                        <div style={{ color: theme.textMute, fontSize: 11 }}>{a.email}</div>
                      </div>
                    </div>
                  </td>
                  <td style={tdStyle(theme)}><span style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 16, color: theme.primary, letterSpacing: 0.5 }}>{a.id}</span></td>
                  <td style={tdStyle(theme)}>{a.age}</td>
                  <td style={tdStyle(theme)}><Badge theme={theme} color="info">{a.cat}</Badge></td>
                  <td style={tdStyle(theme)}>
                    {(() => {
                      const dc = docCounts?.[a._uuid];
                      const loaded = typeof dc === 'object' ? (dc.loaded ?? 0) : (Number(dc) || 0);
                      const required = typeof dc === 'object' ? (dc.required ?? docCatalogTotal) : docCatalogTotal;
                      const complete = required > 0 && loaded >= required;
                      return (
                        <Badge theme={theme} color={complete ? 'success' : (loaded > 0 ? 'warning' : 'danger')}>
                          {loaded}/{required} {complete ? 'OK' : (loaded > 0 ? 'Parcial' : 'Pendiente')}
                        </Badge>
                      );
                    })()}
                  </td>
                  <td style={tdStyle(theme)}>{a.tutor}</td>
                  <td style={tdStyle(theme)}>{a.phone}</td>
                  <td style={tdStyle(theme)}><StatusPill status={a.status} theme={theme} /></td>
                  <td style={tdStyle(theme)}>
                    {a.adeudo > 0 ? (
                      <span style={{ color: theme.danger, fontWeight: 700, fontFamily: 'Bebas Neue, sans-serif', fontSize: 18, letterSpacing: 0.5 }}>${a.adeudo}</span>
                    ) : (
                      <span style={{ color: theme.success, fontSize: 12, fontWeight: 600 }}>Al corriente</span>
                    )}
                  </td>
                  <td style={tdStyle(theme)}>
                    <div style={{ display: 'flex', gap: 4 }}>
                      <button type="button" onClick={() => setSelected(a)} style={iconBtnSmall(theme)} title="Ver expediente"><Icon name="eye" size={14} /></button>
                      <button type="button" onClick={() => openWaForStudent(a, null, onWhatsApp)} style={iconBtnSmall(theme, theme.wa)} title="Enviar WhatsApp"><Icon name="wa" size={14} /></button>
                      <button type="button" onClick={() => openEdit(a)} style={iconBtnSmall(theme)} title="Editar"><Icon name="edit" size={14} /></button>
                      <button type="button" onClick={async () => {
                        if (!confirm(`¿Eliminar alumno ${a.name} (${a.id})? Se borrarán sus pagos vinculados.`)) return;
                        try {
                          await deleteStudent(a._uuid);
                          reload();
                        } catch (e) {
                          alert(e?.message || 'No se pudo eliminar el alumno.');
                        }
                      }} style={iconBtnSmall(theme, theme.danger)} title="Eliminar"><Icon name="trash" size={14} /></button>
                    </div>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
        <div style={{
          padding: 14, borderTop: `1px solid ${theme.border}`,
          display: 'flex', justifyContent: 'space-between', alignItems: 'center',
          fontSize: 12, color: theme.textDim, fontFamily: 'Inter, sans-serif',
        }}>
          <span>Mostrando {filtered.length} de {realAlumnos.length} alumno{realAlumnos.length !== 1 ? 's' : ''}</span>
          {q && <button type="button" onClick={() => setSearch('')} style={{ background: 'none', border: 'none', color: theme.primary, cursor: 'pointer', fontWeight: 600 }}>Limpiar búsqueda</button>}
        </div>
        </>
        )}
      </Card>
      )}

      <AlumnoModal open={!!selected} onClose={() => setSelected(null)} alumno={selected} theme={theme}
        onWhatsApp={onWhatsApp} onEdit={openEdit} onReload={reload} />
      <StudentFormModal open={showAdd || !!editing} onClose={() => { setShowAdd(false); setEditing(null); }} theme={theme}
        initial={editing} onSaved={reload} />
    </div>
  );
};

const AlumnoWaExpedienteModal = ({ open, onClose, theme, alumno, recipients, getExpedienteOpts }) => {
  const [selected, setSelected] = React.useState({});
  const [busy, setBusy] = React.useState(false);
  const [err, setErr] = React.useState('');

  React.useEffect(() => {
    if (!open) return;
    const init = {};
    (recipients || []).forEach((r) => { init[r.id] = recipients.length === 1; });
    setSelected(init);
    setErr('');
  }, [open, recipients]);

  const toggle = (id) => setSelected((prev) => ({ ...prev, [id]: !prev[id] }));

  const send = async () => {
    const picked = (recipients || []).filter((r) => selected[r.id]);
    if (!picked.length) {
      setErr('Marca al menos un número para enviar el expediente.');
      return;
    }
    setBusy(true);
    setErr('');
    try {
      const expedienteOpts = typeof getExpedienteOpts === 'function' ? await getExpedienteOpts() : {};
      await sendExpedientePdfViaWhatsApp(alumno, picked, expedienteOpts);
      onClose();
    } catch (e) {
      if (e?.name !== 'AbortError') setErr(e.message || 'No se pudo enviar');
    }
    setBusy(false);
  };

  if (!open) return null;

  return (
    <Modal open={open} onClose={onClose} theme={theme} title="Enviar expediente por WhatsApp" width={440}>
      <div style={{ padding: 24, display: 'grid', gap: 14 }}>
        <p style={{ margin: 0, fontSize: 13, color: theme.textDim, fontFamily: 'Inter, sans-serif', lineHeight: 1.5 }}>
          Se generará el <strong>expediente PDF</strong> y se enviará al número que elijas. Si hay dos teléfonos registrados, marca a cuál deseas mandarlo.
        </p>
        {!recipients?.length ? (
          <p style={{ color: theme.warning, fontSize: 13, margin: 0 }}>Este alumno no tiene teléfonos válidos registrados.</p>
        ) : (
          <div style={{ display: 'grid', gap: 10 }}>
            {recipients.map((r) => (
              <label key={r.id} style={{
                display: 'flex', alignItems: 'center', gap: 12, padding: 14, borderRadius: 10,
                border: `1px solid ${selected[r.id] ? theme.wa : theme.border}`,
                background: selected[r.id] ? `${theme.wa}18` : theme.bgInput,
                cursor: 'pointer', fontFamily: 'Inter, sans-serif',
              }}>
                <input
                  type="checkbox"
                  checked={!!selected[r.id]}
                  onChange={() => toggle(r.id)}
                  style={{ width: 18, height: 18, accentColor: theme.wa }}
                />
                <div style={{ flex: 1 }}>
                  <div style={{ fontWeight: 700, fontSize: 13, color: theme.text }}>{r.role}</div>
                  <div style={{ fontSize: 12, color: theme.textDim, marginTop: 2 }}>{r.display}</div>
                </div>
                <Icon name="wa" size={18} color={theme.wa} />
              </label>
            ))}
          </div>
        )}
        {err && <div style={{ color: theme.danger, fontSize: 13 }}>{err}</div>}
      </div>
      <div style={{ padding: '12px 24px', borderTop: `1px solid ${theme.border}`, display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
        <Btn theme={theme} kind="ghost" onClick={onClose} disabled={busy}>Cancelar</Btn>
        <Btn theme={theme} kind="wa" icon="send" onClick={send} disabled={busy || !recipients?.length}>
          {busy ? 'Enviando…' : 'Enviar expediente PDF'}
        </Btn>
      </div>
    </Modal>
  );
};

const AlumnoModal = ({ open, onClose, alumno, theme, onWhatsApp, onEdit, onReload }) => {
  const [tab, setTab] = React.useState('expediente');
  const [expBusy, setExpBusy] = React.useState(false);
  const [showWaExp, setShowWaExp] = React.useState(false);
  const [receiptPaymentId, setReceiptPaymentId] = React.useState(null);
  const [receiptOrder, setReceiptOrder] = React.useState(null);
  const uuid = alumno?._uuid;
  const code = alumno?.id;

  React.useEffect(() => { if (open) setTab('pagos'); }, [open, uuid]);

  const [calendarYear, setCalendarYear] = React.useState(() => new Date().getFullYear());
  const YearNav = PaymentCalendarYearNav;
  const yearBounds = typeof getPaymentCalendarYearBounds === 'function'
    ? getPaymentCalendarYearBounds(alumno?.joined_at)
    : { minYear: new Date().getFullYear() - 2, maxYear: new Date().getFullYear() + 8 };

  React.useEffect(() => {
    if (open && uuid) setCalendarYear(new Date().getFullYear());
  }, [open, uuid]);

  const { data: calendarMonths, loading: loadingCalendar } = useLiveData(
    () => (uuid ? fetchStudentPaymentCalendar(uuid, calendarYear, alumno?.joined_at) : Promise.resolve([])),
    [uuid, open, alumno?.joined_at, calendarYear]
  );

  const { data: docs, loading: loadingDocs, reload: reloadDocs } = useLiveData(
    () => (uuid ? fetchStudentDocuments(uuid) : Promise.resolve([])),
    [uuid, open]
  );
  const [docRequirements, setDocRequirements] = React.useState(null);

  React.useEffect(() => {
    if (!open || !uuid) {
      setDocRequirements(null);
      return;
    }
    (async () => {
      try {
        const req = typeof fetchStudentDocumentRequirements === 'function'
          ? await fetchStudentDocumentRequirements(uuid)
          : null;
        setDocRequirements(req);
      } catch (e) {
        console.warn('[Tecos] requisitos documentos', e);
        setDocRequirements(typeof defaultDocumentRequirements === 'function' ? defaultDocumentRequirements() : null);
      }
    })();
  }, [open, uuid]);

  const { data: pagos, loading: loadingPagos } = useLiveData(
    () => (uuid ? fetchStudentPaymentsByUuid(uuid) : Promise.resolve([])),
    [uuid, open]
  );
  const [ordersTick, setOrdersTick] = React.useState(0);
  React.useEffect(() => {
    const on = () => setOrdersTick((t) => t + 1);
    window.addEventListener('tecos:orders-changed', on);
    window.addEventListener('tecos:comprobantes-changed', on);
    return () => {
      window.removeEventListener('tecos:orders-changed', on);
      window.removeEventListener('tecos:comprobantes-changed', on);
    };
  }, []);
  const { data: ordenes, loading: loadingOrdenes } = useLiveData(
    () => (uuid && code ? fetchStudentOrdersByUuid(uuid, code) : Promise.resolve([])),
    [uuid, code, open, ordersTick]
  );
  const { data: mensajes, loading: loadingMsg } = useLiveData(
    () => (uuid ? fetchStudentNotificationsByUuid(uuid) : Promise.resolve([])),
    [uuid, open]
  );

  if (!alumno) return null;

  const phoneRecipients = typeof getStudentPhoneRecipients === 'function'
    ? getStudentPhoneRecipients(alumno)
    : [];

  const buildExpedienteOpts = async () => {
    const settings = await fetchAcademySettings();
    return {
      payments: pagos || [],
      orders: ordenes || [],
      documents: docs || [],
      documentRequirements: docRequirements,
      calendarMonths: calendarMonths || [],
      settings,
      year: calendarYear,
    };
  };

  const downloadExpedientePdf = async () => {
    setExpBusy(true);
    try {
      const opts = await buildExpedienteOpts();
      await exportStudentExpedientePdf(alumno, opts);
    } catch (e) { alert(e.message); }
    setExpBusy(false);
  };

  return (
    <Modal open={open} onClose={onClose} theme={theme} width={780}>
      <div style={{
        padding: 24, display: 'flex', gap: 20, alignItems: 'center',
        background: `linear-gradient(135deg, ${theme.primary}22, ${theme.bgElev})`,
        borderBottom: `1px solid ${theme.border}`,
      }}>
        {alumno.photoUrl ? (
          <img src={alumno.photoUrl} alt="" style={{ width: 88, height: 88, borderRadius: '50%', objectFit: 'cover' }} />
        ) : (
          <Avatar name={alumno.name} size={88} color={theme.primary} />
        )}
        <div style={{ flex: 1 }}>
          <Badge theme={theme} color="info">{alumno.cat}</Badge>
          <h3 style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 32, letterSpacing: 1, color: theme.text, margin: '4px 0', fontWeight: 400 }}>{alumno.name}</h3>
          <div style={{ display: 'flex', gap: 14, color: theme.textDim, fontSize: 13, fontFamily: 'Inter, sans-serif', flexWrap: 'wrap' }}>
            <span style={{ color: theme.primary, fontWeight: 700 }}>{alumno.id}</span>
            <span>·</span>
            <span>{alumno.age} años</span>
            <span>·</span>
            <span>Ingreso: {alumno.joined}</span>
            <span>·</span>
            <span>{alumno.antiguedad || computeSeniorityLabel(alumno.joined_at)}</span>
            <span>·</span>
            <StatusPill status={alumno.status} theme={theme} />
          </div>
        </div>
        <button type="button" onClick={onClose} style={{
          width: 36, height: 36, borderRadius: 8, background: theme.bgInput,
          border: `1px solid ${theme.border}`, color: theme.text, cursor: 'pointer',
          display: 'grid', placeItems: 'center',
        }}><Icon name="x" size={16} /></button>
      </div>
      <div style={{ display: 'flex', borderBottom: `1px solid ${theme.border}`, padding: '0 24px' }}>
        {[
          { id: 'expediente', l: 'Expediente' },
          { id: 'papeleria', l: 'Papelería' },
          { id: 'pagos', l: 'Pagos' },
          { id: 'compras', l: 'Compras' },
          { id: 'mensajes', l: 'Notificaciones' },
        ].map(t => (
          <button type="button" key={t.id} onClick={() => setTab(t.id)} style={{
            padding: '14px 18px', border: 'none', background: 'transparent',
            color: tab === t.id ? theme.primary : theme.textDim,
            fontSize: 12, fontWeight: 700, letterSpacing: 0.5, textTransform: 'uppercase',
            cursor: 'pointer', fontFamily: 'Inter, sans-serif',
            borderBottom: `2px solid ${tab === t.id ? theme.primary : 'transparent'}`,
          }}>{t.l}</button>
        ))}
      </div>
      <div style={{ padding: 24, minHeight: 280 }}>
        {tab === 'expediente' && (
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
            {[
              ['Tutor', alumno.tutor],
              ['Teléfono alumno', alumno.student_phone || '—'],
              ['Teléfono tutor', alumno.phone],
              ['Correo', alumno.email],
              ['Fecha nacimiento', formatBirthDateDisplay(alumno.birth_date)],
              ['Fecha de ingreso', alumno.joined],
              ['Categoría', alumno.cat],
              ['Notas', alumno.notes || '—'],
              ['Adeudo (mes actual)', alumno.adeudo > 0 ? `$${Number(alumno.adeudo).toLocaleString('es-MX')}` : 'Al corriente'],
            ].map(([l, v]) => (
              <div key={l} style={{ padding: 12, borderRadius: 8, background: theme.bgInput, border: `1px solid ${theme.border}` }}>
                <div style={{ fontSize: 10, color: theme.textMute, letterSpacing: 1, fontWeight: 700, textTransform: 'uppercase', fontFamily: 'Inter, sans-serif' }}>{l}</div>
                <div style={{ color: theme.text, fontSize: 13, marginTop: 4, fontFamily: 'Inter, sans-serif', fontWeight: 600 }}>{v}</div>
              </div>
            ))}
          </div>
        )}
        {tab === 'papeleria' && (
          loadingDocs ? <LoadingBlock theme={theme} label="Cargando documentos…" /> :
          typeof StudentDocumentsChecklist === 'function' ? (
            <StudentDocumentsChecklist
              theme={theme}
              docs={docs || []}
              requirements={docRequirements}
              canUpload={false}
              canEditRequirements
              studentId={uuid}
              studentCode={code}
              onUploaded={() => { reloadDocs(); onReload?.(); }}
              readOnlyHint="Marca «Aplica» por documento. El alumno solo ve los que aplican en Mi papelería."
            />
          ) : (
            <p style={{ fontSize: 12, color: theme.textDim }}>Carga student-documents-ui.jsx</p>
          )
        )}
        {tab === 'pagos' && (
          loadingCalendar ? <LoadingBlock theme={theme} label="Cargando calendario…" /> :
          <div style={{ display: 'grid', gap: 16 }}>
            <div>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 12, marginBottom: 6, flexWrap: 'wrap' }}>
                <h4 style={{
                  fontFamily: 'Bebas Neue, sans-serif', fontSize: 22, color: theme.text,
                  letterSpacing: 1, margin: 0, fontWeight: 400,
                }}>
                  CALENDARIO DE PAGOS
                </h4>
                {YearNav && (
                  <YearNav
                    theme={theme}
                    year={calendarYear}
                    onPrev={() => setCalendarYear((y) => Math.max(yearBounds.minYear, y - 1))}
                    onNext={() => setCalendarYear((y) => Math.min(yearBounds.maxYear, y + 1))}
                    minYear={yearBounds.minYear}
                    maxYear={yearBounds.maxYear}
                  />
                )}
              </div>
              <p style={{ fontSize: 12, color: theme.textDim, margin: '0 0 12px', fontFamily: 'Inter, sans-serif' }}>
                Todos los meses desde el ingreso del alumno están activos. Meses futuros: adelanto con tarifa base (días 1–5); al llegar el mes aplica la tarifa por periodo.
              </p>
              <PaymentGrid theme={theme} months={calendarMonths || []} adminCalendar
                onPayClick={(m) => {
                  if (m.status === 'pagado' && m.paymentId) setReceiptPaymentId(m.paymentId);
                  else if (m.status === 'revision' || m.status === 'en_revision') {
                    alert('Comprobante en revisión. Apruébalo en Verificar comprobantes.');
                  } else if (!m.hideAmount || m.advancePay) {
                    alert('Para registrar el pago de este mes (incluido adelanto), usa Ventas y Pagos → Registrar pago.');
                  }
                }}
              />
              <div style={{
                display: 'flex', gap: 14, marginTop: 12, flexWrap: 'wrap',
                fontSize: 11, color: theme.textDim, fontFamily: 'Inter, sans-serif',
              }}>
                <LegendDot c={theme.success} l="Pagado" />
                <LegendDot c={theme.warning} l="Pendiente / revisión" />
                <LegendDot c={theme.danger} l="Vencido" />
                <LegendDot c={theme.urgent} l="Urgente" />
                <LegendDot c={theme.warning} l="Pendiente (sin monto aún)" />
              </div>
            </div>
            {loadingPagos ? <LoadingBlock theme={theme} label="Historial…" /> :
            !pagos.length ? null :
            <div>
              <h4 style={{
                fontFamily: 'Bebas Neue, sans-serif', fontSize: 18, color: theme.textDim,
                letterSpacing: 0.8, margin: '0 0 10px', fontWeight: 400,
              }}>DETALLE</h4>
              <div style={{ display: 'grid', gap: 8 }}>
                {pagos.map((p, i) => (
                  <div key={p.id || i} style={{
                    display: 'flex', alignItems: 'center', gap: 14, padding: 12,
                    borderRadius: 10, background: theme.bgInput, border: `1px solid ${theme.border}`,
                  }}>
                    <div style={{ flex: 1 }}>
                      <div style={{ fontWeight: 600, color: theme.text, fontFamily: 'Inter, sans-serif', fontSize: 14 }}>{p.c}</div>
                      <div style={{ color: theme.textMute, fontSize: 11, fontFamily: 'Inter, sans-serif', marginTop: 2 }}>
                        {p.m} · {p.d}
                      </div>
                    </div>
                    {p.hideAmount ? (
                      <span style={{ fontSize: 11, color: theme.textMute, fontFamily: 'Inter, sans-serif', fontWeight: 600 }}>
                        Tarifa al iniciar mes
                      </span>
                    ) : (
                      <div style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 22, color: theme.text, letterSpacing: 0.5 }}>
                        ${Number(p.a).toLocaleString('es-MX')}
                      </div>
                    )}
                    <StatusPill status={p.s} theme={theme} />
                  </div>
                ))}
              </div>
            </div>}
            {!loadingPagos && !pagos.length && !(calendarMonths || []).some(m => m.paymentId) && (
              <EmptyState theme={theme} title="Sin mensualidades" message="Al dar de alta al alumno marca los meses pagados, o genera el ciclo en Ventas y Pagos." icon="money" />
            )}
          </div>
        )}
        {tab === 'compras' && (
          loadingOrdenes ? <LoadingBlock theme={theme} label="Cargando compras…" /> :
          !ordenes.length ? <EmptyState theme={theme} title="Sin compras" message="Las órdenes de tienda con su ID aparecerán aquí." icon="cart" /> :
          <div style={{ display: 'grid', gap: 8 }}>
            {ordenes.map((c) => (
              <div key={c.id} style={{
                display: 'flex', alignItems: 'center', gap: 14, padding: 12,
                borderRadius: 10, background: theme.bgInput, border: `1px solid ${theme.border}`,
              }}>
                <div style={{ width: 40, height: 40, borderRadius: 8, background: theme.bgElev, color: theme.primary, display: 'grid', placeItems: 'center' }}>
                  <Icon name="cart" size={16} />
                </div>
                <div style={{ flex: 1 }}>
                  <div style={{ fontWeight: 600, color: theme.text, fontFamily: 'Inter, sans-serif', fontSize: 14 }}>{c.p}</div>
                  <div style={{ color: theme.textMute, fontSize: 11, fontFamily: 'Inter, sans-serif' }}>{c.n} · {c.d}</div>
                </div>
                <div style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 20, color: theme.text }}>${c.a}</div>
                <StatusPill status={c.s} theme={theme} />
                <Btn theme={theme} size="sm" kind="soft" icon="doc" onClick={() => setReceiptOrder(c)} title="Comprobante PDF y WhatsApp">
                  Comprobante
                </Btn>
              </div>
            ))}
          </div>
        )}
        {tab === 'mensajes' && (
          loadingMsg ? <LoadingBlock theme={theme} label="Cargando notificaciones…" /> :
          !mensajes.length ? <EmptyState theme={theme} title="Sin notificaciones" message="Las alertas del sistema para este alumno aparecerán aquí." icon="bell" /> :
          <div style={{ display: 'grid', gap: 8 }}>
            {mensajes.map((m, i) => (
              <div key={i} style={{
                display: 'flex', alignItems: 'center', gap: 14, padding: 12,
                borderRadius: 10, background: theme.bgInput, border: `1px solid ${theme.border}`,
              }}>
                <div style={{ width: 40, height: 40, borderRadius: 8, background: `${theme.primary}22`, color: theme.primary, display: 'grid', placeItems: 'center' }}>
                  <Icon name={m.i} size={16} />
                </div>
                <div style={{ flex: 1 }}>
                  <div style={{ fontWeight: 600, color: theme.text, fontFamily: 'Inter, sans-serif', fontSize: 14 }}>{m.t}</div>
                  <div style={{ color: theme.textMute, fontSize: 11, fontFamily: 'Inter, sans-serif' }}>{m.d}</div>
                </div>
                <StatusPill status={m.s} theme={theme} />
              </div>
            ))}
          </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" icon="download" disabled={expBusy} onClick={downloadExpedientePdf}>
          {expBusy ? 'Generando PDF…' : 'Generar expediente PDF'}
        </Btn>
        <Btn theme={theme} kind="wa" icon="wa"
          disabled={!phoneRecipients.length}
          onClick={() => {
            if (!phoneRecipients.length) {
              alert('Registra el teléfono del tutor o del alumno para enviar el expediente.');
              return;
            }
            setShowWaExp(true);
          }}>
          Enviar WhatsApp
        </Btn>
        <Btn theme={theme} icon="edit" onClick={() => { onClose(); onEdit?.(alumno); }}>Editar</Btn>
      </div>
      <AlumnoWaExpedienteModal
        open={showWaExp}
        onClose={() => setShowWaExp(false)}
        theme={theme}
        alumno={alumno}
        recipients={phoneRecipients}
        getExpedienteOpts={buildExpedienteOpts}
      />
      <PaymentReceiptDetailModal
        open={!!receiptPaymentId}
        onClose={() => setReceiptPaymentId(null)}
        theme={theme}
        alumno={alumno}
        paymentId={receiptPaymentId}
        onReload={onReload}
      />
      <StoreOrderReceiptDetailModal
        open={!!receiptOrder}
        onClose={() => setReceiptOrder(null)}
        theme={theme}
        alumno={alumno}
        order={receiptOrder}
      />
    </Modal>
  );
};

const tdStyle = (theme) => ({
  padding: '14px 16px',
  fontSize: 13,
  color: theme.text,
  fontFamily: 'Inter, sans-serif',
});

const iconBtnSmall = (theme, color) => ({
  width: 30, height: 30, borderRadius: 7,
  background: theme.bgInput,
  border: `1px solid ${theme.border}`,
  color: color || theme.text,
  cursor: 'pointer', display: 'grid', placeItems: 'center',
});

async function openWaForStudent (student, settings, onWhatsApp) {
  if (!onWhatsApp || !student) return;
  try {
    if (typeof buildWaRecipientFromStudent === 'function') {
      const recipient = await buildWaRecipientFromStudent(student, settings);
      onWhatsApp(recipient);
      return;
    }
  } catch (e) {
    alert(e?.message || 'No se pudo preparar el mensaje de WhatsApp.');
    return;
  }
  onWhatsApp(student);
}

/* ===================== VENTAS Y PAGOS ===================== */
const PagosModule = ({ theme, onWhatsApp }) => {
  const [search, setSearch] = React.useState('');
  const [statusFilter, setStatusFilter] = React.useState('todos');
  const [showLookup, setShowLookup] = React.useState(false);
  const [activeStudent, setActiveStudent] = React.useState(null);

  const { data: alumnos, loading, error, needsAuth, reload } = useLiveData(
    () => fetchStudentsAdmin(),
    []
  );
  const { data: billingSettings } = useLiveData(() => fetchAcademySettings(), []);
  const { data: storeOrders, loading: loadingStoreOrders, reload: reloadStoreOrders } = useLiveData(
    () => (typeof fetchOrdersAdminLandingQueue === 'function'
      ? fetchOrdersAdminLandingQueue()
      : typeof fetchOrdersAdmin === 'function'
        ? fetchOrdersAdmin().then(rows => (rows || []).filter(o => typeof isOrderInAdminSalesQueue === 'function'
          ? isOrderInAdminSalesQueue(o.status)
          : !['confirmado', 'entregado'].includes(String(o.status).toLowerCase())))
        : Promise.resolve([])),
    []
  );

  React.useEffect(() => {
    const onChange = () => {
      reload();
      reloadStoreOrders();
    };
    window.addEventListener('tecos:academy-data-sync', onChange);
    window.addEventListener('tecos:payments-changed', onChange);
    window.addEventListener('tecos:comprobantes-changed', onChange);
    window.addEventListener('tecos:settings-changed', onChange);
    window.addEventListener('tecos:orders-changed', onChange);
    window.addEventListener('tecos:students-changed', onChange);
    window.addEventListener('tecos:finance-changed', onChange);
    return () => {
      window.removeEventListener('tecos:academy-data-sync', onChange);
      window.removeEventListener('tecos:payments-changed', onChange);
      window.removeEventListener('tecos:comprobantes-changed', onChange);
      window.removeEventListener('tecos:settings-changed', onChange);
      window.removeEventListener('tecos:orders-changed', onChange);
      window.removeEventListener('tecos:students-changed', onChange);
      window.removeEventListener('tecos:finance-changed', onChange);
    };
  }, [reload, reloadStoreOrders]);

  const formatFechaAdmin = (iso) => {
    if (!iso) return '—';
    try {
      return new Date(iso).toLocaleString('es-MX', { dateStyle: 'short', timeStyle: 'short' });
    } catch {
      return iso;
    }
  };

  const realAlumnos = (alumnos || []).filter(a => !isDemoStudentCode(a.id));
  const q = search.trim().toLowerCase();
  const filtered = realAlumnos.filter(a => {
    if (statusFilter === 'activo' && a.status !== 'activo') return false;
    if (statusFilter === 'adeudo' && !(a.adeudo > 0)) return false;
    if (!q) return true;
    const hay = [a.name, a.id, a.tutor, a.phone, a.email, a.cat].join(' ').toLowerCase();
    return hay.includes(q);
  });

  const conAdeudo = realAlumnos.filter(a => a.adeudo > 0).length;
  const alCorriente = realAlumnos.length - conAdeudo;

  const openStudent = (a) => {
    setActiveStudent(a);
  };

  const refreshStudent = async () => {
    reload();
    if (!activeStudent?._uuid) return;
    const list = await fetchStudentsAdmin();
    const fresh = (list || []).find(s => s._uuid === activeStudent._uuid);
    if (fresh) setActiveStudent(fresh);
  };

  return (
    <div className="tecos-page-shell" style={{ padding: 32 }}>
      {billingSettings && (
        <Card theme={theme} style={{ padding: 14, marginBottom: 14, fontFamily: 'Inter, sans-serif', fontSize: 12, color: theme.textDim }}>
          <strong style={{ color: theme.text }}>Cobro por periodo:</strong> un mes a la vez según tarifas de Configuración.
          {' '}Los pagos por <strong>transferencia</strong> se aprueban en <strong>Verificar comprobantes</strong> y se reflejan aquí y en Alumnos.
        </Card>
      )}

      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 14, marginBottom: 18 }}>
        {[
          { l: 'Alumnos', v: String(realAlumnos.length), c: theme.primary },
          { l: 'Al corriente', v: String(alCorriente), c: theme.success },
          { l: 'Con adeudo (mes)', v: String(conAdeudo), c: theme.danger },
        ].map(s => (
          <Card key={s.l} theme={theme} style={{ padding: 14, display: 'flex', alignItems: 'center', gap: 12 }}>
            <div style={{ width: 4, height: 36, borderRadius: 2, background: s.c }} />
            <div>
              <div style={{ fontSize: 10, color: theme.textMute, letterSpacing: 1.2, fontWeight: 700, textTransform: 'uppercase', fontFamily: 'Inter, sans-serif' }}>{s.l}</div>
              <div style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 26, color: theme.text, letterSpacing: 0.5, marginTop: 2 }}>{s.v}</div>
            </div>
          </Card>
        ))}
      </div>

      <div style={{ display: 'flex', gap: 12, marginBottom: 14, flexWrap: 'wrap', alignItems: 'center' }}>
        <div style={{ flex: 1, minWidth: 240 }}>
          <Input theme={theme} placeholder="Buscar por nombre, ID, tutor…" icon="search"
            value={search} onChange={e => setSearch(e.target.value)} />
        </div>
        <div style={{ display: 'flex', gap: 6 }}>
          {[
            { id: 'todos', l: 'Todos' },
            { id: 'activo', l: 'Activos' },
            { id: 'adeudo', l: 'Con adeudo' },
          ].map(f => (
            <button key={f.id} type="button" onClick={() => setStatusFilter(f.id)} style={{
              padding: '8px 14px', borderRadius: 8, 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}</button>
          ))}
        </div>
        <Btn theme={theme} icon="plus" onClick={() => setShowLookup(true)}>Registrar pago</Btn>
      </div>

      <Card theme={theme} style={{ padding: 16, marginBottom: 18 }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 10, marginBottom: 12 }}>
          <div>
            <h4 style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 22, color: theme.text, margin: 0, letterSpacing: 1 }}>
              ÓRDENES TIENDA (LANDING)
            </h4>
            <p style={{ margin: '6px 0 0', fontSize: 12, color: theme.textDim, fontFamily: 'Inter, sans-serif' }}>
              Pedidos con ID de alumno desde la tienda pública. Revisa comprobantes en <strong>Verificar comprobantes → Tienda online</strong>.
            </p>
          </div>
          <Btn theme={theme} kind="ghost" size="sm" icon="history" onClick={reloadStoreOrders} disabled={loadingStoreOrders}>
            Actualizar
          </Btn>
        </div>
        {loadingStoreOrders ? (
          <LoadingBlock theme={theme} label="Cargando órdenes…" />
        ) : !(storeOrders || []).length ? (
          <p style={{ margin: 0, fontSize: 13, color: theme.textDim, fontFamily: 'Inter, sans-serif' }}>
            Sin pedidos pendientes de tienda. Las órdenes confirmadas aparecen en <strong>Historial</strong>.
          </p>
        ) : (
          <div style={{ display: 'grid', gap: 8, maxHeight: 220, overflowY: 'auto' }}>
            {(storeOrders || []).slice(0, 12).map((o) => {
              const items = (o.order_items || []).map((i) => `${i.product_name} ×${i.qty}`).join(', ');
              const stu = o.students;
              return (
                <div key={o.id} style={{
                  display: 'flex', alignItems: 'center', gap: 12, padding: 10, borderRadius: 8,
                  background: theme.bgInput, border: `1px solid ${theme.border}`,
                }}>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontWeight: 700, fontSize: 13, color: theme.text }}>
                      {o.order_number} · {stu?.full_name || o.guest_student_name || '—'}
                    </div>
                    <div style={{ fontSize: 11, color: theme.textDim, marginTop: 2 }}>
                      {stu?.code || o.guest_student_code || '—'} · {items || '—'} · {formatFechaAdmin(o.created_at)}
                    </div>
                  </div>
                  <StatusPill status={o.status} theme={theme} />
                </div>
              );
            })}
          </div>
        )}
      </Card>

      {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>} />
      ) : (
        <Card theme={theme} style={{ padding: 0, overflow: 'hidden' }}>
          {filtered.length === 0 ? (
            <EmptyState theme={theme} title="Sin alumnos" message="Registra alumnos o ajusta la búsqueda." icon="users" />
          ) : (
            <>
              <div className="tecos-table-scroll" style={{ overflowX: 'auto' }}>
                <table style={{ width: '100%', borderCollapse: 'collapse', fontFamily: 'Inter, sans-serif' }}>
                  <thead>
                    <tr style={{ background: theme.bgInput, borderBottom: `1px solid ${theme.border}` }}>
                      {['Alumno', 'ID', 'Categoría', 'Tutor', 'Teléfono', 'Estado', 'Adeudo (mes)', 'Acciones'].map(h => (
                        <th key={h} style={{
                          padding: '14px 16px', textAlign: 'left', fontSize: 10, fontWeight: 700,
                          color: theme.textMute, letterSpacing: 1, textTransform: 'uppercase',
                        }}>{h}</th>
                      ))}
                    </tr>
                  </thead>
                  <tbody>
                    {filtered.map((a, i) => (
                      <tr key={a._uuid} style={{ borderBottom: i < filtered.length - 1 ? `1px solid ${theme.border}` : 'none' }}
                        onMouseEnter={e => { e.currentTarget.style.background = theme.bgInput; }}
                        onMouseLeave={e => { e.currentTarget.style.background = 'transparent'; }}
                      >
                        <td style={tdStyle(theme)}>
                          <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                            {a.photoUrl ? (
                              <img src={a.photoUrl} alt="" style={{ width: 36, height: 36, borderRadius: '50%', objectFit: 'cover' }} />
                            ) : (
                              <Avatar name={a.name} size={36} theme={theme} />
                            )}
                            <div>
                              <div style={{ fontWeight: 600, fontSize: 13 }}>{a.name}</div>
                              <div style={{ fontSize: 11, color: theme.textMute }}>{a.email}</div>
                            </div>
                          </div>
                        </td>
                        <td style={tdStyle(theme)}><span style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 16, color: theme.primary }}>{a.id}</span></td>
                        <td style={tdStyle(theme)}><Badge theme={theme} color="info">{a.cat}</Badge></td>
                        <td style={tdStyle(theme)}>{a.tutor}</td>
                        <td style={tdStyle(theme)}>{a.phone}</td>
                        <td style={tdStyle(theme)}><StatusPill status={a.status} theme={theme} /></td>
                        <td style={tdStyle(theme)}>
                          {a.adeudo > 0 ? (
                            <span style={{ color: theme.danger, fontWeight: 700, fontFamily: 'Bebas Neue, sans-serif', fontSize: 18 }}>${a.adeudo}</span>
                          ) : (
                            <span style={{ color: theme.success, fontSize: 12, fontWeight: 600 }}>Al corriente</span>
                          )}
                        </td>
                        <td style={tdStyle(theme)}>
                          <div style={{ display: 'flex', gap: 4 }}>
                            <button type="button" title="Calendario y pagos" onClick={() => openStudent(a)} style={iconBtnSmall(theme, theme.primary)}>
                              <Icon name="money" size={14} />
                            </button>
                            {a.phone && a.phone !== '—' && (
                              <button type="button" onClick={() => openWaForStudent(a, billingSettings, onWhatsApp)} style={iconBtnSmall(theme, theme.wa)} title="WhatsApp">
                                <Icon name="wa" size={14} />
                              </button>
                            )}
                          </div>
                        </td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              </div>
              <div style={{ padding: 14, borderTop: `1px solid ${theme.border}`, fontSize: 12, color: theme.textDim, fontFamily: 'Inter, sans-serif' }}>
                Mostrando {filtered.length} de {realAlumnos.length} alumno{realAlumnos.length !== 1 ? 's' : ''}
              </div>
            </>
          )}
        </Card>
      )}

      <StudentLookupModal
        open={showLookup}
        onClose={() => setShowLookup(false)}
        theme={theme}
        students={alumnos}
        onSelect={(a) => { setShowLookup(false); openStudent(a); }}
      />
      <VentasPagosStudentModal
        open={!!activeStudent}
        onClose={() => setActiveStudent(null)}
        theme={theme}
        alumno={activeStudent}
        onSaved={refreshStudent}
      />
    </div>
  );
};



/* ===================== WHATSAPP MODULE (bot OpenWA) ===================== */
const WhatsAppModule = ({ theme, view, onPreview }) => {
  if (typeof WhatsAppBotModule === 'function') {
    return <WhatsAppBotModule theme={theme} view={view || 'wa-dashboard'} onPreview={onPreview} />;
  }
  return (
    <div className="tecos-page-shell" style={{ padding: 32 }}>
      <EmptyState
        theme={theme}
        icon="wa"
        title="WhatsApp"
        message="Carga whatsapp-bot-admin.jsx en index.html y ejecuta el servicio en whatsapp-service/."
      />
    </div>
  );
};


/* ===================== WHATSAPP PREVIEW MODAL ===================== */
const WaPreviewModal = ({ open, onClose, recipient, theme }) => {
  if (!recipient) return null;
  const isUrgent = recipient.urgent;
  const isComprobante = recipient.template === 'comprobante' || recipient.comprobanteApproved;
  const previewFull = typeof waBuildPreviewText === 'function'
    ? waBuildPreviewText(recipient, isUrgent)
    : '';
  const previewPlain = typeof waPreviewPlainText === 'function'
    ? waPreviewPlainText(previewFull)
    : previewFull.replace(/\*/g, '');
  const [sending, setSending] = React.useState(false);
  const [sendErr, setSendErr] = React.useState('');

  const handleSend = async () => {
    if (typeof waSendFromRecipient !== 'function') {
      alert('Servicio WhatsApp no cargado. Revisa whatsapp-bot-admin.jsx y whatsapp-service.');
      return;
    }
    const phone = recipient.tel || recipient.phone;
    if (!phone) {
      alert('Agrega teléfono del tutor o alumno antes de enviar por el bot.');
      return;
    }
    setSending(true);
    setSendErr('');
    try {
      await waSendFromRecipient(recipient, { urgent: isUrgent });
      onClose?.();
    } catch (e) {
      setSendErr(e.message || String(e));
    }
    setSending(false);
  };

  return (
    <Modal open={open} onClose={onClose} theme={theme} width={520}>
      <div style={{ padding: '20px 24px', borderBottom: `1px solid ${theme.border}`, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
          <div style={{ width: 38, height: 38, borderRadius: 10, background: `${theme.wa}22`, color: theme.wa, display: 'grid', placeItems: 'center' }}>
            <Icon name="wa" size={18} />
          </div>
          <div>
            <h3 style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 22, color: theme.text, letterSpacing: 1, margin: 0, fontWeight: 400 }}>VISTA PREVIA DE MENSAJE</h3>
            <p style={{ color: theme.textDim, fontSize: 12, fontFamily: 'Inter, sans-serif', margin: 0 }}>Confirma antes de enviar</p>
          </div>
        </div>
        <button onClick={onClose} style={{ ...iconBtnSmall(theme), width: 36, height: 36 }}><Icon name="x" size={16} /></button>
      </div>
      <div style={{ padding: 24 }}>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12, marginBottom: 18 }}>
          {[
            ['Alumno', recipient.name || recipient.alumno || '—'],
            ['Tutor', recipient.tutor && recipient.tutor !== '—' ? recipient.tutor : '—'],
            ['Teléfono', typeof formatWaPhoneDisplay === 'function'
              ? formatWaPhoneDisplay(recipient.tel || recipient.phone)
              : (recipient.tel || recipient.phone || '—')],
            ['Tipo', recipient.tipo || (isUrgent ? 'Mensaje urgente' : 'Recordatorio')],
            ...(recipient.billingKind === 'al_corriente' || (!recipient.monto && recipient.billingKind)
              ? [] : [['Mes / concepto', recipient.mes || recipient.concepto || '—']]),
            ...(recipient.monto != null ? [['Monto', `$${Number(recipient.monto).toLocaleString('es-MX')}`]] : []),
          ].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, letterSpacing: 1, textTransform: 'uppercase', fontFamily: 'Inter, sans-serif' }}>{l}</div>
              <div style={{ fontSize: 13, color: theme.text, fontWeight: 600, marginTop: 4, fontFamily: 'Inter, sans-serif' }}>{v}</div>
            </div>
          ))}
        </div>
        {/* WhatsApp bubble preview */}
        <div style={{
          padding: 20, borderRadius: 14,
          background: '#0b1419',
          backgroundImage: `linear-gradient(rgba(11,20,25,0.92), rgba(11,20,25,0.92)), repeating-linear-gradient(45deg, #0e1c22 0 6px, #0a1216 6px 12px)`,
        }}>
          <div style={{
            background: '#005c4b', color: '#fff', padding: '10px 14px',
            borderRadius: '14px 14px 14px 4px', maxWidth: '85%',
            fontFamily: 'Inter, sans-serif', fontSize: 13, lineHeight: 1.5,
            boxShadow: '0 1px 2px rgba(0,0,0,0.3)',
          }}>
            {isUrgent && recipient.billingKind !== 'al_corriente' && (
              <div style={{ fontSize: 11, fontWeight: 700, marginBottom: 6, color: '#fcd34d' }}>🚨 MENSAJE URGENTE</div>
            )}
            {previewPlain ? (
              <div style={{ whiteSpace: 'pre-wrap' }}>{previewPlain}</div>
            ) : isComprobante ? (
              <>
                Hola {recipient.tutor && recipient.tutor !== '—' ? recipient.tutor : 'familia'}, confirmamos el comprobante de{' '}
                <strong>{recipient.name || recipient.alumno || 'su alumno'}</strong>
                {recipient.concepto || recipient.mes ? <> ({recipient.concepto || recipient.mes})</> : null}.
                {recipient.monto != null && <> Monto: <strong>${Number(recipient.monto).toLocaleString('es-MX')}</strong>.</>}
                <br /><br />¡Gracias! Ya está al corriente.
              </>
            ) : (
              <>
                Hola {recipient.tutor && recipient.tutor !== '—' ? recipient.tutor : 'familia'}, mensaje de cobranza para{' '}
                <strong>{recipient.name || recipient.alumno || 'su alumno'}</strong>.
              </>
            )}
            <div style={{ textAlign: 'right', fontSize: 10, opacity: 0.7, marginTop: 6 }}>10:42 ✓✓</div>
          </div>
        </div>
        {typeof isMobileWhatsAppDevice === 'function' && isMobileWhatsAppDevice() && (
          <p style={{ margin: '14px 0 0', fontSize: 12, color: theme.textDim, fontFamily: 'Inter, sans-serif', lineHeight: 1.45 }}>
            En el celular usa <strong style={{ color: theme.text }}>Abrir WhatsApp con mensaje</strong> para que el texto aparezca precargado en el chat. «Enviar por bot» solo funciona en PC con Chrome del bot.
          </p>
        )}
      </div>
      {sendErr && (
        <div style={{ padding: '0 24px 8px', fontSize: 12, color: theme.danger, fontFamily: 'Inter, sans-serif' }}>
          {sendErr}
        </div>
      )}
      <div style={{ padding: '16px 24px', borderTop: `1px solid ${theme.border}`, display: 'flex', justifyContent: 'space-between', flexWrap: 'wrap', gap: 8 }}>
        <Btn theme={theme} kind="ghost" onClick={onClose} disabled={sending}>Cancelar</Btn>
        <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
          <Btn theme={theme} kind="wa" icon="wa"
            onClick={() => {
              const phone = recipient.tel || recipient.phone;
              if (!phone || phone === '—') {
                alert('Sin teléfono o formato inválido. Usa prefijo internacional (+52, +1, +34, etc.).');
                return;
              }
              const ok = typeof openWhatsAppForRecipient === 'function'
                ? openWhatsAppForRecipient(recipient, { urgent: isUrgent })
                : (typeof openWhatsAppChat === 'function'
                  ? openWhatsAppChat(phone, previewPlain)
                  : false);
              if (!ok) alert('No se pudo abrir WhatsApp. Verifica el teléfono.');
            }}>
            Abrir WhatsApp con mensaje
          </Btn>
          <Btn theme={theme} kind="soft" icon="send" onClick={handleSend} disabled={sending}>
            {sending ? 'Enviando…' : 'Enviar por bot (PC)'}
          </Btn>
        </div>
      </div>
    </Modal>
  );
};

/* ===================== STUB MODULES ===================== */
const StubModule = ({ theme, title, sub, icon, content }) => (
  <div className="tecos-page-shell" style={{ padding: 32 }}>
    <Card theme={theme} style={{ padding: 32, textAlign: 'center', marginBottom: 18 }}>
      <div style={{
        width: 80, height: 80, borderRadius: 20, margin: '0 auto 16px',
        background: `linear-gradient(135deg, ${theme.primary}, ${theme.primaryDark})`,
        color: '#fff', display: 'grid', placeItems: 'center',
      }}><Icon name={icon} size={36} /></div>
      <h3 style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 36, color: theme.text, letterSpacing: 1, margin: 0, fontWeight: 400 }}>{title}</h3>
      <p style={{ color: theme.textDim, fontSize: 14, fontFamily: 'Inter, sans-serif', margin: '8px auto 0', maxWidth: 540 }}>{sub}</p>
    </Card>
    {content}
  </div>
);

const MODULE_LOADERS = {
  anuncios: {
    title: 'ANUNCIOS', sub: 'Avisos publicados en el sitio (tabla announcements).', icon: 'megaphone',
    load: () => fetchAnnouncementsAdmin(),
    format: (rows) => rows.map(a => `${a.title}${a.published_at ? '' : ' (borrador)'}`),
  },
  eventos: {
    title: 'EVENTOS', sub: 'Calendario público (tabla events).', icon: 'cal',
    load: () => fetchPublicEvents(),
    format: (rows) => rows.map(e => {
      const d = new Date(e.starts_at);
      return `${d.toLocaleDateString('es-MX', { day: 'numeric', month: 'short' })} — ${e.title}`;
    }),
  },
  inventario: {
    title: 'INVENTARIO', sub: 'Productos en catálogo (tabla products).', icon: 'box',
    load: () => fetchProductsAdmin(),
    format: (rows) => rows.map(p => `${p.name} — stock ${p.stock} — $${p.price}`),
  },
  'tienda-admin': {
    title: 'TIENDA ONLINE', sub: 'Vitrina, fotos y visibilidad en landing.', icon: 'cart',
    load: () => fetchProductsAdmin(),
    format: (rows) => rows.filter(p => p.active).map(p => `${p.name} — $${p.price}`),
  },
  'galeria-admin': {
    title: 'GALERÍA', sub: 'Fotos (tabla gallery_items).', icon: 'image',
    load: async () => {
      const sb = getSupabase();
      const { data, error } = await sb.from('gallery_items').select('id, title, created_at').order('created_at', { ascending: false }).limit(30);
      if (error) throw error;
      return data || [];
    },
    format: (rows) => rows.map(g => g.title || `Foto ${new Date(g.created_at).toLocaleDateString('es-MX')}`),
  },
  'entrenadores-admin': {
    title: 'ENTRENADORES', sub: 'Cuerpo técnico (tabla coaches).', icon: 'whistle',
    load: () => fetchActiveCoaches(),
    format: (rows) => rows.map(c => `${c.name} — ${c.specialty || 'Entrenador'}`),
  },
  interesados: {
    title: 'INTERESADOS', sub: 'Formulario Contacto de la landing → interested_leads.', icon: 'form',
    load: () => fetchInterestedLeads(),
    format: (rows) => rows.map(l => `${l.name} — ${l.phone || l.email || 'sin contacto'}`),
  },
  ubicacion: {
    title: 'UBICACIÓN', sub: 'Dirección, mapa y horarios sincronizados con la landing.', icon: 'pin',
    load: () => fetchAcademySettings(),
    format: (s) => [s?.venue_name, s?.address_street, `${s?.address_city || ''}, ${s?.address_state || ''}`].filter(Boolean),
  },
  config: {
    title: 'CONFIGURACIÓN', sub: 'Tarifas, cuenta admin, landing y bancos.', icon: 'settings',
    load: () => fetchAcademySettings(),
    format: (s) => {
      const base = Number(s?.monthly_fee) || 0;
      return [
        `Mensualidad base: $${base.toLocaleString('es-MX')} MXN`,
        `Sin recargo: días ${s?.billing_period_start_day || 1}–${s?.billing_period_end_day || 5}`,
        `Recargo 1: días ${s?.overdue_from_day || 6}–${s?.late_until_day || 15} (+${s?.late_fee_percent || 0}%)`,
        `Recargo 2: días ${s?.severe_late_from_day || 16}–${s?.severe_late_until_day || 28} (+${s?.severe_late_fee_percent || 0}%)`,
      ];
    },
  },
};

const OtrosModulos = ({ theme, view }) => {
  const cfg = MODULE_LOADERS[view];
  if (!cfg) return null;
  const { data, loading, needsAuth, error, reload } = useLiveData(cfg.load, [view]);
  const list = cfg.format(data);

  return (
    <StubModule theme={theme} title={cfg.title} sub={cfg.sub} icon={cfg.icon}
      content={
        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>} />
        ) : (
        <Card theme={theme}>
          <div style={{ fontSize: 11, color: theme.textMute, letterSpacing: 1.5, fontWeight: 700, textTransform: 'uppercase', marginBottom: 14, fontFamily: 'Inter, sans-serif' }}>
            Registros en Supabase ({list.length})
          </div>
          {list.length === 0 ? (
            <EmptyState theme={theme} title="Vacío" message="Agrega registros en Supabase y aparecerán aquí." icon={cfg.icon} />
          ) : (
          <div style={{ display: 'grid', gap: 8 }}>
            {list.map((it, i) => (
              <div key={i} style={{
                padding: 14, borderRadius: 10, background: theme.bgInput, border: `1px solid ${theme.border}`,
                display: 'flex', alignItems: 'center', gap: 12, fontFamily: 'Inter, sans-serif', fontSize: 13, color: theme.text,
              }}>
                <div style={{ width: 36, height: 36, borderRadius: 8, background: `${theme.primary}22`, color: theme.primary, display: 'grid', placeItems: 'center' }}>
                  <Icon name={cfg.icon} size={16} />
                </div>
                <span style={{ flex: 1, fontWeight: 600 }}>{it}</span>
              </div>
            ))}
          </div>
          )}
        </Card>
        )
      } />
  );
};

Object.assign(window, {
  AlumnosModule, PagosModule, WhatsAppModule, WaPreviewModal, OtrosModulos, tdStyle,
});
