/* Bot WhatsApp — panel admin (local o puente Supabase en producción) */

function isWaLocalDevHost () {
  if (typeof window === 'undefined') return true;
  const h = window.location.hostname;
  return h === 'localhost' || h === '127.0.0.1';
}

function getWaServiceConfig () {
  const cfg = (typeof window !== 'undefined' && window.WHATSAPP_SERVICE_CONFIG) || {};
  const forceLocal = cfg.forceLocal === true;
  const sb = typeof window !== 'undefined' && window.SUPABASE_CONFIG;
  const sbUrl = sb?.url && !String(sb.url).includes('TU_PROYECTO') ? String(sb.url).replace(/\/$/, '') : '';

  if (!forceLocal && !isWaLocalDevHost() && sbUrl) {
    return {
      baseUrl: `${sbUrl}/functions/v1/wa-bridge`,
      apiKey: '',
      mode: 'cloud-bridge',
      useSupabaseSession: true,
    };
  }

  return {
    baseUrl: (cfg.baseUrl || 'http://localhost:3030').replace(/\/$/, ''),
    apiKey: cfg.apiKey || '',
    mode: 'local',
    useSupabaseSession: false,
  };
}

async function getWaSupabaseAuthHeader () {
  const sb = typeof getSupabase === 'function' ? getSupabase() : null;
  if (!sb?.auth?.getSession) return null;
  const { data } = await sb.auth.getSession();
  const token = data?.session?.access_token;
  return token ? `Bearer ${token}` : null;
}

async function waRequestReconnect () {
  try {
    return await waServiceFetch('/reconnect', { method: 'POST', body: '{}' });
  } catch (e) {
    if (String(e.message || '').includes('404')) {
      return waServiceFetch('/reset-session', { method: 'POST', body: '{}' });
    }
    throw e;
  }
}

async function waRequestOpenChrome () {
  return waServiceFetch('/open-chrome', { method: 'POST', body: '{}', timeoutMs: 90000 });
}

async function waRequestLogout () {
  try {
    return await waServiceFetch('/logout', { method: 'POST', body: '{}' });
  } catch (e) {
    if (String(e.message || '').includes('404')) {
      return waServiceFetch('/reset-session', { method: 'POST', body: '{}' });
    }
    throw e;
  }
}

async function waServiceFetch (path, options = {}) {
  const { timeoutMs = 0, ...fetchOpts } = options;
  const { baseUrl, apiKey, useSupabaseSession, mode } = getWaServiceConfig();
  const url = `${baseUrl}${path.startsWith('/') ? path : `/${path}`}`;
  const headers = { Accept: 'application/json', ...(fetchOpts.headers || {}) };
  if (apiKey) headers['X-WA-API-Key'] = apiKey;
  if (useSupabaseSession) {
    const auth = await getWaSupabaseAuthHeader();
    if (!auth) {
      throw new Error('Inicia sesión como administrador para usar el bot de WhatsApp.');
    }
    headers.Authorization = auth;
    const sb = window.SUPABASE_CONFIG;
    if (sb?.anonKey) headers.apikey = sb.anonKey;
  }
  if (fetchOpts.body && !headers['Content-Type']) {
    headers['Content-Type'] = 'application/json';
  }
  const ctrl = timeoutMs > 0 ? new AbortController() : null;
  const timer = ctrl ? setTimeout(() => ctrl.abort(), timeoutMs) : null;
  let res;
  try {
    res = await fetch(url, { ...fetchOpts, headers, signal: ctrl?.signal });
  } catch (e) {
    if (e?.name === 'AbortError') {
      throw new Error(`Tiempo agotado (${Math.round(timeoutMs / 1000)}s). Revisa que npm start siga activo.`);
    }
    const hint = mode === 'cloud-bridge'
      ? 'Configura WA_SERVICE_URL en Supabase (Edge Functions → Secrets) con la URL de tu servidor WhatsApp en la nube.'
      : '¿Está corriendo «npm start» en whatsapp-service?';
    throw new Error(`No se pudo conectar con el bot (${baseUrl}). ${hint} (${e.message || e})`);
  } finally {
    if (timer) clearTimeout(timer);
  }
  let data = null;
  try { data = await res.json(); } catch (_) { /* ignore */ }
  if (!res.ok) {
    if (res.status === 404) {
      throw new Error(
        `Error HTTP 404 en ${url}. El servicio WhatsApp no responde ahí. ` +
        (getWaServiceConfig().mode === 'cloud-bridge'
          ? 'Despliega whatsapp-service (Railway/Docker) y define WA_SERVICE_URL en Supabase Secrets.'
          : 'En terminal: cd whatsapp-service && npm start. En local.settings.js: baseUrl http://localhost:3030')
      );
    }
    const msg = data?.error || data?.message || `Error HTTP ${res.status} (${url})`;
    throw new Error(msg);
  }
  return data;
}

const WA_MSG_SIGNATURE = '\n\n— Tecos Elite VOLLEYBALL';

function waBuildPreviewText (recipient, urgent) {
  const tutor = recipient.tutor && recipient.tutor !== '—' ? recipient.tutor : 'familia';
  const alumno = recipient.name || recipient.alumno || 'su alumno';
  const mes = recipient.mes || recipient.concepto || 'el periodo indicado';
  const monto = recipient.monto != null ? Number(recipient.monto).toLocaleString('es-MX') : '—';
  const lim = recipient.lim || '';
  const dias = recipient.dias != null ? recipient.dias : (recipient.daysLate != null ? recipient.daysLate : 0);
  const kind = recipient.billingKind || recipient.billingStatus;

  if (recipient.template === 'comprobante' || recipient.comprobanteApproved) {
    let text = `Hola ${tutor}, *confirmamos* el comprobante de *${alumno}* (${mes}).`;
    if (recipient.monto != null) text += ` Monto registrado: *$${monto}* MXN.`;
    text += '\n\n¡Gracias! Ya está al corriente.';
    return text + WA_MSG_SIGNATURE;
  }

  if (kind === 'al_corriente') {
    return `Hola ${tutor}, le informamos que *${alumno}* está *al corriente* en su mensualidad del mes en curso. ¡Gracias por su puntualidad!${WA_MSG_SIGNATURE}`;
  }
  if (kind === 'revision') {
    return `Hola ${tutor}, recibimos el comprobante de pago de *${alumno}* (${mes}). Está *en revisión*; le avisaremos al confirmarlo.${WA_MSG_SIGNATURE}`;
  }
  if (kind === 'rechazado') {
    return `Hola ${tutor}, el comprobante de *${alumno}* (${mes}) fue *rechazado*. Revise el portal o contacte administración para subir uno nuevo.${WA_MSG_SIGNATURE}`;
  }
  if (kind === 'urgente' || (urgent && kind !== 'pendiente')) {
    let text = `Hola ${tutor}, *URGENTE*: *${alumno}* tiene un adeudo de *$${monto}* MXN (${mes})`;
    if (dias > 0) text += ` con *${dias}* día(s) de atraso`;
    text += '. Favor de regularizar a la brevedad para evitar restricciones.';
    if (lim) text += ` Periodo de referencia: ${lim}.`;
    return text + WA_MSG_SIGNATURE;
  }
  if (kind === 'vencido') {
    let text = `Hola ${tutor}, la mensualidad de *${alumno}* (${mes}) tiene *atraso*`;
    if (dias > 0) text += ` de *${dias}* día(s)`;
    text += `. Saldo del mes: *$${monto}* MXN.`;
    if (lim) text += ` Referencia del periodo: ${lim}.`;
    text += '\n\nPor favor regularice pronto.';
    return text + WA_MSG_SIGNATURE;
  }
  if (kind === 'pendiente') {
    let text = `Hola ${tutor}, le recordamos la mensualidad de *${alumno}* (${mes}) por *$${monto}* MXN.`;
    if (lim) text += ` Periodo sin recargo hasta: ${lim}.`;
    return text + WA_MSG_SIGNATURE;
  }

  let text = `Hola ${tutor}, le recordamos que la mensualidad de *${alumno}* (${mes}) está pendiente por *$${monto}* MXN.`;
  if (lim) text += ` Fecha límite: ${lim}.`;
  if (urgent) text += '\n\n⚠️ Por favor regularice a la brevedad.';
  return text + WA_MSG_SIGNATURE;
}

function waPreviewPlainText (text) {
  return typeof stripWaMessageForUrl === 'function'
    ? stripWaMessageForUrl(text)
    : String(text || '').replace(/\*/g, '');
}

/** Abre WhatsApp nativo con el texto de vista previa del destinatario. */
function openWhatsAppForRecipient (recipient, { urgent } = {}) {
  if (!recipient) return false;
  const phone = recipient.tel || recipient.phone;
  const isUrgent = !!(urgent || recipient.urgent);
  const full = waBuildPreviewText(recipient, isUrgent);
  const plain = waPreviewPlainText(full);
  if (typeof openWhatsAppChat === 'function') {
    return openWhatsAppChat(phone, plain);
  }
  if (typeof buildWhatsAppUrl === 'function') {
    const url = buildWhatsAppUrl(phone, plain);
    if (!url) return false;
    window.open(url, '_blank', 'noopener,noreferrer');
    return true;
  }
  return false;
}

const WaConnectionPanel = ({ theme }) => {
  const [status, setStatus] = React.useState(null);
  const [qr, setQr] = React.useState(null);
  const [loading, setLoading] = React.useState(true);
  const [err, setErr] = React.useState('');
  const defaultDial = typeof DEFAULT_PHONE_DIAL !== 'undefined' ? DEFAULT_PHONE_DIAL : '+52';
  const [testDial, setTestDial] = React.useState(defaultDial);
  const [testNational, setTestNational] = React.useState('');
  const [testMsg, setTestMsg] = React.useState('Hola, prueba desde Tecos Elite VOLLEYBALL 🏐');
  const [sending, setSending] = React.useState(false);
  const [reconnecting, setReconnecting] = React.useState(false);
  const [loggingOut, setLoggingOut] = React.useState(false);
  const [syncing, setSyncing] = React.useState(false);
  const [openingChrome, setOpeningChrome] = React.useState(false);
  const [syncNote, setSyncNote] = React.useState('');
  const [lastSyncAt, setLastSyncAt] = React.useState(null);
  const pollRef = React.useRef(null);

  const applyStatus = React.useCallback(async (s) => {
    setStatus(s);
    if (s.readyToSend) {
      setQr(null);
      setSyncNote('Listo para enviar mensajes.');
    } else if (s.status === 'connecting') {
      setSyncNote('Sincronizando WhatsApp en Chrome…');
      if (s.qrAvailable) {
        const q = await waServiceFetch('/qr', { timeoutMs: 8000 });
        setQr(q.qr || null);
      }
    } else if (s.status === 'qr' || s.qrAvailable) {
      setSyncNote('Sesión cerrada — escanea el QR o pulsa Reconectar.');
      const q = await waServiceFetch('/qr', { timeoutMs: 8000 });
      setQr(q.qr || null);
    } else {
      setQr(null);
      setSyncNote(s.error || 'Sin sesión activa. Pulsa Reconectar.');
    }
  }, []);

  const refresh = React.useCallback(async () => {
    setErr('');
    try {
      const s = await waServiceFetch('/status', { timeoutMs: 12000 });
      await applyStatus(s);
    } catch (e) {
      setErr(e.message);
      setStatus(null);
      setSyncNote('');
    } finally {
      setLoading(false);
    }
  }, [applyStatus]);

  const syncNow = async () => {
    if (syncing || reconnecting) return;
    setSyncing(true);
    setErr('');
    setSyncNote('Comprobando Chrome y WhatsApp…');
    try {
      let s;
      try {
        s = await waServiceFetch('/sync', { method: 'POST', body: '{}', timeoutMs: 20000 });
      } catch (e) {
        if (!String(e.message || '').includes('404')) throw e;
        s = await waServiceFetch('/status', { timeoutMs: 12000 });
      }
      await applyStatus(s);
      setLastSyncAt(s.syncedAt ? new Date(s.syncedAt) : new Date());
    } catch (e) {
      setErr(e.message);
      setSyncNote('');
    } finally {
      setSyncing(false);
      setLoading(false);
    }
  };

  React.useEffect(() => {
    refresh();
    const t = setInterval(refresh, 8000);
    return () => {
      clearInterval(t);
      if (pollRef.current) clearInterval(pollRef.current);
    };
  }, [refresh]);

  const waitForQrOrConnected = React.useCallback(async (onDone) => {
    if (pollRef.current) clearInterval(pollRef.current);
    let tries = 0;
    const pollOnce = async () => {
      tries += 1;
      try {
        const s = await waServiceFetch('/status', { timeoutMs: 12000 });
        await applyStatus(s);
        if (s.readyToSend || s.qrAvailable || s.status === 'qr') return true;
      } catch (e) {
        setErr(e.message);
      }
      return tries >= 48;
    };
    if (await pollOnce()) {
      onDone();
      return;
    }
    pollRef.current = setInterval(async () => {
      if (await pollOnce()) {
        clearInterval(pollRef.current);
        pollRef.current = null;
        onDone();
      }
    }, 2500);
  }, [applyStatus]);

  const handleOpenChrome = async () => {
    if (openingChrome || reconnecting || loggingOut) return;
    setOpeningChrome(true);
    setErr('');
    setSyncNote('Abriendo Google Chrome con WhatsApp Web…');
    try {
      const s = await waRequestOpenChrome();
      await applyStatus(s);
      setLastSyncAt(s.syncedAt ? new Date(s.syncedAt) : new Date());
      setSyncNote(s.message || 'Chrome abierto. Escanea el QR si aparece.');
      if (s.qrAvailable) {
        const q = await waServiceFetch('/qr', { timeoutMs: 8000 });
        setQr(q.qr || null);
      }
    } catch (e) {
      alert(e.message);
      setSyncNote('');
    } finally {
      setOpeningChrome(false);
    }
  };

  const handleReconnect = async () => {
    if (reconnecting || loggingOut) return;
    setReconnecting(true);
    setErr('');
    setQr(null);
    setSyncNote('Generando código QR…');
    try {
      await waRequestReconnect();
      await waitForQrOrConnected(() => setReconnecting(false));
    } catch (e) {
      alert(e.message);
      setReconnecting(false);
    }
  };

  const handleLogout = async () => {
    if (reconnecting || loggingOut) return;
    if (!confirm(
      '¿Cerrar sesión de WhatsApp?\n\n'
      + 'Se desvincula el teléfono actual en esta computadora. '
      + 'Después escanea el QR con el otro número (WhatsApp → Dispositivos vinculados).'
    )) return;
    setLoggingOut(true);
    setErr('');
    setQr(null);
    setSyncNote('Cerrando sesión…');
    try {
      await waRequestLogout();
      setSyncNote('Sesión cerrada. Escanea el QR con el nuevo teléfono.');
      await waitForQrOrConnected(() => setLoggingOut(false));
    } catch (e) {
      alert(e.message);
      setLoggingOut(false);
    }
  };

  React.useEffect(() => {
    if (status?.status !== 'connected') return;
    const run = () => { if (typeof waProcessDueScheduled === 'function') waProcessDueScheduled().catch(() => {}); };
    run();
    const t = setInterval(run, 60000);
    return () => clearInterval(t);
  }, [status?.status]);

  const sendTest = async () => {
    const phone = typeof combinePhoneWithDial === 'function'
      ? combinePhoneWithDial(testDial, testNational)
      : `${testDial || defaultDial} ${testNational}`.trim();
    if (!phone.replace(/\D/g, '').length) { alert('Escribe un teléfono de prueba.'); return; }
    if (!canSend) {
      alert('WhatsApp no está listo para enviar. Pulsa «Sincronizar» o «Reconectar».');
      return;
    }
    setSending(true);
    try {
      const r = await waServiceFetch('/send-text', {
        method: 'POST',
        body: JSON.stringify({ phone, text: testMsg }),
        timeoutMs: 35000,
      });
      alert(
        `Mensaje enviado a ${phone}.\n\n`
        + 'Debe llegar al WhatsApp de ese número como mensaje entrante desde la línea vinculada en Chrome. '
        + 'Si no llega, abre Chrome del bot, busca el número y envía un mensaje manual una vez; luego prueba de nuevo.'
        + (r.chatId ? `\n\nChat: ${r.chatId}` : '')
      );
    } catch (e) {
      alert(e.message);
      await syncNow();
    } finally {
      setSending(false);
    }
  };

  const canSend = status?.readyToSend === true;
  const st = status?.status || 'unknown';
  const displaySt = st === 'connected' && !canSend ? 'connecting' : st;
  const sessionBusy = reconnecting || loggingOut || openingChrome;
  const showOpenChromeBtn = !canSend && !sessionBusy
    && (st === 'disconnected' || st === 'error' || st === 'starting' || (status?.error || '').includes('Chrome'));
  const showReconnectBtn = !canSend && !sessionBusy
    && (st === 'disconnected' || st === 'qr' || st === 'error');
  const showLogoutBtn = (canSend || st === 'connected' || st === 'connecting') && !sessionBusy;
  const statusColor = canSend ? theme.success
    : displaySt === 'connecting' ? theme.primary
      : displaySt === 'qr' ? theme.warning
        : displaySt === 'error' ? theme.danger : theme.textMute;

  return (
    <Card theme={theme} style={{ marginBottom: 18 }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', flexWrap: 'wrap', gap: 12, marginBottom: 16 }}>
        <div>
          <h3 style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 24, color: theme.text, letterSpacing: 1, margin: 0, fontWeight: 400 }}>
            CONEXIÓN DEL BOT
          </h3>
          <p style={{ color: theme.textDim, fontSize: 12, fontFamily: 'Inter, sans-serif', margin: '6px 0 0' }}>
            {getWaServiceConfig().mode === 'cloud-bridge'
              ? 'Bot en la nube (vía Supabase) · escanea el QR aquí'
              : `Servicio local OpenWA · ${getWaServiceConfig().baseUrl}`}
            {status?.browserExecutable && (
              <> · Navegador: <strong style={{ color: theme.text }}>Google Chrome</strong></>
            )}
          </p>
          {status?.popupEnabled && (
            <p style={{ margin: '6px 0 0', fontSize: 11, color: theme.warning, fontFamily: 'Inter, sans-serif' }}>
              WA_POPUP activo: puede abrir Safari. En <code>whatsapp-service/.env</code> pon <code>WA_POPUP=false</code> y reinicia <code>npm start</code>.
            </p>
          )}
        </div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
          <span style={{ width: 10, height: 10, borderRadius: '50%', background: statusColor, boxShadow: `0 0 8px ${statusColor}` }} />
          <span style={{ fontSize: 12, fontWeight: 700, color: statusColor, fontFamily: 'Inter, sans-serif', textTransform: 'uppercase' }}>
            {canSend ? 'Conectado' : st === 'qr' ? 'Sesión cerrada' : st === 'connected' ? 'Cargando envíos…' : displaySt === 'connecting' ? 'Sincronizando…' : displaySt === 'starting' ? 'Iniciando…' : displaySt === 'error' ? 'Error' : 'Desconectado'}
          </span>
          {showOpenChromeBtn && (
            <Btn theme={theme} kind="primary" size="sm" onClick={handleOpenChrome}
              disabled={openingChrome}
              title="Abre Google Chrome con el perfil del bot y WhatsApp Web">
              {openingChrome ? 'Abriendo…' : 'Abrir WhatsApp'}
            </Btn>
          )}
          {showReconnectBtn && (
            <Btn theme={theme} kind="wa" size="sm" icon="refresh" onClick={handleReconnect}>
              Reconectar
            </Btn>
          )}
          {showLogoutBtn && (
            <Btn theme={theme} kind="danger" size="sm" onClick={handleLogout} title="Desvincular teléfono y mostrar QR para otro número">
              Cerrar sesión
            </Btn>
          )}
          {reconnecting && (
            <Btn theme={theme} kind="soft" size="sm" disabled>Generando QR…</Btn>
          )}
          {loggingOut && (
            <Btn theme={theme} kind="soft" size="sm" disabled>Cerrando sesión…</Btn>
          )}
          <Btn theme={theme} kind="primary" size="sm" icon="refresh" onClick={syncNow}
            disabled={loading || sessionBusy || syncing || openingChrome}
            title="Lee Chrome y actualiza el estado Conectado / QR">
            {syncing ? 'Sincronizando…' : 'Sincronizar'}
          </Btn>
        </div>
      </div>

      {(syncNote || lastSyncAt) && (
        <p style={{
          fontSize: 12, color: syncNote.includes('Listo') ? theme.success : theme.textDim,
          margin: '0 0 12px', fontFamily: 'Inter, sans-serif', lineHeight: 1.5,
        }}>
          {syncNote}
          {lastSyncAt && (
            <span style={{ color: theme.textMute }}> · Última sync: {lastSyncAt.toLocaleTimeString('es-MX')}</span>
          )}
        </p>
      )}

      {err && (
        <div style={{
          padding: 12, borderRadius: 10, marginBottom: 14,
          background: `${theme.danger}18`, border: `1px solid ${theme.danger}44`,
          fontSize: 13, color: theme.danger, fontFamily: 'Inter, sans-serif', lineHeight: 1.55,
        }}>
          <strong>No se pudo contactar el servicio:</strong> {err}
          <div style={{ marginTop: 10, color: theme.textDim, fontSize: 12 }}>
            {getWaServiceConfig().mode === 'cloud-bridge' ? (
              <>
                1) Despliega <code>whatsapp-service</code> en Railway u otro servidor (ver <code>WHATSAPP.md</code>).<br />
                2) Supabase → Edge Functions → despliega <code>wa-bridge</code> y agrega el secret <code>WA_SERVICE_URL</code> (URL HTTPS del bot).<br />
                3) Ejecuta la migración <code>071_academy_wa_bridge_url.sql</code>.<br />
                4) Recarga esta página, escanea el QR y prueba un mensaje.
              </>
            ) : (
              <>
                1) En terminal: <code>cd whatsapp-service && npm install && npm start</code><br />
                2) Deja esa ventana abierta. En <code>local.settings.js</code>: <code>baseUrl: 'http://localhost:3030'</code><br />
                3) Mac: <code>WA_POPUP=false</code> en <code>.env</code>; si falla: <code>npm run reset</code>.<br />
                4) Escanea el QR aquí o en Chrome.
              </>
            )}
          </div>
        </div>
      )}

      {status?.status === 'error' && status?.error && (
        <div style={{
          padding: 12, borderRadius: 10, marginBottom: 14,
          background: `${theme.warning}18`, border: `1px solid ${theme.warning}44`,
          fontSize: 12, color: theme.text, fontFamily: 'Inter, sans-serif', lineHeight: 1.5,
        }}>
          <strong>Error del bot:</strong> {status.error}
          {(status.error || '').includes('Connection closed') || (status.error || '').includes('page has been closed') ? (
            <p style={{ margin: '8px 0 0', fontSize: 11, color: theme.textDim, lineHeight: 1.45 }}>
              Suele pasar si cerraste la ventana de Chrome antes de escanear el QR. Usa <strong>Resetear sesión</strong> y no cierres Chrome hasta ver «Conectado».
            </p>
          ) : null}
          {(status.error || '').includes('Failed to launch the browser') || (status.error || '').includes('30000ms') || (status.error || '').includes('Waiting failed') ? (
            <p style={{ margin: '8px 0 0', fontSize: 11, color: theme.textDim, lineHeight: 1.45 }}>
              En terminal: <code>cd whatsapp-service && npm install && npm run reset && npm start</code> (aplica parche OpenWA). Usa Node 20 si puedes. No cierres Chrome hasta ver Conectado.
            </p>
          ) : null}
          <div style={{ marginTop: 8, display: 'flex', gap: 8, flexWrap: 'wrap' }}>
            <Btn theme={theme} kind="soft" size="sm" onClick={async () => {
              try {
                await waServiceFetch('/retry-boot', { method: 'POST', body: '{}' });
                refresh();
              } catch (e) { alert(e.message); }
            }}>Reintentar conexión</Btn>
            <Btn theme={theme} kind="danger" size="sm" icon="refresh" onClick={async () => {
              if (!confirm('¿Resetear sesión de WhatsApp?\n\nSe borra la sesión local y se abre Chrome de nuevo. Deberás escanear el QR otra vez.')) return;
              try {
                const r = await waServiceFetch('/reset-session', { method: 'POST', body: '{}' });
                alert(r.hint || 'Sesión reiniciada. Espera el QR.');
                refresh();
              } catch (e) { alert(e.message); }
            }}>Resetear sesión</Btn>
          </div>
        </div>
      )}

      {loading && !status ? <LoadingBlock theme={theme} /> : null}

      {canSend && (
        <div style={{ marginBottom: 14, fontSize: 13, fontFamily: 'Inter, sans-serif', color: theme.textDim }}>
          Línea: <strong style={{ color: theme.text }}>{status.pushname || 'WhatsApp'}</strong>
          {status.phone ? <> · {status.phone}</> : null}
        </div>
      )}

      {st === 'connected' && !canSend && (
        <p style={{ fontSize: 13, color: theme.primary, marginBottom: 12, fontFamily: 'Inter, sans-serif' }}>
          Chrome tiene sesión pero WhatsApp Web aún carga el motor de envío. Pulsa <strong>Sincronizar</strong> (espera hasta 2 min) o deja la ventana de Chrome abierta sin cerrarla.
        </p>
      )}

      {st === 'qr' && !err && (
        <p style={{ fontSize: 13, color: theme.warning, marginBottom: 12, fontFamily: 'Inter, sans-serif', lineHeight: 1.55 }}>
          {status?.error || 'Sesión cerrada en WhatsApp.'} Pulsa <strong>Reconectar</strong> para generar el QR o escanéalo abajo / en Chrome del bot.
        </p>
      )}

      {st === 'disconnected' && !err && !reconnecting && (
        <p style={{ fontSize: 13, color: theme.warning, marginBottom: 12, fontFamily: 'Inter, sans-serif', lineHeight: 1.55 }}>
          Debe estar abierta la ventana de <strong>Google Chrome del bot</strong> (perfil Tecos, carpeta <code style={{ fontSize: 11 }}>whatsapp-service/_IGNORE_tecos-elite</code>).
          No uses solo tu Chrome personal. Si no aparece, pulsa <strong>Reconectar</strong>.
        </p>
      )}

      {reconnecting && (
        <p style={{ fontSize: 13, color: theme.primary, marginBottom: 12, fontFamily: 'Inter, sans-serif' }}>
          Reiniciando sesión y generando QR… <strong>No cierres Chrome</strong>. El código aparecerá aquí en unos segundos.
        </p>
      )}

      {st === 'connecting' && (
        <p style={{ fontSize: 13, color: theme.primary, marginBottom: 12, fontFamily: 'Inter, sans-serif' }}>
          QR escaneado. Preparando envíos en Chrome… <strong>No cierres Chrome</strong>. Cuando diga «Conectado» ya puedes enviar; si tarda, pulsa <strong>Sincronizar</strong>.
        </p>
      )}

      {(st === 'starting' || st === 'qr') && !qr && !err && (
        <p style={{ fontSize: 13, color: theme.textDim, marginBottom: 12, fontFamily: 'Inter, sans-serif' }}>
          Esperando código QR… Si tarda más de 1 minuto, ejecuta <code>npm run reset</code> en <code>whatsapp-service/</code> y vuelve a <code>npm start</code>.
        </p>
      )}

      {qr && !canSend && (
        <div style={{ display: 'grid', gridTemplateColumns: 'auto 1fr', gap: 20, alignItems: 'center', marginBottom: 16 }}>
          <img src={qr} alt="Código QR WhatsApp" style={{ width: 220, height: 220, borderRadius: 12, background: '#fff', padding: 8 }} />
          <div style={{ fontSize: 13, color: theme.textDim, fontFamily: 'Inter, sans-serif', lineHeight: 1.6 }}>
            Debe abrirse <strong style={{ color: theme.text }}>Google Chrome</strong> (no Safari). Pulsa <strong>Sincronizar</strong> justo antes de escanear (QR fresco).<br />
            <strong style={{ color: theme.warning }}>No cierres Chrome</strong> hasta que diga Conectado.<br />
            <strong style={{ color: theme.text }}>1.</strong> Mac y teléfono con <strong>internet</strong> (misma WiFi ayuda; no uses VPN).<br />
            <strong style={{ color: theme.text }}>2.</strong> WhatsApp en el teléfono → Menú → Dispositivos vinculados → Vincular dispositivo.<br />
            <strong style={{ color: theme.text }}>3.</strong> Escanea en menos de 20 s. Si dice «Revisa tu conexión», espera QR nuevo y repite.<br />
            <strong style={{ color: theme.text }}>4.</strong> También puedes escanear el QR en la ventana de Chrome del bot.
          </div>
        </div>
      )}

      {(st === 'qr' || st === 'connecting') && !canSend && (
        <div style={{
          padding: 12, borderRadius: 10, marginBottom: 14,
          background: `${theme.warning}12`, border: `1px solid ${theme.warning}44`,
          fontSize: 12, color: theme.textDim, fontFamily: 'Inter, sans-serif', lineHeight: 1.55,
        }}>
          <strong style={{ color: theme.warning }}>Si al escanear dice «Revisa tu conexión»:</strong>
          <ul style={{ margin: '8px 0 0', paddingLeft: 18 }}>
            <li>Deja abierta la ventana de <strong>Chrome</strong> del bot (no la cierres con Cmd+Q).</li>
            <li>Desactiva VPN en Mac y en el teléfono; prueba con datos móviles si la WiFi falla.</li>
            <li>Actualiza WhatsApp en el teléfono y reinicia el servicio: <code>npm start</code> en <code>whatsapp-service</code>.</li>
            <li>Pulsa <strong>Reconectar</strong>, espera QR nuevo y escanea de inmediato.</li>
          </ul>
        </div>
      )}

      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12, marginTop: 8 }}>
        <PhoneInput theme={theme} label="Teléfono de prueba" icon="wa"
          dial={testDial} national={testNational}
          onDialChange={setTestDial} onNationalChange={setTestNational} />
        <Input theme={theme} label="Mensaje de prueba" placeholder="Texto…" icon="chat"
          value={testMsg} onChange={e => setTestMsg(e.target.value)} />
      </div>
      <div style={{ marginTop: 12 }}>
        <Btn theme={theme} kind="wa" icon="send" onClick={sendTest} disabled={sending || !canSend}>
          {sending ? 'Enviando…' : 'Enviar mensaje de prueba'}
        </Btn>
        {!canSend && (
          <span style={{ marginLeft: 10, fontSize: 11, color: theme.textMute, fontFamily: 'Inter, sans-serif' }}>
            Conecta WhatsApp y pulsa Sincronizar antes de enviar.
          </span>
        )}
        {canSend && (
          <p style={{ margin: '10px 0 0', fontSize: 11, color: theme.textMute, fontFamily: 'Inter, sans-serif', lineHeight: 1.45 }}>
            El mensaje sale desde la línea del bot (Chrome). Elige el <strong>prefijo del país</strong> (+52, +1, +34, etc.) y el número con WhatsApp.
            Si nunca has escrito a ese número, ábrelo primero en Chrome del bot.
          </p>
        )}
      </div>

    </Card>
  );
};

const WaToggle = ({ theme, active, onChange }) => (
  <button type="button" onClick={() => onChange?.(!active)} aria-pressed={active} style={{
    width: 40, height: 22, borderRadius: 11, border: 'none', padding: 0,
    background: active ? theme.success : theme.border,
    position: 'relative', cursor: 'pointer',
  }}>
    <span style={{
      position: 'absolute', top: 3, left: active ? 21 : 3, width: 16, height: 16,
      borderRadius: '50%', background: '#fff', transition: 'left .2s',
      boxShadow: '0 2px 4px rgba(0,0,0,0.2)',
    }} />
  </button>
);

const useWaReload = (loadFn, deps = []) => {
  const [tick, setTick] = React.useState(0);
  React.useEffect(() => {
    const on = () => setTick(t => t + 1);
    window.addEventListener('tecos:wa-changed', on);
    return () => window.removeEventListener('tecos:wa-changed', on);
  }, []);
  return useLiveData(loadFn, [...deps, tick]);
};

const WaDashboard = ({ theme, onPreview }) => {
  const { data: log } = useWaReload(() => (typeof waFetchMessageLog === 'function' ? waFetchMessageLog({ limit: 8 }) : Promise.resolve([])), []);
  const { data: scheduled } = useWaReload(() => (typeof waFetchScheduled === 'function' ? waFetchScheduled() : Promise.resolve([])), []);
  const { data: campaigns } = useWaReload(() => (typeof waFetchCampaigns === 'function' ? waFetchCampaigns() : Promise.resolve([])), []);

  const todayStart = new Date();
  todayStart.setHours(0, 0, 0, 0);
  const sentToday = (log || []).filter(r => new Date(r.created_at) >= todayStart).length;
  const pendingSched = (scheduled || []).filter(s => s.status === 'programado').length;
  const activeCamps = (campaigns || []).filter(c => c.status === 'programado' || c.status === 'borrador').length;

  const metrics = [
    { l: 'Enviados hoy', v: String(sentToday), i: 'send', c: theme.wa, t: '' },
    { l: 'Programados', v: String(pendingSched), i: 'clock', c: theme.warning, t: '' },
    { l: 'Campañas', v: String((campaigns || []).length), i: 'megaphone', c: theme.info, t: '' },
    { l: 'Activas', v: String(activeCamps), i: 'flame', c: theme.urgent, t: '' },
  ];

  return (
    <div className="tecos-page-shell" style={{ padding: '0 32px 32px', display: 'grid', gap: 18 }}>
      <div className="tecos-grid-4" style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 14 }}>
        {metrics.map(m => <MetricCard key={m.l} m={m} theme={theme} />)}
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: '1.4fr 1fr', gap: 18 }}>
        <Card theme={theme}>
          <h3 style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 22, color: theme.text, letterSpacing: 1, margin: '0 0 16px', fontWeight: 400 }}>ÚLTIMOS MENSAJES</h3>
          {(log || []).length === 0 ? (
            <p style={{ color: theme.textDim, fontSize: 13, fontFamily: 'Inter, sans-serif' }}>Sin envíos registrados. Usa Recordatorios o el bot conectado.</p>
          ) : (
            <div style={{ display: 'grid', gap: 8 }}>
              {(log || []).slice(0, 6).map((m) => (
                <div key={m.id} style={{
                  display: 'flex', alignItems: 'center', gap: 12, padding: 12,
                  borderRadius: 10, background: theme.bgInput, border: `1px solid ${theme.border}`,
                }}>
                  <Avatar name={m.student_name || '?'} size={36} theme={theme} />
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontSize: 13, color: theme.text, fontWeight: 600, fontFamily: 'Inter, sans-serif' }}>{m.student_name || '—'}</div>
                    <div style={{ fontSize: 12, color: theme.textDim, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{m.template_name || m.message_type}</div>
                  </div>
                  <StatusPill status={m.status} theme={theme} />
                </div>
              ))}
            </div>
          )}
        </Card>
        <Card theme={theme}>
          <h3 style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 22, color: theme.text, letterSpacing: 1, margin: '0 0 16px', fontWeight: 400 }}>PRÓXIMOS PROGRAMADOS</h3>
          {(scheduled || []).filter(s => s.status === 'programado').length === 0 ? (
            <p style={{ color: theme.textDim, fontSize: 13, fontFamily: 'Inter, sans-serif' }}>Nada programado. Ve a Programar.</p>
          ) : (
            <div style={{ display: 'grid', gap: 8 }}>
              {(scheduled || []).filter(s => s.status === 'programado').slice(0, 5).map((p) => (
                <div key={p.id} style={{ padding: 12, borderRadius: 10, background: `${theme.primary}11`, border: `1px solid ${theme.primary}33` }}>
                  <div style={{ fontSize: 13, fontWeight: 600, color: theme.text, fontFamily: 'Inter, sans-serif' }}>{p.template_name || 'Mensaje'}</div>
                  <div style={{ fontSize: 11, color: theme.textDim, marginTop: 4 }}>
                    {new Date(p.scheduled_at).toLocaleString('es-MX')} · {(p.recipient_json || []).length} destinatario(s)
                  </div>
                </div>
              ))}
            </div>
          )}
        </Card>
      </div>
    </div>
  );
};

const WaRecordatorios = ({ theme, onPreview }) => {
  const { data: records, loading } = useWaReload(() => (typeof waFetchReminderRecipients === 'function' ? waFetchReminderRecipients() : Promise.resolve([])), []);
  const list = records || [];
  const [sendingAll, setSendingAll] = React.useState(false);

  const sendAll = async () => {
    if (!list.length) return;
    if (!window.confirm(`¿Enviar recordatorio a ${list.length} tutor(es)?`)) return;
    setSendingAll(true);
    let ok = 0;
    for (const r of list) {
      try {
        if (typeof waSendFromRecipient === 'function') await waSendFromRecipient(r, { urgent: false });
        ok += 1;
      } catch (e) { console.warn(e); }
    }
    setSendingAll(false);
    alert(`Enviados: ${ok} de ${list.length}`);
  };

  return (
    <div className="tecos-page-shell" style={{ padding: 32 }}>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 14, marginBottom: 18 }}>
        {[
          { l: 'Con adeudo', v: String(list.length), c: theme.danger, i: 'warning' },
          { l: 'Con teléfono', v: String(list.filter(r => r.tel).length), c: theme.success, i: 'check' },
          { l: 'Listos bot', v: String(list.length), c: theme.wa, i: 'send' },
        ].map(m => <MetricCard key={m.l} m={m} theme={theme} />)}
      </div>
      <Card theme={theme} style={{ padding: 0, overflow: 'hidden' }}>
        <div style={{ padding: '14px 18px', display: 'flex', justifyContent: 'space-between', alignItems: 'center', borderBottom: `1px solid ${theme.border}` }}>
          <h3 style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 22, color: theme.text, letterSpacing: 1, margin: 0, fontWeight: 400 }}>RECORDATORIOS (DATOS REALES)</h3>
          <Btn theme={theme} kind="wa" size="sm" icon="send" onClick={sendAll} disabled={sendingAll || !list.length}>
            {sendingAll ? 'Enviando…' : 'Enviar a todos'}
          </Btn>
        </div>
        {loading ? <LoadingBlock theme={theme} label="Cargando alumnos…" /> : list.length === 0 ? (
          <p style={{ padding: 24, color: theme.textDim, fontFamily: 'Inter, sans-serif' }}>No hay alumnos con adeudo y teléfono de tutor.</p>
        ) : (
          <table style={{ width: '100%', borderCollapse: 'collapse', fontFamily: 'Inter, sans-serif' }}>
            <thead>
              <tr style={{ background: theme.bgInput, borderBottom: `1px solid ${theme.border}` }}>
                {['Alumno', 'Tutor', 'Mes', 'Monto', 'Acciones'].map(h => (
                  <th key={h} style={{ padding: '12px 16px', textAlign: 'left', fontSize: 10, fontWeight: 700, color: theme.textMute, letterSpacing: 1, textTransform: 'uppercase' }}>{h}</th>
                ))}
              </tr>
            </thead>
            <tbody>
              {list.map((r, i) => (
                <tr key={r.id || i} style={{ borderBottom: `1px solid ${theme.border}` }}>
                  <td style={typeof tdStyle === 'function' ? tdStyle(theme) : { padding: 12 }}>{r.name}<div style={{ fontSize: 11, color: theme.textMute }}>{r.id}</div></td>
                  <td style={typeof tdStyle === 'function' ? tdStyle(theme) : { padding: 12 }}>{r.tutor}<div style={{ fontSize: 11, color: theme.textMute }}>{r.tel}</div></td>
                  <td style={typeof tdStyle === 'function' ? tdStyle(theme) : { padding: 12 }}>{r.mes}</td>
                  <td style={typeof tdStyle === 'function' ? tdStyle(theme) : { padding: 12 }}>${Number(r.monto).toLocaleString('es-MX')}</td>
                  <td style={typeof tdStyle === 'function' ? tdStyle(theme) : { padding: 12 }}>
                    <button type="button" onClick={() => onPreview?.(r)} style={typeof iconBtnSmall === 'function' ? iconBtnSmall(theme, theme.wa) : {}} title="Enviar"><Icon name="wa" size={14} /></button>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        )}
      </Card>
    </div>
  );
};

const WaPendientes = ({ theme, onPreview }) => {
  const { data: jobs, loading, reload } = useWaReload(() => (typeof waFetchScheduled === 'function' ? waFetchScheduled() : Promise.resolve([])), []);
  const pending = (jobs || []).filter(j => j.status === 'programado');

  const runDue = async () => {
    if (typeof waProcessDueScheduled !== 'function') return;
    const n = await waProcessDueScheduled();
    reload?.();
    alert(n ? `Procesados ${n} mensaje(s).` : 'No había envíos pendientes por hora.');
  };

  return (
    <div className="tecos-page-shell" style={{ padding: 32 }}>
      <Card theme={theme} style={{ padding: 0, overflow: 'hidden' }}>
        <div style={{ padding: '14px 18px', display: 'flex', justifyContent: 'space-between', alignItems: 'center', borderBottom: `1px solid ${theme.border}` }}>
          <h3 style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 22, color: theme.text, letterSpacing: 1, margin: 0, fontWeight: 400 }}>MENSAJES PROGRAMADOS PENDIENTES</h3>
          <Btn theme={theme} kind="wa" size="sm" icon="send" onClick={runDue}>Procesar vencidos</Btn>
        </div>
        {loading ? <LoadingBlock theme={theme} /> : pending.length === 0 ? (
          <p style={{ padding: 24, color: theme.textDim, fontFamily: 'Inter, sans-serif' }}>Sin cola pendiente.</p>
        ) : (
          <table style={{ width: '100%', borderCollapse: 'collapse' }}>
            <thead>
              <tr style={{ background: theme.bgInput }}>
                {['Plantilla / mensaje', 'Programado', 'Destinatarios', 'Estado', ''].map(h => <th key={h} style={{ padding: 12, fontSize: 10, textAlign: 'left', color: theme.textMute }}>{h}</th>)}
              </tr>
            </thead>
            <tbody>
              {pending.map((r) => (
                <tr key={r.id} style={{ borderBottom: `1px solid ${theme.border}` }}>
                  <td style={{ padding: 12 }}>{r.template_name || (r.message_body || '').slice(0, 40)}</td>
                  <td style={{ padding: 12 }}>{new Date(r.scheduled_at).toLocaleString('es-MX')}</td>
                  <td style={{ padding: 12 }}>{(r.recipient_json || []).length}</td>
                  <td style={{ padding: 12 }}><StatusPill status={r.status} theme={theme} /></td>
                  <td style={{ padding: 12 }}>
                    <Btn theme={theme} size="sm" kind="ghost" icon="x" onClick={() => waDeleteScheduled(r.id).then(() => reload?.())}>Cancelar</Btn>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        )}
      </Card>
    </div>
  );
};

const WaUrgentes = ({ theme, onPreview }) => {
  const { data: recipients } = useWaReload(() => (typeof waFetchReminderRecipients === 'function' ? waFetchReminderRecipients() : Promise.resolve([])), []);
  return (
    <div className="tecos-page-shell" style={{ padding: 32 }}>
      <Card theme={theme} style={{ padding: 24, marginBottom: 18, borderColor: `${theme.urgent}44` }}>
        <h3 style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 28, color: theme.text, margin: 0 }}>MENSAJES URGENTES</h3>
        <p style={{ color: theme.textDim, fontSize: 13, marginTop: 8 }}>Alumnos con adeudo — envío urgente por el bot.</p>
      </Card>
      <div style={{ display: 'grid', gap: 10 }}>
        {(recipients || []).map((r) => (
          <Card key={r.id} theme={theme} style={{ padding: 14, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
            <div>
              <div style={{ fontWeight: 600 }}>{r.name}</div>
              <div style={{ fontSize: 12, color: theme.textDim }}>{r.tutor} · ${Number(r.monto).toLocaleString('es-MX')}</div>
            </div>
            <Btn theme={theme} kind="primary" icon="flame" size="sm" style={{ background: theme.urgent }} onClick={() => onPreview?.({ ...r, urgent: true })}>Enviar urgente</Btn>
          </Card>
        ))}
        {!(recipients || []).length && <p style={{ color: theme.textDim }}>No hay alumnos con adeudo y teléfono.</p>}
      </div>
    </div>
  );
};

const WaCampanas = ({ theme, onPreview }) => {
  const { data: camps, loading, reload } = useWaReload(() => (typeof waFetchCampaigns === 'function' ? waFetchCampaigns() : Promise.resolve([])), []);
  const { data: templates } = useWaReload(() => (typeof waFetchTemplates === 'function' ? waFetchTemplates() : Promise.resolve([])), []);
  const [modal, setModal] = React.useState(null);
  const [busy, setBusy] = React.useState(false);

  const save = async () => {
    setBusy(true);
    try {
      await waSaveCampaign(modal);
      setModal(null);
      reload?.();
    } catch (e) { alert(e.message); }
    setBusy(false);
  };

  const runCampaign = async (c) => {
    const tpl = (templates || []).find(t => t.id === c.template_id) || (templates || [])[0];
    if (!tpl) { alert('Asigna una plantilla a la campaña.'); return; }
    let recipients = [];
    if (c.target_mode === 'adeudo' && typeof waFetchReminderRecipients === 'function') {
      recipients = await waFetchReminderRecipients();
    }
    if (!recipients.length) { alert('Sin destinatarios.'); return; }
    if (!window.confirm(`Enviar campaña "${c.name}" a ${recipients.length} contacto(s)?`)) return;
    setBusy(true);
    let sent = 0;
    for (const r of recipients) {
      try {
        await waSendTemplateToRecipient(tpl, r);
        sent += 1;
      } catch (_) { /* skip */ }
    }
    await waSaveCampaign({ ...c, status: 'enviado', sent_count: sent, pending_count: 0 });
    reload?.();
    setBusy(false);
    alert(`Campaña enviada: ${sent} mensajes.`);
  };

  return (
    <div className="tecos-page-shell" style={{ padding: 32 }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 18 }}>
        <h3 style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 28, color: theme.text, margin: 0 }}>CAMPAÑAS</h3>
        <Btn theme={theme} icon="plus" onClick={() => setModal({ name: '', description: '', tipo: 'Mensualidades', target_mode: 'adeudo', status: 'borrador', template_id: templates?.[0]?.id })}>Nueva campaña</Btn>
      </div>
      {loading ? <LoadingBlock theme={theme} /> : (
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))', gap: 14 }}>
          {(camps || []).map((c) => (
            <Card key={c.id} theme={theme}>
              <Badge theme={theme} color="primary">{c.tipo}</Badge>
              <h4 style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 22, margin: '8px 0' }}>{c.name}</h4>
              <p style={{ fontSize: 12, color: theme.textDim }}>{c.description}</p>
              <StatusPill status={c.status} theme={theme} />
              <div style={{ display: 'flex', gap: 6, marginTop: 12, flexWrap: 'wrap' }}>
                <Btn theme={theme} kind="wa" size="sm" icon="send" disabled={busy} onClick={() => runCampaign(c)}>Enviar</Btn>
                <Btn theme={theme} kind="soft" size="sm" icon="edit" onClick={() => setModal({ ...c })}>Editar</Btn>
                <Btn theme={theme} kind="ghost" size="sm" icon="trash" onClick={() => waDeleteCampaign(c.id).then(() => reload?.())}>Eliminar</Btn>
              </div>
            </Card>
          ))}
        </div>
      )}
      <Modal open={!!modal} onClose={() => setModal(null)} theme={theme} title="Campaña" width={480}>
        {modal && (
          <div style={{ padding: 20, display: 'grid', gap: 12 }}>
            <Input theme={theme} label="Nombre" value={modal.name} onChange={e => setModal({ ...modal, name: e.target.value })} />
            <Input theme={theme} label="Descripción" value={modal.description} onChange={e => setModal({ ...modal, description: e.target.value })} />
            <label style={{ fontSize: 11, color: theme.textDim }}>Plantilla</label>
            <select value={modal.template_id || ''} onChange={e => setModal({ ...modal, template_id: e.target.value })} style={{ padding: 10, borderRadius: 8, border: `1px solid ${theme.border}`, background: theme.bgInput, color: theme.text }}>
              {(templates || []).map(t => <option key={t.id} value={t.id}>{t.name}</option>)}
            </select>
            <select value={modal.target_mode} onChange={e => setModal({ ...modal, target_mode: e.target.value })} style={{ padding: 10, borderRadius: 8, border: `1px solid ${theme.border}`, background: theme.bgInput, color: theme.text }}>
              <option value="adeudo">Alumnos con adeudo</option>
            </select>
            <Btn theme={theme} icon="check" onClick={save} disabled={busy}>{busy ? 'Guardando…' : 'Guardar'}</Btn>
          </div>
        )}
      </Modal>
    </div>
  );
};

const WaPlantillas = ({ theme }) => {
  const { data: tpls, loading, reload } = useWaReload(() => (typeof waFetchTemplates === 'function' ? waFetchTemplates() : Promise.resolve([])), []);
  const [edit, setEdit] = React.useState(null);
  const [busy, setBusy] = React.useState(false);

  const save = async () => {
    setBusy(true);
    try {
      await waSaveTemplate(edit);
      setEdit(null);
      reload?.();
    } catch (e) { alert(e.message); }
    setBusy(false);
  };

  const removeTemplate = async (p) => {
    const name = p?.name || 'esta plantilla';
    if (!window.confirm(`¿Eliminar la plantilla «${name}»?\n\nEsta acción no se puede deshacer.`)) return;
    setBusy(true);
    try {
      await waDeleteTemplate(p.id);
      if (edit?.id === p.id) setEdit(null);
      reload?.();
    } catch (e) { alert(e.message || 'No se pudo eliminar'); }
    setBusy(false);
  };

  const WA_TEMPLATE_VARS = '{nombre_tutor}, {nombre_alumno}, {codigo_alumno}, {mes}, {monto}, {fecha_limite}, {concepto}, {dias_restantes}, {dias_para_pagar}, {dias_vencidos}, {evento_nombre}, {fecha_evento}, {hora_evento}, {lugar_evento}, {orden_numero}, {notas}';

  return (
    <div className="tecos-page-shell" style={{ padding: 32 }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 18 }}>
        <h3 style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 28, color: theme.text, margin: 0 }}>PLANTILLAS</h3>
        <Btn theme={theme} icon="plus" onClick={() => setEdit({ name: '', category: 'General', body: '', active: true })}>Nueva plantilla</Btn>
      </div>
      {loading ? <LoadingBlock theme={theme} /> : (
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))', gap: 14 }}>
          {(tpls || []).map((p) => (
            <Card key={p.id} theme={theme}>
              <Badge theme={theme} color="primary">{p.category}</Badge>
              <h4 style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 20, margin: '8px 0' }}>{p.name}</h4>
              <p style={{ fontSize: 12, color: theme.textDim, lineHeight: 1.5 }}>{(p.body || '').slice(0, 120)}…</p>
              <div style={{ display: 'flex', gap: 6, marginTop: 12 }}>
                <Btn theme={theme} kind="soft" size="sm" icon="edit" onClick={() => setEdit({ ...p })}>Editar</Btn>
                <Btn theme={theme} kind="ghost" size="sm" icon="trash" onClick={() => removeTemplate(p)} disabled={busy}>Eliminar</Btn>
              </div>
            </Card>
          ))}
        </div>
      )}
      <Modal open={!!edit} onClose={() => setEdit(null)} theme={theme} title={edit?.id ? 'Editar plantilla' : 'Nueva plantilla'} width={520}>
        {edit && (
          <div style={{ padding: 20, display: 'grid', gap: 12 }}>
            <Input theme={theme} label="Nombre" value={edit.name} onChange={e => setEdit({ ...edit, name: e.target.value })} />
            <Input theme={theme} label="Categoría" value={edit.category} onChange={e => setEdit({ ...edit, category: e.target.value })} />
            <label style={{ fontSize: 11, color: theme.textDim, lineHeight: 1.5 }}>Texto — variables: {WA_TEMPLATE_VARS}</label>
            <textarea value={edit.body} onChange={e => setEdit({ ...edit, body: e.target.value })} rows={6} style={{
              width: '100%', padding: 12, borderRadius: 8, border: `1px solid ${theme.border}`,
              background: theme.bgInput, color: theme.text, fontFamily: 'Inter, sans-serif', fontSize: 13,
            }} />
            <Btn theme={theme} icon="check" onClick={save} disabled={busy}>{busy ? 'Guardando…' : 'Guardar'}</Btn>
          </div>
        )}
      </Modal>
    </div>
  );
};

const WaHistorial = ({ theme }) => {
  const [desdeMes, setDesdeMes] = React.useState(new Date().getMonth());
  const [desdeAnio, setDesdeAnio] = React.useState(new Date().getFullYear());
  const since = React.useMemo(() => new Date(desdeAnio, desdeMes, 1).toISOString(), [desdeMes, desdeAnio]);
  const { data: rows, loading, reload } = useWaReload(
    () => (typeof waFetchMessageLog === 'function' ? waFetchMessageLog({ since, limit: 300 }) : Promise.resolve([])),
    [since]
  );
  const [q, setQ] = React.useState('');
  const filtered = (rows || []).filter((r) => {
    if (!q.trim()) return true;
    const hay = [r.student_name, r.tutor_name, r.template_name, r.message_type].join(' ').toLowerCase();
    return hay.includes(q.toLowerCase());
  });

  const meses = ['Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'];
  const anios = [new Date().getFullYear(), new Date().getFullYear() - 1, new Date().getFullYear() - 2];

  return (
    <div className="tecos-page-shell" style={{ padding: 32 }}>
      <Card theme={theme} style={{ padding: 14, marginBottom: 14, display: 'flex', gap: 10, flexWrap: 'wrap', alignItems: 'center' }}>
        <Input theme={theme} placeholder="Buscar alumno o plantilla…" icon="search" value={q} onChange={e => setQ(e.target.value)} />
        <span style={{ fontSize: 11, color: theme.textDim, fontWeight: 700 }}>Mostrar desde</span>
        <select value={desdeMes} onChange={e => setDesdeMes(Number(e.target.value))} style={{ padding: '8px 10px', borderRadius: 8, border: `1px solid ${theme.border}`, background: theme.bgInput, color: theme.text }}>
          {meses.map((m, i) => <option key={m} value={i}>{m}</option>)}
        </select>
        <select value={desdeAnio} onChange={e => setDesdeAnio(Number(e.target.value))} style={{ padding: '8px 10px', borderRadius: 8, border: `1px solid ${theme.border}`, background: theme.bgInput, color: theme.text }}>
          {anios.map(y => <option key={y} value={y}>{y}</option>)}
        </select>
        <Btn theme={theme} kind="ghost" onClick={() => reload?.()}>Actualizar</Btn>
      </Card>
      <Card theme={theme} style={{ padding: 0, overflow: 'hidden' }}>
        {loading ? <LoadingBlock theme={theme} /> : (
          <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 13 }}>
            <thead>
              <tr style={{ background: theme.bgInput }}>
                {['Fecha', 'Alumno', 'Tipo', 'Plantilla', 'Estado', ''].map(h => <th key={h} style={{ padding: 12, fontSize: 10, textAlign: 'left', color: theme.textMute }}>{h}</th>)}
              </tr>
            </thead>
            <tbody>
              {filtered.map((r) => (
                <tr key={r.id} style={{ borderBottom: `1px solid ${theme.border}` }}>
                  <td style={{ padding: 12 }}>{new Date(r.created_at).toLocaleString('es-MX')}</td>
                  <td style={{ padding: 12 }}>{r.student_name}<div style={{ fontSize: 11, color: theme.textMute }}>{r.tutor_name}</div></td>
                  <td style={{ padding: 12 }}><Badge theme={theme} color="primary">{r.message_type}</Badge></td>
                  <td style={{ padding: 12 }}>{r.template_name || '—'}</td>
                  <td style={{ padding: 12 }}><StatusPill status={r.status} theme={theme} /></td>
                  <td style={{ padding: 12 }}>
                    <Btn theme={theme} kind="ghost" size="sm" icon="trash" onClick={() => {
                      if (window.confirm('¿Eliminar registro del historial?')) waDeleteMessageLog(r.id).then(() => reload?.());
                    }}>Eliminar</Btn>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        )}
        {!loading && !filtered.length && <p style={{ padding: 24, color: theme.textDim }}>Sin registros en este periodo.</p>}
      </Card>
    </div>
  );
};

const WA_AUTO_CATEGORY_COLORS = {
  Pago: 'warning',
  Evento: 'primary',
  Tienda: 'info',
  Cumple: 'success',
  General: 'muted',
};

const WaProgramar = ({ theme, onPreview }) => {
  const { data: templates } = useWaReload(() => (typeof waFetchTemplates === 'function' ? waFetchTemplates() : Promise.resolve([])), []);
  const { data: automations, reload: reloadAuto } = useWaReload(() => (typeof waFetchAutomations === 'function' ? waFetchAutomations() : Promise.resolve([])), []);
  const [tplId, setTplId] = React.useState('');
  const [fecha, setFecha] = React.useState('');
  const [hora, setHora] = React.useState('09:00');
  const [repeat, setRepeat] = React.useState('once');
  const [target, setTarget] = React.useState('adeudo');
  const [recipientCount, setRecipientCount] = React.useState(null);
  const [busy, setBusy] = React.useState(false);

  const targetModes = typeof WA_SCHEDULE_TARGET_MODES !== 'undefined'
    ? WA_SCHEDULE_TARGET_MODES
    : [{ id: 'adeudo', label: 'Alumnos con adeudo', description: '' }];

  const repeatLabels = typeof WA_REPEAT_LABELS !== 'undefined'
    ? WA_REPEAT_LABELS
    : { once: 'Una vez', daily: 'Diario', weekly: 'Semanal', monthly: 'Mensual' };

  React.useEffect(() => {
    if (!tplId && templates?.[0]) setTplId(templates[0].id);
  }, [templates, tplId]);

  React.useEffect(() => {
    let cancelled = false;
    (async () => {
      if (typeof waFetchScheduleRecipients !== 'function') {
        setRecipientCount(null);
        return;
      }
      const list = await waFetchScheduleRecipients(target);
      if (!cancelled) setRecipientCount(list.length);
    })();
    return () => { cancelled = true; };
  }, [target]);

  const programar = async () => {
    if (!fecha) { alert('Indica la fecha.'); return; }
    const tpl = (templates || []).find(t => t.id === tplId);
    if (!tpl) { alert('Elige plantilla.'); return; }
    let recipients = [];
    if (typeof waFetchScheduleRecipients === 'function') {
      recipients = await waFetchScheduleRecipients(target);
    } else if (target === 'adeudo' && typeof waFetchReminderRecipients === 'function') {
      recipients = await waFetchReminderRecipients();
    }
    if (!recipients.length) {
      alert('No hay destinatarios para el segmento elegido (revisa teléfonos de tutores).');
      return;
    }
    const scheduled_at = new Date(`${fecha}T${hora}:00`).toISOString();
    const preview = typeof waRenderTemplate === 'function'
      ? waRenderTemplate(tpl.body, {
        nombre_tutor: 'Tutor Ejemplo',
        nombre_alumno: 'Alumno Ejemplo',
        codigo_alumno: 'TEC001',
        mes: 'Mayo',
        monto: '1,200',
        fecha_limite: '05/05/2026',
        concepto: 'Mensualidad',
        dias_vencidos: '3',
        evento_nombre: 'Torneo regional',
        fecha_evento: '25/05/2026',
        hora_evento: '9:00 a.m.',
        lugar_evento: 'Gimnasio Tecos',
        orden_numero: 'ORD-001001',
        notas: '—',
        dias_restantes: '3',
        dias_para_pagar: '3',
      })
      : tpl.body;
    setBusy(true);
    try {
      await waSaveScheduled({
        template_id: tpl.id,
        template_name: tpl.name,
        message_body: preview,
        scheduled_at,
        repeat_rule: repeat,
        recipient_mode: target,
        recipient_json: recipients,
        status: 'programado',
      });
      alert(`Programado para ${recipients.length} destinatario(s). Revisa el envío en Pendientes.`);
      setFecha('');
    } catch (e) { alert(e.message); }
    setBusy(false);
  };

  const enabledAutoCount = (automations || []).filter((r) => r.enabled).length;

  return (
    <div className="tecos-page-shell tecos-grid-wa-split" style={{ padding: 32, display: 'grid', gridTemplateColumns: 'minmax(300px, 1fr) minmax(340px, 1.2fr)', gap: 18, alignItems: 'start' }}>
      <Card theme={theme}>
        <h3 style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 24, color: theme.text, margin: '0 0 8px' }}>PROGRAMAR MENSAJE</h3>
        <p style={{ fontSize: 12, color: theme.textDim, margin: '0 0 16px', lineHeight: 1.5 }}>
          Envío manual en fecha/hora. Elige plantilla y segmento (adeudo, al corriente, activos, todos).
        </p>
        <div style={{ display: 'grid', gap: 12 }}>
          <label style={{ fontSize: 11, color: theme.textDim, fontWeight: 600 }}>Plantilla</label>
          <select value={tplId} onChange={e => setTplId(e.target.value)} style={{ padding: 10, borderRadius: 8, border: `1px solid ${theme.border}`, background: theme.bgInput, color: theme.text }}>
            {(templates || []).map(t => <option key={t.id} value={t.id}>{t.category ? `[${t.category}] ` : ''}{t.name}</option>)}
          </select>
          <label style={{ fontSize: 11, color: theme.textDim, fontWeight: 600 }}>Fecha de envío</label>
          <input type="date" value={fecha} onChange={e => setFecha(e.target.value)} style={{
            padding: '10px 12px', borderRadius: 8, border: `1px solid ${theme.border}`,
            background: theme.bgInput, color: theme.text, fontFamily: 'Inter, sans-serif', width: '100%',
          }} />
          <Input theme={theme} label="Hora" value={hora} onChange={e => setHora(e.target.value)} />
          <label style={{ fontSize: 11, color: theme.textDim, fontWeight: 600 }}>Repetición</label>
          <select value={repeat} onChange={e => setRepeat(e.target.value)} style={{ padding: 10, borderRadius: 8, border: `1px solid ${theme.border}`, background: theme.bgInput, color: theme.text }}>
            {Object.entries(repeatLabels).map(([k, label]) => <option key={k} value={k}>{label}</option>)}
          </select>
          <label style={{ fontSize: 11, color: theme.textDim, fontWeight: 600 }}>Destinatarios</label>
          <select value={target} onChange={e => setTarget(e.target.value)} style={{ padding: 10, borderRadius: 8, border: `1px solid ${theme.border}`, background: theme.bgInput, color: theme.text }}>
            {targetModes.map((m) => <option key={m.id} value={m.id}>{m.label}</option>)}
          </select>
          {targetModes.find(m => m.id === target)?.description && (
            <p style={{ fontSize: 11, color: theme.textDim, margin: 0, lineHeight: 1.4 }}>
              {targetModes.find(m => m.id === target).description}
              {recipientCount != null && (
                <strong style={{ color: theme.text }}> · {recipientCount} destinatario(s)</strong>
              )}
            </p>
          )}
          <Btn theme={theme} icon="clock" onClick={programar} disabled={busy || recipientCount === 0}>
            {busy ? 'Guardando…' : 'Programar envío'}
          </Btn>
        </div>
      </Card>
      <Card theme={theme}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 12, marginBottom: 12, flexWrap: 'wrap' }}>
          <div>
            <h3 style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 24, color: theme.text, margin: 0 }}>AUTOMATIZACIONES</h3>
            <p style={{ fontSize: 12, color: theme.textDim, margin: '6px 0 0', lineHeight: 1.5 }}>
              Reglas del sistema: pagos, adeudos, eventos, tienda y cumpleaños.
              <strong> {enabledAutoCount}</strong> activa(s) de <strong>{(automations || []).length}</strong>.
            </p>
          </div>
          <Btn theme={theme} kind="ghost" size="sm" onClick={() => reloadAuto?.()}>Actualizar</Btn>
        </div>
        <p style={{ fontSize: 11, color: theme.textMute, marginBottom: 12, lineHeight: 1.45 }}>
          Activa cada regla según tu flujo. El bot debe estar conectado; los envíos programados aparecen en <strong>Pendientes</strong>.
          La plantilla sugerida se muestra como referencia (configúrala en Plantillas).
        </p>
        <div style={{ display: 'grid', gap: 8, maxHeight: 'min(70vh, 640px)', overflowY: 'auto', paddingRight: 4 }}>
          {(automations || []).map((r) => {
            const cat = r.config?.category || 'General';
            const badgeColor = WA_AUTO_CATEGORY_COLORS[cat] || 'muted';
            return (
              <div key={r.id || r.rule_key} style={{
                display: 'grid',
                gridTemplateColumns: '1fr auto',
                gap: 10,
                padding: 12,
                borderRadius: 10,
                background: r.enabled ? `${theme.primary}08` : theme.bgInput,
                border: `1px solid ${r.enabled ? `${theme.primary}33` : theme.border}`,
              }}>
                <div>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap', marginBottom: 4 }}>
                    <Badge theme={theme} color={badgeColor}>{cat}</Badge>
                    <span style={{ fontSize: 13, fontWeight: 600, color: theme.text }}>{r.title}</span>
                  </div>
                  {r.config?.description && (
                    <p style={{ fontSize: 11, color: theme.textDim, margin: '0 0 4px', lineHeight: 1.4 }}>{r.config.description}</p>
                  )}
                  {r.config?.suggested_template && (
                    <p style={{ fontSize: 10, color: theme.textMute, margin: 0 }}>
                      Plantilla sugerida: <span style={{ color: theme.primary }}>{r.config.suggested_template}</span>
                    </p>
                  )}
                </div>
                <WaToggle theme={theme} active={!!r.enabled} onChange={(v) => waSetAutomationEnabled(r.id || r.rule_key, v).then(() => reloadAuto?.())} />
              </div>
            );
          })}
          {!(automations || []).length && (
            <p style={{ fontSize: 13, color: theme.textDim, padding: 16 }}>Cargando automatizaciones…</p>
          )}
        </div>
      </Card>
    </div>
  );
};

const WaQuickActions = ({ theme, onPreview }) => (
  <Card theme={theme}>
    <h3 style={{ fontFamily: 'Bebas Neue, sans-serif', fontSize: 20, color: theme.text, letterSpacing: 1, margin: '0 0 12px', fontWeight: 400 }}>
      ACCIONES RÁPIDAS
    </h3>
    <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
      <Btn theme={theme} kind="wa" size="sm" icon="bell"
        onClick={() => onPreview?.({ name: 'Alumno demo', tutor: 'Tutor demo', tel: '', mes: 'Junio', monto: 1200, lim: '05 Jun' })}>
        Vista previa recordatorio
      </Btn>
      <Btn theme={theme} kind="soft" size="sm" icon="check"
        onClick={() => onPreview?.({
          name: 'Alumno demo', tutor: 'Tutor demo', tel: '', concepto: 'Mensualidad Junio',
          monto: 1200, template: 'comprobante',
        })}>
        Vista previa comprobante OK
      </Btn>
    </div>
  </Card>
);

function renderWhatsAppBotAdmin ({ theme, view, onPreview }) {
  const body = (() => {
    switch (view) {
      case 'wa-dashboard':
        return (
          <>
            <WaConnectionPanel theme={theme} />
            <WaDashboard theme={theme} onPreview={onPreview} />
            <div className="tecos-page-shell" style={{ padding: '0 32px 32px' }}><WaQuickActions theme={theme} onPreview={onPreview} /></div>
          </>
        );
      case 'wa-recordatorios':
        return <WaRecordatorios theme={theme} onPreview={onPreview} />;
      case 'wa-pendientes':
        return <WaPendientes theme={theme} onPreview={onPreview} />;
      case 'wa-urgentes':
        return <WaUrgentes theme={theme} onPreview={onPreview} />;
      case 'wa-campanas':
        return <WaCampanas theme={theme} onPreview={onPreview} />;
      case 'wa-plantillas':
        return <WaPlantillas theme={theme} />;
      case 'wa-historial':
        return <WaHistorial theme={theme} />;
      case 'wa-programar':
        return <WaProgramar theme={theme} onPreview={onPreview} />;
      default:
        return <WaConnectionPanel theme={theme} />;
    }
  })();

  return <div>{body}</div>;
}

function waBlobToDataUrl (blob) {
  return new Promise((resolve, reject) => {
    const r = new FileReader();
    r.onload = () => resolve(r.result);
    r.onerror = reject;
    r.readAsDataURL(blob);
  });
}

/** Comprueba que el bot esté listo antes de enviar archivos. */
async function waEnsureReadyToSend () {
  const s = await waServiceFetch('/status', { timeoutMs: 20000 });
  if (s.readyToSend) return s;
  const hint = s.error
    || (s.status === 'qr' ? 'Escanea el QR en Admin → WhatsApp.' : 'Pulsa «Sincronizar» en Admin → WhatsApp y espera «Listo para enviar».');
  throw new Error(hint);
}

/** Envía comprobante (imagen JPEG) de mensualidad por el bot (whatsapp-service /send-file). */
async function waSendPaymentReceiptPdfViaBot ({ phone, filename, caption, blob }) {
  if (!blob) throw new Error('No hay comprobante para enviar.');
  await waEnsureReadyToSend();

  const dataBase64 = await waBlobToDataUrl(blob);
  const digits = typeof normalizeWaPhoneDigits === 'function'
    ? normalizeWaPhoneDigits(phone)
    : String(phone || '').replace(/\D/g, '');
  if (!digits) throw new Error('Teléfono inválido. Usa prefijo internacional (+52, +1, etc.) en el expediente del alumno.');

  const sizeMb = blob.size / (1024 * 1024);
  if (sizeMb > 16) {
    throw new Error(`El archivo pesa ${sizeMb.toFixed(1)} MB (máx. ~16 MB por WhatsApp). Reduce el comprobante.`);
  }

  return waServiceFetch('/send-file', {
    method: 'POST',
    body: JSON.stringify({
      phone: digits,
      filename: filename || 'comprobante.jpg',
      caption: caption || 'Aquí está tu comprobante de pago — Tecos Elite VOLLEYBALL',
      dataBase64,
    }),
    timeoutMs: 180000,
  });
}

async function waSendFromRecipient (recipient, { urgent } = {}) {
  const rawPhone = recipient.tel || recipient.phone;
  if (!rawPhone || rawPhone === '—') throw new Error('El destinatario no tiene teléfono registrado.');
  const phone = typeof normalizeWaPhoneDigits === 'function'
    ? normalizeWaPhoneDigits(rawPhone)
    : String(rawPhone).replace(/\D/g, '');
  if (!phone) {
    throw new Error('Teléfono inválido. Elige el prefijo del país (+52, +1, +34, etc.) y el número con WhatsApp, sin repetir el prefijo.');
  }

  const sendOpts = { timeoutMs: 22000 };

  if (recipient.template === 'comprobante' || recipient.comprobanteApproved) {
    return waServiceFetch('/templates/comprobante-approved', {
      method: 'POST',
      body: JSON.stringify({
        phone,
        tutorName: recipient.tutor,
        studentName: recipient.name || recipient.alumno,
        concept: recipient.concepto || recipient.mes,
        amount: recipient.monto,
      }),
      ...sendOpts,
    });
  }

  const useBillingText = !!(recipient.billingKind || recipient.billingStatus);
  const isUrgentSend = !!(urgent || recipient.urgent);

  if (!useBillingText && (recipient.monto != null || recipient.mes || isUrgentSend)) {
    return waServiceFetch('/templates/payment-reminder', {
      method: 'POST',
      body: JSON.stringify({
        phone,
        tutorName: recipient.tutor,
        studentName: recipient.name || recipient.alumno,
        month: recipient.mes || recipient.concepto,
        amount: recipient.monto,
        dueDate: recipient.lim,
        urgent: isUrgentSend,
      }),
      ...sendOpts,
    });
  }

  const text = waBuildPreviewText(recipient, isUrgentSend);
  const res = await waServiceFetch('/send-text', {
    method: 'POST',
    body: JSON.stringify({ phone, text }),
    ...sendOpts,
  });
  if (typeof waLogMessage === 'function') {
    await waLogMessage({
      student_code: recipient.id,
      student_name: recipient.name || recipient.alumno,
      tutor_name: recipient.tutor,
      phone,
      message_type: recipient.tipo || (isUrgentSend ? 'Urgente' : 'Recordatorio'),
      body_preview: text,
      urgent: isUrgentSend,
    });
  }
  return res;
}

const WhatsAppBotModule = ({ theme, view, onPreview }) =>
  renderWhatsAppBotAdmin({ theme, view, onPreview });

Object.assign(window, {
  renderWhatsAppBotAdmin,
  WhatsAppBotModule,
  waServiceFetch,
  waBuildPreviewText,
  waPreviewPlainText,
  openWhatsAppForRecipient,
  waEnsureReadyToSend,
  waSendPaymentReceiptPdfViaBot,
  waSendFromRecipient,
  getWaServiceConfig,
  WaDashboard,
  WaRecordatorios,
  WaPendientes,
  WaUrgentes,
  WaCampanas,
  WaPlantillas,
  WaHistorial,
  WaProgramar,
});
