/* panel-app.jsx — Login (koi/123) + Shell + routing */
const { useState: aState, useEffect: aEffect } = React;

const NAV = [
["Principal", [
["dashboard", "Dashboard", "grid"],
["solicitar", "Pedir domiciliario", "plus"],
["analitica", "Analítica avanzada", "layers"],
["pedidos", "Pedidos", "pkg"],
["repartidores", "Mis Repartidores", "user"]]],

["Marketplace", [
["mk-productos", "Productos y menú", "chef"],
["mk-fotos", "Fotos del negocio", "camera"],
["mk-perfil", "Perfil público", "star"],
["mk-pagos", "Métodos de pago", "award"]]],

["Gestión", [
["historial", "Historial", "clock"],
["reclamaciones", "Mis PQRs", "bell"],
["plan", "Mi Plan", "award"],
["config", "Configuración", "cog"]]]];


const TITLES = {
  dashboard: ["Dashboard", "Resumen de hoy · 31 may 2026"],
  solicitar: ["Pedir domiciliario", "Solicita un repartidor para tu pedido"],
  analitica: ["Analítica avanzada", "Mapas de calor, series de tiempo y tendencias"],
  pedidos: ["Pedidos", "Domicilios directos y pedidos del marketplace, en un solo lugar"],
  repartidores: ["Mis Repartidores", "Tu equipo en la calle"],
  historial: ["Historial", "Entregas de los últimos días"],
  reclamaciones: ["Mis PQRs", "Quejas de pedido, peticiones, reclamos y felicitaciones"],
  plan: ["Mi Plan", "Suscripción, consumo y multas"],
  config: ["Configuración", "Ajustes de tu cuenta"],
  "mk-productos": ["Productos y menú", "Lo que tus clientes ven y piden en el marketplace"],
  "mk-fotos": ["Fotos del negocio", "Portada y logo de tu página pública"],
  "mk-perfil": ["Perfil público", "Descripción, horarios y visibilidad en el marketplace"],
  "mk-pagos": ["Métodos de pago", "Cómo te pagan los clientes del marketplace"]
};

/* mensajes según estado_cuenta del restaurante al intentar entrar */
const ESTADO_LOGIN_MSG = {
  pendiente: "Tu cuenta está en revisión. Te notificaremos por correo.",
  rechazado: "Tu solicitud no fue aprobada. Contáctanos para más información.",
  no_aprobado: "Tu cuenta aún no fue aprobada por el administrador. Te notificaremos por correo cuando esté lista.",
  terminos: "Debes aceptar los términos y condiciones para acceder. Acéptalos desde tu cuenta o contáctanos.",
  moroso: "Tu membresía tiene un saldo pendiente. Ponte al día con el pago para reactivar el acceso.",
};

/* mensaje al volver de reset-password.html (?pwreset=1) */
const PWRESET_MSG = "Contraseña actualizada. Ya puedes iniciar sesión.";
const pwresetEnUrl = () => new URLSearchParams(window.location.search).has("pwreset");

/* ---------------- hCaptcha (anti-bot en login y registro) ---------------- */
const HCAPTCHA_SITEKEY =
  (typeof TREEGO_CONFIG !== "undefined" && TREEGO_CONFIG.hcaptchaSiteKey) || "";

/* Renderiza el widget de hCaptcha (modo explícito). Llama onToken(token) al
   resolverlo y onToken("") si expira o falla. Expone una función de reseteo vía
   onReset(fn) para volver a pedir el captcha tras un intento fallido (el token
   es de un solo uso). Si no hay site key configurada, no renderiza nada. */
function HCaptchaBox({ onToken, onReset, theme }) {
  const cont = React.useRef(null);
  const wid = React.useRef(null);
  /* refs con los callbacks siempre frescos: así el widget NO se recrea en cada
     render del padre, solo cuando cambia el tema (efecto con dep [theme]). */
  const onTokenRef = React.useRef(onToken);
  const onResetRef = React.useRef(onReset);
  onTokenRef.current = onToken;
  onResetRef.current = onReset;

  React.useEffect(() => {
    if (!HCAPTCHA_SITEKEY) return;
    let stop = false;
    (function intentar() {
      if (stop) return;
      if (!window.hcaptcha || !window.hcaptcha.render || !cont.current) {
        setTimeout(intentar, 200);
        return;
      }
      if (wid.current != null) return;
      try {
        wid.current = window.hcaptcha.render(cont.current, {
          sitekey: HCAPTCHA_SITEKEY,
          theme: theme === "light" ? "light" : "dark",
          callback: (t) => onTokenRef.current(t),
          "expired-callback": () => onTokenRef.current(""),
          "error-callback": () => onTokenRef.current(""),
        });
        if (onResetRef.current) {
          onResetRef.current(() => {
            try { window.hcaptcha.reset(wid.current); } catch (e) {}
            onTokenRef.current("");
          });
        }
      } catch (e) { /* API aún no lista o ya renderizado */ }
    })();
    /* Al cambiar el tema, eliminamos el widget para volver a crearlo con el
       theme correcto (hCaptcha no permite cambiar el tema en caliente). El
       token anterior deja de ser válido, así que lo limpiamos. */
    return () => {
      stop = true;
      if (wid.current != null && window.hcaptcha) {
        try {
          if (window.hcaptcha.remove) window.hcaptcha.remove(wid.current);
          else window.hcaptcha.reset(wid.current);
        } catch (e) { /* widget ya retirado */ }
        wid.current = null;
        onTokenRef.current("");
      }
    };
  }, [theme]);
  if (!HCAPTCHA_SITEKEY) return null;
  return <div ref={cont} className="h-captcha-box" />;
}

/* ---------------- Modal de Términos y Condiciones ----------------
   Aparece cuando un restaurante con sesión válida aún no aceptó los términos.
   Reutilizable: login (bloqueo 'terminos') y cualquier otro punto que lo requiera. */
function TerminosGate({ onAceptar, onCancelar, theme }) {
  const [acepta, setAcepta] = aState(false);
  const [busy, setBusy] = aState(false);
  const [err, setErr] = aState('');

  const confirmar = async () => {
    if (!acepta || busy) return;
    setBusy(true); setErr('');
    const error = await onAceptar();
    if (error) {
      setErr('No pudimos registrar tu aceptación. Intenta de nuevo en un momento.');
      setBusy(false);
    }
    /* Si sale bien, el flujo de auth re-evalúa el acceso y desmonta este modal. */
  };

  return (
    <div style={{ position: 'fixed', inset: 0, zIndex: 300, display: 'grid', placeItems: 'center', padding: 20,
      background: 'color-mix(in oklab, #000 55%, transparent)', backdropFilter: 'blur(6px)' }}>
      <div style={{ width: 'min(560px,100%)', maxHeight: '90vh', display: 'flex', flexDirection: 'column',
        background: 'var(--panel)', border: '1px solid var(--line-2)', borderRadius: 18,
        boxShadow: '0 24px 60px -20px rgba(0,0,0,.5)', padding: '28px 28px 24px' }}>
        <h2 style={{ fontFamily: 'var(--font-display)', fontSize: 22, margin: '0 0 6px', color: 'var(--ink)' }}>Términos y Condiciones</h2>
        <p style={{ color: 'var(--ink-2)', fontSize: 14.5, margin: '0 0 16px' }}>
          Para acceder a tu panel, lee y acepta nuestros términos y la política de privacidad.
        </p>
        <div style={{ flex: 1, overflowY: 'auto', border: '1px solid var(--line-2)', borderRadius: 12,
          padding: '16px 18px', background: 'var(--bg)', fontSize: 13.5, lineHeight: 1.6, color: 'var(--ink-2)' }}>
          <p style={{ margin: '0 0 10px' }}>Al usar Treego como restaurante aliado aceptas, en resumen:</p>
          <ul style={{ margin: '0 0 10px', paddingLeft: 18 }}>
            <li style={{ marginBottom: 6 }}><b>Suscripción:</b> pagas por anticipado el plan elegido (Gourmet Express, Premium Partner o Elite Gastro) para mantener tu cuenta activa.</li>
            <li style={{ marginBottom: 6 }}><b>Carreras:</b> los planes cubren el software y el servicio; el valor de cada domicilio se cobra aparte según la tarifa vigente en Cajicá.</li>
            <li style={{ marginBottom: 6 }}><b>Puntualidad:</b> si superas el tiempo de tolerancia para entregar el pedido al repartidor, se genera una multa de $800 COP por minuto.</li>
            <li style={{ marginBottom: 6 }}><b>Garantía:</b> en los planes que la incluyen, el reembolso aplica solo cuando el daño es atribuible al repartidor de Treego.</li>
            <li style={{ marginBottom: 6 }}><b>Datos:</b> tratamos tus datos conforme a la Ley 1581 de 2012 (Habeas Data) de Colombia.</li>
          </ul>
          <p style={{ margin: 0 }}>
            Puedes leer el texto completo aquí:{' '}
            <a href="/pages/terminos.html" target="_blank" rel="noopener noreferrer" style={{ color: 'var(--g2)', fontWeight: 600 }}>Términos y Condiciones</a>
            {' '}y{' '}
            <a href="/pages/privacidad.html" target="_blank" rel="noopener noreferrer" style={{ color: 'var(--g2)', fontWeight: 600 }}>Política de Privacidad</a>.
          </p>
        </div>
        {err && <div className="login-err" style={{ marginTop: 14 }}><Icon name="flag" style={{ width: 15, height: 15 }} />{err}</div>}
        <label style={{ display: 'flex', alignItems: 'flex-start', gap: 10, margin: '16px 0 18px', fontSize: 14, color: 'var(--ink)', cursor: 'pointer' }}>
          <input type="checkbox" checked={acepta} onChange={e => setAcepta(e.target.checked)}
            style={{ marginTop: 2, width: 18, height: 18, accentColor: 'var(--g2)', flex: 'none', cursor: 'pointer' }} />
          <span>He leído y acepto los{' '}
            <a href="/pages/terminos.html" target="_blank" rel="noopener noreferrer" style={{ color: 'var(--g2)', fontWeight: 600 }}>Términos y Condiciones</a>
            {' '}y la{' '}
            <a href="/pages/privacidad.html" target="_blank" rel="noopener noreferrer" style={{ color: 'var(--g2)', fontWeight: 600 }}>Política de Privacidad</a>
            {' '}de Treego.</span>
        </label>
        <div style={{ display: 'flex', gap: 10 }}>
          <button type="button" className="btn btn-ghost" style={{ flex: 1 }} onClick={onCancelar} disabled={busy}>Cancelar</button>
          <button type="button" className="btn btn-primary" style={{ flex: 2 }} disabled={!acepta || busy} onClick={confirmar}>
            {busy ? 'Guardando…' : <React.Fragment>Aceptar y continuar <Icon name="arrow" /></React.Fragment>}
          </button>
        </div>
      </div>
    </div>
  );
}

/* ---------------- LOGIN ---------------- */
function Login({ onLogin, onGoRegister, onBack, theme, onToggleTheme, notice, info }) {
  const [email, setEmail] = aState("");
  const [pass,  setPass]  = aState("");
  const [show,  setShow]  = aState(false);
  const [err,   setErr]   = aState("");
  const [busy,  setBusy]  = aState(false);
  /* recuperación de contraseña: 'login' | 'forgot' | 'sent' */
  const [vista, setVista] = aState("login");
  const [fmail, setFmail] = aState("");
  const [ferr,  setFerr]  = aState("");
  const [fbusy, setFbusy] = aState(false);
  const [captcha, setCaptcha] = aState("");
  const captchaReset = React.useRef(null);

  const solicitarReset = async (e) => {
    e.preventDefault();
    if (fbusy) return;
    if (!/\S+@\S+\.\S+/.test(fmail)) { setFerr("El correo ingresado no es válido"); return; }
    if (!window.treego_db) { setFerr("Sin conexión. Verifica tu internet e intenta de nuevo."); return; }
    setFbusy(true);
    setFerr("");
    try {
      const { data, error } = await window.treego_db.functions.invoke("recuperar-password", {
        body: { action: "solicitar", email: fmail.trim().toLowerCase() },
      });
      if (!error && data && data.ok) setVista("sent");
      else if (data && data.code === "rate_limited") setFerr("Demasiados intentos. Espera unos minutos e inténtalo de nuevo.");
      else setFerr("No se pudo enviar el correo. Intenta de nuevo en un momento.");
    } catch (ex) {
      setFerr("No se pudo enviar el correo. Intenta de nuevo en un momento.");
    }
    setFbusy(false);
  };

  /* Vuelve a la landing: en modo unificado usa onBack (SPA, sin recarga);
     en modo standalone navega a la raíz. */
  const volverInicio = () => {
    if (typeof onBack === "function") onBack();
    else window.location.href = "/";
  };

  const submit = async (e) => {
    e.preventDefault();
    if (busy) return;
    /* Exige captcha solo si el runtime de hCaptcha llegó a cargar (evita
       bloquear el login si el script falla o lo bloquea una extensión). */
    if (HCAPTCHA_SITEKEY && window.hcaptcha && !captcha) { setErr("Completa el captcha para continuar."); return; }
    setBusy(true);
    setErr("");
    const error = await onLogin(email.trim().toLowerCase(), pass, captcha);
    if (error) {
      setErr("Correo o contraseña incorrectos. Revisa tus datos.");
      setBusy(false);
      setCaptcha("");
      if (captchaReset.current) captchaReset.current();
    }
  };

  return (
    <div className="login" style={{display:'grid',gridTemplateColumns:'1fr 1fr',minHeight:'100vh'}}>
      <button className="login-theme" onClick={onToggleTheme}
        title={theme === "light" ? "Modo oscuro" : "Modo claro"}>
        <Icon name={theme === "light" ? "moon" : "sun"} />
      </button>
      <div className="login-aside">
        <div className="lglow"></div>
        <div className="login-brand">
          <img loading="lazy" src={theme === "light" ? "/assets/streego-logo.png?v=2" : "/assets/treego-logo-dark.png?v=2"} alt="Treego" />
        </div>
        <div>
          <h2>El control total de tus domicilios, en un solo lugar.</h2>
          <p className="lead">Sigue cada pedido en vivo, conoce a tus repartidores y mantén
            la reputación de tu cocina intacta hasta el último kilómetro.</p>
          <div className="login-points">
            {[["route","Seguimiento en vivo por etapas"],
              ["shield","Repartidores capacitados y verificados"],
              ["chart","Métricas, consumo del plan y multas claras"]].map(([ic,t]) =>
              <div className="lp" key={t}><span className="ck"><Icon name={ic} /></span>{t}</div>
            )}
          </div>
        </div>
        <div className="lfoot">© 2026 Treego · Panel del restaurante</div>
      </div>
      <div className="login-main">
        {vista === "forgot" ? (
          <form className="login-card" onSubmit={solicitarReset}>
            <div className="eyebrow">Panel del restaurante</div>
            <h1>Recuperar contraseña</h1>
            <p className="sub">Ingresa tu correo y te enviaremos un enlace para crear una nueva.</p>
            {ferr && <div className="login-err"><Icon name="flag" style={{width:15,height:15}} />{ferr}</div>}
            <div className="field">
              <label>Correo electrónico</label>
              <div className="inp">
                <Icon name="user" />
                <input value={fmail} onChange={(e) => setFmail(e.target.value)}
                  placeholder="tucorreo@restaurante.co" type="email" autoFocus />
              </div>
            </div>
            <button className="btn btn-primary full" type="submit" disabled={fbusy}>
              {fbusy ? "Enviando…" : <React.Fragment>Enviar enlace <Icon name="arrow" /></React.Fragment>}
            </button>
            <div style={{ marginTop: 20, paddingTop: 20, borderTop: '1px solid var(--line-2)', textAlign: 'center', fontSize: 13.5, color: 'var(--ink-3)' }}>
              <button type="button"
                style={{ background: 'none', border: 'none', color: 'var(--g2)', fontWeight: 600, cursor: 'pointer', padding: 0, fontSize: 'inherit' }}
                onClick={() => { setVista("login"); setFerr(""); }}>
                ← Volver a iniciar sesión
              </button>
            </div>
          </form>
        ) : vista === "sent" ? (
          <div className="login-card">
            <div className="eyebrow">Panel del restaurante</div>
            <h1>Revisa tu correo</h1>
            <p className="sub">Te enviamos un correo con instrucciones. Revisa tu bandeja.</p>
            <p className="sub" style={{ marginTop: 0 }}>El enlace vence en 1 hora. Si no lo ves, mira en la carpeta de spam.</p>
            <button className="btn btn-primary full" type="button" onClick={() => { setVista("login"); setFerr(""); }}>
              Volver a iniciar sesión
            </button>
          </div>
        ) : (
        <form className="login-card" onSubmit={submit}>
          <button type="button" onClick={volverInicio}
            style={{ background: 'none', border: 'none', color: 'var(--ink-3)', fontWeight: 600, cursor: 'pointer', padding: 0, fontSize: 13.5, display: 'inline-flex', alignItems: 'center', gap: 6, marginBottom: 18 }}>
            <Icon name="arrow" style={{ width: 15, height: 15, transform: 'rotate(180deg)' }} /> Volver al inicio
          </button>
          <div className="eyebrow">Panel del restaurante</div>
          <h1>Bienvenido</h1>
          <p className="sub">Ingresa para ver tus pedidos de hoy.</p>
          {(err || notice) && <div className="login-err"><Icon name="flag" style={{width:15,height:15}} />{err || notice}</div>}
          {!err && !notice && info && (
            <div className="login-err" style={{ color: "var(--g2)", borderColor: "color-mix(in oklab, var(--g2) 35%, transparent)", background: "color-mix(in oklab, var(--g2) 10%, transparent)" }}>
              <Icon name="check" style={{width:15,height:15}} />{info}
            </div>
          )}
          <div className="field">
            <label>Correo electrónico</label>
            <div className="inp">
              <Icon name="user" />
              <input value={email} onChange={(e) => setEmail(e.target.value)}
                placeholder="koi@treego.co" type="email" autoFocus />
            </div>
          </div>
          <div className="field">
            <label>Contraseña</label>
            <div className="inp">
              <Icon name="shield" />
              <input type={show ? "text" : "password"} value={pass}
                onChange={(e) => setPass(e.target.value)} placeholder="••••" />
              <button type="button" className="eye" onClick={() => setShow(!show)}>
                <Icon name={show ? "eyeoff" : "eye"} />
              </button>
            </div>
          </div>
          <div className="login-row">
            <span className="muted" style={{color:"var(--ink-3)"}}>Mantener sesión</span>
            <a href="#" onClick={(e) => { e.preventDefault(); setVista("forgot"); setFmail(email); setFerr(""); }}>¿Olvidaste tu contraseña?</a>
          </div>
          <HCaptchaBox onToken={setCaptcha} onReset={(fn) => (captchaReset.current = fn)} theme={theme} />
          <button className="btn btn-primary full" type="submit" disabled={busy}>
            {busy ? "Ingresando…" : <React.Fragment>Iniciar sesión <Icon name="arrow" /></React.Fragment>}
          </button>
          {onGoRegister && (
            <div style={{ marginTop: 20, paddingTop: 20, borderTop: '1px solid var(--line-2)', textAlign: 'center', fontSize: 13.5, color: 'var(--ink-3)' }}>
              ¿No tienes cuenta?{' '}
              <button type="button"
                style={{ background: 'none', border: 'none', color: 'var(--g2)', fontWeight: 600, cursor: 'pointer', padding: 0, fontSize: 'inherit' }}
                onClick={onGoRegister}>
                Regístrate
              </button>
            </div>
          )}
        </form>
        )}
      </div>
    </div>
  );
}

/* Traduce CUALQUIER error de registro (Supabase Auth/DB, captcha o excepción
   de red) a un mensaje claro en español. Nunca devuelve un objeto ni texto en
   inglés: si no reconoce el error, cae a un mensaje genérico legible. */
function traducirErrorRegistro(e) {
  const crudo = (e && (e.message || e.error_description || e.msg || e.error)) ||
                (typeof e === "string" ? e : "");
  const m = String(crudo).toLowerCase();
  if (m.includes("captcha"))
    return "No pudimos validar el captcha. Vuelve a marcarlo e intenta de nuevo.";
  if (m.includes("already registered") || m.includes("already exists") || m.includes("user already"))
    return "Este correo ya tiene una cuenta. Inicia sesión en su lugar.";
  if (m.includes("weak password") || m.includes("password"))
    return "La contraseña debe tener al menos 8 caracteres.";
  if (m.includes("validate email") || m.includes("invalid email") || m.includes("email address"))
    return "El correo ingresado no es válido.";
  if (m.includes("rate limit") || m.includes("too many") || m.includes("email rate") || m.includes("over_"))
    return "Demasiados intentos. Espera un momento e intenta de nuevo.";
  if (m.includes("sending confirmation") || m.includes("confirmation email") || m.includes("error sending"))
    return "Tu cuenta se creó, pero no pudimos enviar el correo de verificación. Escríbenos a treegos.a.s@gmail.com.";
  if (m.includes("database error") || m.includes("saving new user"))
    return "Hubo un problema al crear tu cuenta. Intenta de nuevo en unos minutos.";
  return "No se pudo completar el registro. Revisa tus datos e intenta de nuevo.";
}

/* ---------------- REGISTER ---------------- */
function Register({ onBack, theme, onToggleTheme }) {
  const [step, setStep] = aState(1);
  const [nombre, setNombre] = aState('');
  const [apellido, setApellido] = aState('');
  const [email, setEmail] = aState('');
  const [password, setPassword] = aState('');
  const [confirmPassword, setConfirmPassword] = aState('');
  const [nombreComercial, setNombreComercial] = aState('');
  const [municipio, setMunicipio] = aState('');
  const [telefono, setTelefono] = aState('');
  const [err, setErr] = aState('');
  const [busy, setBusy] = aState(false);
  const [showPass, setShowPass] = aState(false);
  const [showConfirmPass, setShowConfirmPass] = aState(false);
  const [captcha, setCaptcha] = aState('');
  const captchaReset = React.useRef(null);
  const [aceptaTerminos, setAceptaTerminos] = aState(false);

  const MUNICIPIOS_CO = ["Cajicá","Chía","Sopó","Tabio","Zipaquirá"];

  function validarPaso1() {
    if (nombre.trim().length < 2) return 'El nombre debe tener al menos 2 caracteres';
    if (apellido.trim().length < 2) return 'El apellido debe tener al menos 2 caracteres';
    if (!/\S+@\S+\.\S+/.test(email)) return 'El correo ingresado no es válido';
    if (password.length < 8) return 'La contraseña debe tener al menos 8 caracteres';
    if (password !== confirmPassword) return 'Las contraseñas no coinciden';
    return null;
  }

  function siguientePaso(e) {
    e.preventDefault();
    const error = validarPaso1();
    if (error) { setErr(error); return; }
    setErr('');
    setStep(2);
  }

  async function crearCuenta(e) {
    e.preventDefault();
    if (nombreComercial.trim().length < 3) {
      setErr('El nombre del restaurante debe tener al menos 3 caracteres');
      return;
    }
    if (!municipio) {
      setErr('Selecciona tu municipio o ciudad');
      return;
    }
    if (!window.treego_db) {
      setErr('Error de conexión. Verifica tu internet e intenta de nuevo');
      return;
    }
    if (HCAPTCHA_SITEKEY && window.hcaptcha && !captcha) {
      setErr('Completa el captcha para continuar');
      return;
    }
    if (!aceptaTerminos) {
      setErr('Debes aceptar los Términos y Condiciones para registrarte');
      return;
    }
    setBusy(true);
    setErr('');

    let authData = null, authError = null;
    try {
      const resp = await window.treego_db.auth.signUp({
        email: email.trim().toLowerCase(),
        password,
        options: {
          captchaToken: captcha || undefined,
          emailRedirectTo: window.location.origin,
          data: {
            nombre:           nombre.trim(),
            apellido:         apellido.trim(),
            nombre_comercial: nombreComercial.trim(),
            municipio:        municipio,
            telefono:         telefono.trim() || null,
            terminos_aceptados: true,
          },
        },
      });
      authData = resp.data;
      authError = resp.error;
    } catch (ex) {
      authError = ex; // excepción de red u otra; la traducimos igual
    }

    if (authError) {
      /* Log crudo para diagnóstico (consola del navegador). Al usuario solo le
         mostramos un mensaje claro en español, nunca el objeto o el texto en inglés. */
      console.error('[Treego][registro] signUp falló:', authError);
      setErr(traducirErrorRegistro(authError));
      setBusy(false);
      setCaptcha('');
      if (captchaReset.current) captchaReset.current();
      return;
    }

    // Con confirmación de email obligatoria, Supabase devuelve session: null.
    // El perfil se creará automáticamente en el primer login (ver auth.jsx → loadUsuario).
    if (!authData.session) {
      setBusy(false);
      setStep('listo');
      return;
    }

    // Sin confirmación de email (entorno de desarrollo) — crear perfil de inmediato.
    const { data: usuarioData, error: usuarioError } = await window.treego_db
      .from('usuarios')
      .insert({
        nombre:        nombre.trim(),
        apellido:      apellido.trim() || null,
        email:         email.trim().toLowerCase(),
        rol:           'restaurante',
        activo:        true,
        password_hash: 'managed_by_supabase_auth',
      })
      .select('id')
      .single();

    if (usuarioError) {
      setErr('Hubo un problema al crear tu perfil. Contacta a soporte.');
      setBusy(false);
      return;
    }

    const { error: restError } = await window.treego_db
      .from('restaurantes')
      .insert({
        usuario_id: usuarioData.id,
        nombre_comercial: nombreComercial.trim(),
        municipio: municipio.trim(),
        telefono: telefono.trim() || null,
        email: email.trim().toLowerCase(),
        estado_cuenta: 'pendiente',
        terminos_aceptados: true,
      });

    if (restError) {
      setErr('Cuenta creada pero hubo un problema al guardar tu restaurante. Contacta a soporte.');
      setBusy(false);
      return;
    }

    setBusy(false);
    setStep('listo');
  }

  if (step === 'listo') {
    return (
      <div style={{
        minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center',
        flexDirection: 'column', gap: 20, background: 'var(--bg)', padding: 40, textAlign: 'center',
      }}>
        <div style={{
          width: 72, height: 72, borderRadius: '50%', background: 'var(--grad)',
          display: 'grid', placeItems: 'center', color: '#fff', marginBottom: 8,
        }}>
          <Icon name="check" style={{ width: 34, height: 34 }} />
        </div>
        <h2 style={{ fontFamily: 'var(--font-display)', fontSize: 28 }}>Tu solicitud fue recibida</h2>
        <p style={{ color: 'var(--ink-2)', maxWidth: '42ch', fontSize: 16 }}>
          Nuestro equipo la revisará y te contactaremos pronto. Mientras tanto, confirma el correo de verificación que enviamos a <b>{email}</b>.
        </p>
        <p style={{ color: 'var(--ink-3)', fontSize: 14, maxWidth: '38ch' }}>
          Podrás acceder al panel cuando tu cuenta sea aprobada. Si no ves el correo en unos minutos, revisa tu carpeta de spam.
        </p>
        <button type="button" className="btn btn-ghost" style={{ marginTop: 8 }} onClick={onBack}>
          Volver al inicio
        </button>
      </div>
    );
  }

  const indicadorPaso = (
    <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 8 }}>
      <div className="eyebrow">Crear cuenta</div>
      <div style={{ fontSize: 12, color: 'var(--ink-3)' }}>Paso {step} de 2</div>
    </div>
  );

  const aside = (
    <div className="login-aside">
      <div className="lglow"></div>
      <div className="login-brand">
        <img loading="lazy" src={theme === 'light' ? '/assets/streego-logo.png?v=2' : '/assets/treego-logo-dark.png?v=2'} alt="Treego" />
      </div>
      <div>
        {step === 1
          ? <React.Fragment>
              <h2>Empieza a gestionar tus domicilios hoy.</h2>
              <p className="lead">Crea tu cuenta en menos de 2 minutos y conecta tu restaurante al mejor equipo de repartidores.</p>
            </React.Fragment>
          : <React.Fragment>
              <h2>Un paso más y ya estás dentro.</h2>
              <p className="lead">Cuéntanos sobre tu restaurante para personalizar tu experiencia en Treego.</p>
            </React.Fragment>
        }
      </div>
      <div className="lfoot">© 2026 Treego · Panel del restaurante</div>
    </div>
  );

  /* Defensa extra: si err no fuera un string usable, mostramos un genérico
     legible en vez de renderizar un objeto sin formato. */
  const errTexto = typeof err === 'string' ? err : (err ? 'Ocurrió un error. Intenta de nuevo.' : '');

  return (
    <div className="login" style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', minHeight: '100vh' }}>
      <button className="login-theme" onClick={onToggleTheme}
        title={theme === 'light' ? 'Modo oscuro' : 'Modo claro'}>
        <Icon name={theme === 'light' ? 'moon' : 'sun'} />
      </button>
      {aside}
      <div className="login-main">
        {step === 1 && (
          <form className="login-card" onSubmit={siguientePaso}>
            {indicadorPaso}
            <h1>Datos de tu cuenta</h1>
            <p className="sub">Ingresa tus datos personales para continuar.</p>
            {errTexto && <div className="login-err"><Icon name="flag" style={{ width: 15, height: 15 }} />{errTexto}</div>}
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
              <div className="field">
                <label>Nombre</label>
                <div className="inp">
                  <Icon name="user" />
                  <input value={nombre} onChange={e => setNombre(e.target.value)}
                    placeholder="" type="text" autoFocus />
                </div>
              </div>
              <div className="field">
                <label>Apellido</label>
                <div className="inp">
                  <Icon name="user" />
                  <input value={apellido} onChange={e => setApellido(e.target.value)}
                    placeholder="" type="text" />
                </div>
              </div>
            </div>
            <div className="field">
              <label>Correo electrónico</label>
              <div className="inp">
                <Icon name="search" />
                <input value={email} onChange={e => setEmail(e.target.value)}
                  placeholder="" type="email" />
              </div>
            </div>
            <div className="field">
              <label>Contraseña</label>
              <div className="inp">
                <Icon name="shield" />
                <input type={showPass ? 'text' : 'password'} value={password}
                  onChange={e => setPassword(e.target.value)} placeholder="Mínimo 8 caracteres" />
                <button type="button" className="eye" onClick={() => setShowPass(s => !s)}>
                  <Icon name={showPass ? 'eyeoff' : 'eye'} />
                </button>
              </div>
            </div>
            <div className="field">
              <label>Confirmar contraseña</label>
              <div className="inp">
                <Icon name="shield" />
                <input type={showConfirmPass ? 'text' : 'password'} value={confirmPassword}
                  onChange={e => setConfirmPassword(e.target.value)} placeholder="Repite tu contraseña" />
                <button type="button" className="eye" onClick={() => setShowConfirmPass(s => !s)}>
                  <Icon name={showConfirmPass ? 'eyeoff' : 'eye'} />
                </button>
              </div>
            </div>
            <button className="btn btn-primary full" type="submit">
              Siguiente <Icon name="arrow" />
            </button>
            <div style={{ marginTop: 20, paddingTop: 20, borderTop: '1px solid var(--line-2)', textAlign: 'center', fontSize: 13.5, color: 'var(--ink-3)' }}>
              ¿Ya tienes cuenta?{' '}
              <button type="button"
                style={{ background: 'none', border: 'none', color: 'var(--g2)', fontWeight: 600, cursor: 'pointer', padding: 0, fontSize: 'inherit' }}
                onClick={onBack}>
                Inicia sesión
              </button>
            </div>
          </form>
        )}
        {step === 2 && (
          <form className="login-card" onSubmit={crearCuenta}>
            {indicadorPaso}
            <h1>Datos de tu restaurante</h1>
            <p className="sub">Esta información aparecerá en tu panel.</p>
            {errTexto && <div className="login-err"><Icon name="flag" style={{ width: 15, height: 15 }} />{errTexto}</div>}
            <div className="field">
              <label>Nombre del restaurante</label>
              <div className="inp">
                <Icon name="building" />
                <input value={nombreComercial} onChange={e => setNombreComercial(e.target.value)}
                  placeholder="Trattoria del Bosque" type="text" autoFocus />
              </div>
            </div>
            <div className="field">
              <label>Municipio / Ciudad</label>
              <div className="inp">
                <Icon name="pin" />
                <select value={municipio} onChange={e => setMunicipio(e.target.value)}
                  style={{ background: 'none', border: 'none', outline: 'none', flex: 1, color: 'inherit', fontSize: 'inherit', fontFamily: 'inherit', cursor: 'pointer' }}>
                  <option value="">Selecciona tu ciudad</option>
                  {MUNICIPIOS_CO.map(m => <option key={m} value={m}>{m}</option>)}
                </select>
              </div>
            </div>
            <div className="field">
              <label>Teléfono <span style={{ fontWeight: 400, color: 'var(--ink-3)' }}>(opcional)</span></label>
              <div className="inp">
                <Icon name="phone" />
                <input value={telefono} onChange={e => setTelefono(e.target.value)}
                  placeholder="300 000 0000" type="tel" />
              </div>
            </div>
            <HCaptchaBox onToken={setCaptcha} onReset={(fn) => (captchaReset.current = fn)} theme={theme} />
            <label style={{ display: 'flex', alignItems: 'flex-start', gap: 10, margin: '4px 0 14px', fontSize: 13.5, color: 'var(--ink-2)', cursor: 'pointer' }}>
              <input type="checkbox" checked={aceptaTerminos} onChange={e => setAceptaTerminos(e.target.checked)}
                style={{ marginTop: 2, width: 17, height: 17, accentColor: 'var(--g2)', flex: 'none', cursor: 'pointer' }} />
              <span>He leído y acepto los{' '}
                <a href="/pages/terminos.html" target="_blank" rel="noopener noreferrer" style={{ color: 'var(--g2)', fontWeight: 600 }}>Términos y Condiciones</a>
                {' '}y la{' '}
                <a href="/pages/privacidad.html" target="_blank" rel="noopener noreferrer" style={{ color: 'var(--g2)', fontWeight: 600 }}>Política de Privacidad</a>.</span>
            </label>
            <div style={{ display: 'flex', gap: 10, marginTop: 8 }}>
              <button type="button" className="btn btn-ghost" style={{ flex: 1 }}
                onClick={() => { setErr(''); setStep(1); }}>
                ← Atrás
              </button>
              <button className="btn btn-primary" style={{ flex: 2 }} type="submit" disabled={busy}>
                {busy
                  ? 'Creando cuenta…'
                  : <React.Fragment>Crear cuenta <Icon name="arrow" /></React.Fragment>}
              </button>
            </div>
          </form>
        )}
      </div>
    </div>
  );
}

/* ---------------- Onboarding: completar perfil del restaurante ----------------
   Se muestra antes del panel cuando faltan datos operativos (ubicación/dirección).
   Guarda vía RPC completar_perfil_restaurante (RLS no permite UPDATE directo). */
const ONB_CENTRO = { lat: 4.9183, lng: -74.0247 }; // Cajicá

function OnboardingWizard({ rest, theme, onToggleTheme, onLogout, onDone }) {
  const [paso, setPaso] = aState(1);
  const [coord, setCoord] = aState({
    lat: rest && rest.latitud  != null ? Number(rest.latitud)  : ONB_CENTRO.lat,
    lng: rest && rest.longitud != null ? Number(rest.longitud) : ONB_CENTRO.lng,
  });
  const [direccion, setDireccion] = aState((rest && rest.direccion) || '');
  const [telefono, setTelefono]   = aState((rest && rest.telefono) || '');
  const [nit, setNit]             = aState((rest && rest.nit) || '');
  const [razon, setRazon]         = aState((rest && rest.razon_social) || '');
  const [busy, setBusy] = aState(false);
  const [err, setErr]   = aState('');
  const mapRef  = React.useRef(null);
  const mapInit = React.useRef(false);
  const coordRef = React.useRef(coord); coordRef.current = coord;

  aEffect(() => {
    if (paso !== 1 || mapInit.current || !mapRef.current) return;
    if (typeof mapboxgl === 'undefined' || typeof TREEGO_CONFIG === 'undefined' || !TREEGO_CONFIG.mapboxToken) return;
    mapInit.current = true;
    mapboxgl.accessToken = TREEGO_CONFIG.mapboxToken;
    const map = new mapboxgl.Map({
      container: mapRef.current,
      style: TREEGO_CONFIG.mapboxStyleUrl || 'mapbox://styles/mapbox/streets-v12',
      center: [coordRef.current.lng, coordRef.current.lat], zoom: 14,
      attributionControl: false,
    });
    const marker = new mapboxgl.Marker({ draggable: true, color: window.TREEGO_BRAND.primary })
      .setLngLat([coordRef.current.lng, coordRef.current.lat]).addTo(map);
    const upd = (ll) => setCoord({ lat: +Number(ll.lat).toFixed(6), lng: +Number(ll.lng).toFixed(6) });
    marker.on('dragend', () => upd(marker.getLngLat()));
    map.on('click', (e) => { marker.setLngLat(e.lngLat); upd(e.lngLat); });
    setTimeout(() => map.resize(), 80);
    return () => { try { map.remove(); } catch (e) {} mapInit.current = false; };
  }, [paso]);

  const siguiente = () => {
    if (!direccion.trim()) { setErr('Ingresa la dirección de tu restaurante'); return; }
    setErr(''); setPaso(2);
  };

  const guardar = async () => {
    if (busy) return;
    if (!direccion.trim()) { setErr('Ingresa la dirección de tu restaurante'); setPaso(1); return; }
    setBusy(true); setErr('');
    const { error } = await window.treego_db.rpc('completar_perfil_restaurante', {
      p_direccion: direccion.trim(),
      p_latitud: coord.lat,
      p_longitud: coord.lng,
      p_telefono: telefono.trim() || null,
      p_nit: nit.trim() || null,
      p_razon_social: razon.trim() || null,
    });
    if (error) {
      console.error('[Treego][onboarding] guardar perfil falló:', error);
      setErr('No pudimos guardar tu información. Intenta de nuevo.');
      setBusy(false);
      return;
    }
    onDone();
  };

  return (
    <div style={{ minHeight: '100vh', background: 'var(--bg)', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 20, position: 'relative' }}>
      <button className="login-theme" onClick={onToggleTheme} title={theme === 'light' ? 'Modo oscuro' : 'Modo claro'}>
        <Icon name={theme === 'light' ? 'moon' : 'sun'} />
      </button>
      <div style={{ width: 'min(640px,100%)', background: 'var(--panel)', border: '1px solid var(--line-2)', borderRadius: 20, boxShadow: '0 24px 60px -22px rgba(0,0,0,.45)', padding: '30px 30px 26px' }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 6 }}>
          <div className="eyebrow">Configura tu restaurante</div>
          <div style={{ fontSize: 12, color: 'var(--ink-3)' }}>Paso {paso} de 2</div>
        </div>
        <h1 style={{ fontFamily: 'var(--font-display)', fontSize: 26, margin: '0 0 6px', color: 'var(--ink)' }}>
          {paso === 1 ? '¿Dónde está tu restaurante?' : 'Contacto y facturación'}
        </h1>
        <p className="sub" style={{ marginBottom: 18 }}>
          {paso === 1
            ? 'Ubica tu restaurante en el mapa: es el punto de partida del seguimiento en vivo.'
            : 'Estos datos aparecen en tu panel y en tus facturas mensuales.'}
        </p>
        {err && <div className="login-err"><Icon name="flag" style={{ width: 15, height: 15 }} />{err}</div>}

        {paso === 1 && (
          <React.Fragment>
            <div ref={mapRef} style={{ height: 260, borderRadius: 14, overflow: 'hidden', border: '1px solid var(--line-2)', marginBottom: 10, background: 'var(--bg-2, #eee)' }} />
            <p style={{ fontSize: 12.5, color: 'var(--ink-3)', margin: '0 0 14px' }}>
              Arrastra el marcador o toca el mapa para ubicar tu restaurante.{' '}
              <span style={{ color: 'var(--ink-2)' }}>({coord.lat}, {coord.lng})</span>
            </p>
            <div className="field">
              <label>Dirección exacta</label>
              <div className="inp">
                <Icon name="pin" />
                <input value={direccion} onChange={e => setDireccion(e.target.value)} placeholder="Cra. 3 # 4-21, sector / barrio" type="text" />
              </div>
            </div>
            <div style={{ display: 'flex', gap: 10, marginTop: 8 }}>
              <button type="button" className="btn btn-ghost" style={{ flex: 1 }} onClick={onLogout}>Salir</button>
              <button type="button" className="btn btn-primary" style={{ flex: 2 }} onClick={siguiente}>Siguiente <Icon name="arrow" /></button>
            </div>
          </React.Fragment>
        )}

        {paso === 2 && (
          <React.Fragment>
            <div className="field">
              <label>Teléfono de contacto</label>
              <div className="inp"><Icon name="phone" /><input value={telefono} onChange={e => setTelefono(e.target.value)} placeholder="300 000 0000" type="tel" /></div>
            </div>
            <div className="field">
              <label>NIT <span style={{ fontWeight: 400, color: 'var(--ink-3)' }}>(facturación)</span></label>
              <div className="inp"><Icon name="award" /><input value={nit} onChange={e => setNit(e.target.value)} placeholder="900.000.000-0" type="text" /></div>
            </div>
            <div className="field">
              <label>Razón social <span style={{ fontWeight: 400, color: 'var(--ink-3)' }}>(facturación)</span></label>
              <div className="inp"><Icon name="building" /><input value={razon} onChange={e => setRazon(e.target.value)} placeholder="Mi Restaurante S.A.S." type="text" /></div>
            </div>
            <div style={{ display: 'flex', gap: 10, marginTop: 8 }}>
              <button type="button" className="btn btn-ghost" style={{ flex: 1 }} onClick={() => { setErr(''); setPaso(1); }}>← Atrás</button>
              <button type="button" className="btn btn-primary" style={{ flex: 2 }} onClick={guardar} disabled={busy}>
                {busy ? 'Guardando…' : <React.Fragment>Guardar y continuar <Icon name="arrow" /></React.Fragment>}
              </button>
            </div>
          </React.Fragment>
        )}
      </div>
    </div>
  );
}

/* ---------------- Intro de bienvenida (una sola vez) ---------------- */
const INTRO_CARDS = [
  ['sparkle', 'Bienvenido a Treego',          'Tu panel de domicilios premium. Aquí controlas pedidos, repartidores y métricas en un solo lugar.'],
  ['pin',     'Configura tu ubicación',       'Ve a «Configuración» y marca dónde está tu restaurante para que los repartidores lleguen sin rodeos.'],
  ['plus',    'Pide tu primer domicilio',     'Usa el botón «+ Pedir domiciliario» y asignamos al repartidor capacitado más cercano.'],
  ['route',   'Sigue tu pedido en vivo',      'Verás el repartidor moverse en el mapa, etapa por etapa, hasta la entrega confirmada.'],
  ['chart',   'Revisa tus métricas',          'El dashboard te muestra todo lo de hoy: pedidos, entregas, tiempos y el consumo de tu plan.'],
];
function WelcomeIntro({ onDone, theme, onToggleTheme }) {
  const [i, setI] = aState(0);
  const last = i === INTRO_CARDS.length - 1;
  const [ic, t, d] = INTRO_CARDS[i];
  return (
    <div style={{ minHeight: '100vh', background: 'var(--bg)', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 20, position: 'relative' }}>
      <button className="login-theme" onClick={onToggleTheme} title={theme === 'light' ? 'Modo oscuro' : 'Modo claro'}>
        <Icon name={theme === 'light' ? 'moon' : 'sun'} />
      </button>
      <div style={{ width: 'min(460px,100%)', background: 'var(--panel)', border: '1px solid var(--line-2)', borderRadius: 20, boxShadow: '0 24px 60px -22px rgba(0,0,0,.45)', padding: '36px 32px 28px', textAlign: 'center' }}>
        <div style={{ width: 64, height: 64, margin: '0 auto 20px', borderRadius: 18, background: 'var(--grad)', display: 'grid', placeItems: 'center', color: '#fff' }}>
          <Icon name={ic} style={{ width: 30, height: 30 }} />
        </div>
        <h1 style={{ fontFamily: 'var(--font-display)', fontSize: 24, margin: '0 0 10px', color: 'var(--ink)' }}>{t}</h1>
        <p style={{ color: 'var(--ink-2)', fontSize: 15.5, lineHeight: 1.6, margin: '0 0 22px' }}>{d}</p>
        <div style={{ display: 'flex', justifyContent: 'center', gap: 7, marginBottom: 24 }}>
          {INTRO_CARDS.map((_, k) => (
            <span key={k} style={{ width: k === i ? 22 : 7, height: 7, borderRadius: 999, background: k === i ? 'var(--g2)' : 'var(--line-2)', transition: '.25s' }} />
          ))}
        </div>
        <div style={{ display: 'flex', gap: 10 }}>
          {!last && <button type="button" className="btn btn-ghost" style={{ flex: 1 }} onClick={onDone}>Saltar</button>}
          <button type="button" className="btn btn-primary" style={{ flex: 2 }} onClick={() => last ? onDone() : setI(i + 1)}>
            {last ? <React.Fragment>Entrar al panel <Icon name="arrow" /></React.Fragment> : <React.Fragment>Siguiente <Icon name="arrow" /></React.Fragment>}
          </button>
        </div>
      </div>
    </div>
  );
}

/* ---------------- Cuenta sin plan: el admin debe activarlo ---------------- */
function PlanPendiente({ onLogout, theme, onToggleTheme }) {
  return (
    <div style={{ minHeight: '100vh', background: 'var(--bg)', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 20, position: 'relative' }}>
      <button className="login-theme" onClick={onToggleTheme} title={theme === 'light' ? 'Modo oscuro' : 'Modo claro'}>
        <Icon name={theme === 'light' ? 'moon' : 'sun'} />
      </button>
      <div style={{ width: 'min(460px,100%)', background: 'var(--panel)', border: '1px solid var(--line-2)', borderRadius: 20, boxShadow: '0 24px 60px -22px rgba(0,0,0,.45)', padding: '36px 32px 28px', textAlign: 'center' }}>
        <div style={{ width: 64, height: 64, margin: '0 auto 18px', borderRadius: '50%', background: 'var(--grad)', display: 'grid', placeItems: 'center', color: '#fff' }}>
          <Icon name="clock" style={{ width: 30, height: 30 }} />
        </div>
        <h1 style={{ fontFamily: 'var(--font-display)', fontSize: 24, margin: '0 0 10px', color: 'var(--ink)' }}>¡Casi listo!</h1>
        <p style={{ color: 'var(--ink-2)', fontSize: 15.5, lineHeight: 1.6, margin: '0 0 24px' }}>
          Tu información quedó guardada. Falta que el administrador <b>active tu plan</b> para que puedas operar. Te avisaremos por correo apenas esté listo.
        </p>
        <button type="button" className="btn btn-ghost full" onClick={onLogout}>Cerrar sesión</button>
      </div>
    </div>
  );
}

/* ---------------- SHELL ---------------- */
function Shell({ usuario, onLogout, theme, onToggleTheme }) {
  const [view, setView] = aState(() => {
    const v = localStorage.getItem("treego_view") || "dashboard";
    /* la sección "Pedidos del marketplace" se fusionó con "Pedidos" */
    return v === "mk-pedidos" ? "pedidos" : v;
  });
  const [orders,         setOrders]         = aState([]);
  const [deliveredToday, setDeliveredToday] = aState([]);
  const [drivers,        setDrivers]        = aState([]);
  const [historial,      setHistorial]      = aState([]);
  const [fines,          setFines]          = aState([]);
  const [planData,       setPlanData]       = aState(null);
  const [restRow,    setRestRow]    = aState(null);
  const [needsSetup, setNeedsSetup] = aState(false);
  const [planFalta,  setPlanFalta]  = aState(false);
  const [introVisto, setIntroVisto] = aState(false); /* se define desde la BD (restaurantes.onboarding_completado) al cargar */
  const [sel,        setSel]        = aState(null);
  const [driver,     setDriver]     = aState(null);
  const [orderDetail, setOrderDetail] = aState(null);
  const [fineOpen,   setFineOpen]   = aState(true);
  const [mkNuevos,   setMkNuevos]   = aState(0); /* pedidos del marketplace sin ver */

  aEffect(() => { localStorage.setItem("treego_view", view); }, [view]);

  /* Alerta global de pedidos del marketplace: canal Realtime + sonido
     (panel-marketplace.jsx). El badge del menú se limpia al entrar a la vista. */
  aEffect(() => {
    if (!planData || !planData.restId || !window.treegoMkAlertaInit) return;
    window.treegoMkAlertaInit(planData.restId);
    const onNuevo = () => setMkNuevos((n) => n + 1);
    window.addEventListener("treego-mk-nuevo", onNuevo);
    return () => window.removeEventListener("treego-mk-nuevo", onNuevo);
  }, [planData]);
  aEffect(() => { if (view === "pedidos") setMkNuevos(0); }, [view]);

  aEffect(() => {
    if (!window.treego_db || !usuario) return;
    loadRestaurantData();
  }, [usuario]);

  async function loadRestaurantData() {
    const { data: rest, error: restErr } = await window.treego_db
      .from('restaurantes')
      .select('*, planes(*)')
      .eq('usuario_id', usuario.id)
      .single();

    if (restErr || !rest) {
      console.error('[Treego] No se encontró restaurante:', restErr?.message);
      setPlanData({ restId: null });
      return;
    }

    setRestRow(rest);

    /* Onboarding incompleto: sin ubicación o sin dirección → wizard (no panel). */
    if (rest.latitud == null || rest.longitud == null || !rest.direccion) {
      setNeedsSetup(true);
      setPlanData({ restId: rest.id });   // detiene el spinner
      return;
    }
    setNeedsSetup(false);

    /* El tutorial de bienvenida se muestra solo una vez por cuenta (BD, no navegador). */
    setIntroVisto(rest.onboarding_completado === true);

    /* Plan aún no asignado por el admin → pantalla "plan pendiente" (sin crash). */
    if (!rest.planes) {
      setPlanFalta(true);
      setPlanData({ restId: rest.id });
      return;
    }
    setPlanFalta(false);

    const periodoInicio = rest.fecha_inicio_plan || new Date().toISOString().slice(0, 10);
    const { count: usados } = await window.treego_db
      .from('pedidos')
      .select('id', { count: 'exact', head: true })
      .eq('restaurante_id', rest.id)
      .gte('fecha_creacion', periodoInicio);

    const plan = rest.planes;
    const renewDate = new Date(periodoInicio);
    renewDate.setMonth(renewDate.getMonth() + 1);
    const renewStr = renewDate.toLocaleDateString('es-CO', { day: 'numeric', month: 'short', year: 'numeric' });

    S.user = usuario.email;
    S.rest = {
      name: rest.nombre_comercial,
      zone: rest.municipio,
      /* coordenadas del restaurante = origen del tracking en vivo */
      lat:  rest.latitud  != null ? Number(rest.latitud)  : null,
      lng:  rest.longitud != null ? Number(rest.longitud) : null,
    };
    S.plan = {
      key:       plan.nombre.toLowerCase().replace(/ /g, '_'),
      name:      plan.nombre,
      price:     plan.precio_mensual,
      used:      usados || 0,
      renew:     renewStr,
      tolerance: plan.minutos_tolerancia,
      perMinute: 800,
      advanced:  plan.analytics_avanzada,
      feats:     buildFeats(plan),
    };

    await Promise.all([
      loadPedidos(rest.id),
      loadDrivers(rest.id),
      loadHistorial(rest.id),
      loadFines(rest.id),
    ]);

    setPlanData({ restId: rest.id, planName: plan.nombre });
  }

  function buildFeats(plan) {
    const base = [
      `Tolerancia de espera de ${plan.minutos_tolerancia} minutos`,
      'Domicilios ilimitados',
    ];
    if (plan.analytics_avanzada)       base.push('Dashboard avanzado: mapas de calor y analítica de tiempos');
    if (plan.perfil_repartidor_visible) base.push('Perfil del repartidor asignado visible');
    if (plan.prioridad_asignacion && plan.perfil_repartidor_visible) base.push('Soporte 24/7 con gestor de cuenta dedicado');
    return base;
  }

  const ESTADO_STAGE = {
    creado: 0, asignado: 0, en_restaurante: 1, recogido: 2, en_camino: 3, entregado: 4, cancelado: 5,
  };

  function dbPedidoToCard(p) {
    const dir = p.direcciones;
    /* pedidos de prueba: las 3 coordenadas del viaje vienen en notas (JSON) */
    let simTrip = null;
    try { const n = JSON.parse(p.notas || 'null'); if (n && n.sim) simTrip = n.sim; } catch (e) { /* notas libres */ }
    const zone = dir ? `${dir.municipio} · ${dir.barrio}` : (p.tipo_zona || '—');
    const driverName = p.repartidores ? p.repartidores.nombre_completo : 'Sin asignar';
    const stage = ESTADO_STAGE[p.estado_actual] ?? 0;
    const fecha = new Date(p.fecha_creacion);
    const time = fecha.toLocaleTimeString('es-CO', { hour: '2-digit', minute: '2-digit', hour12: true });
    const minsElapsed = Math.round((Date.now() - fecha.getTime()) / 60000);
    return {
      id:     p.codigo_restaurante || String(p.id),
      dbId:   p.id,
      /* origen del pedido: marketplace (cliente en línea) o directo (panel) */
      esMarketplace: p.origen === "marketplace",
      estado: p.estado_actual,
      telefono: p.telefono_cliente || null,
      client: p.nombre_cliente,
      zone,
      driver: driverName,
      stage,
      eta:    stage < 4 ? Math.max(2, Math.min(30, 30 - minsElapsed)) : 0,
      total:  p.valor_pedido ?? 0,
      items:  1,
      time,
      /* coordenadas reales del viaje (tracking en vivo); los pedidos de
         prueba traen las suyas en notas (JSON sim) */
      origen:  (simTrip && simTrip.rest) || (S.rest && S.rest.lat != null ? { lat: S.rest.lat, lng: S.rest.lng } : null),
      destino: (simTrip && simTrip.cli) || (dir && dir.latitud != null ? { lat: Number(dir.latitud), lng: Number(dir.longitud) } : null),
    };
  }

  async function loadPedidos(restId) {
    const hoy = new Date(); hoy.setHours(0, 0, 0, 0);

    const [{ data: activos }, { data: entregados }] = await Promise.all([
      window.treego_db
        .from('pedidos')
        .select('*, repartidores(nombre_completo), direcciones(municipio, barrio, latitud, longitud)')
        .eq('restaurante_id', restId)
        .not('estado_actual', 'in', '("entregado","cancelado")')
        .order('fecha_creacion', { ascending: false }),
      window.treego_db
        .from('pedidos')
        .select('*, repartidores(nombre_completo), direcciones(municipio, barrio, latitud, longitud)')
        .eq('restaurante_id', restId)
        .eq('estado_actual', 'entregado')
        .gte('fecha_creacion', hoy.toISOString())
        .order('fecha_cierre', { ascending: false }),
    ]);

    const activeCards = (activos || []).map(dbPedidoToCard);
    const doneCards = (entregados || []).map(p => ({
      ...dbPedidoToCard(p),
      arrival: p.fecha_cierre
        ? Math.round((new Date(p.fecha_cierre) - new Date(p.fecha_creacion)) / 60000)
        : 10,
      rating: 5,
    }));

    setOrders(activeCards);
    setDeliveredToday(doneCards);
    if (activeCards.length > 0 && !sel) setSel(activeCards[0].id);
    Object.assign(window, { DELIVERED_TODAY: doneCards });

    /* Realtime subscription for order updates — UNA sola vez por sesión.
       (loadPedidos se re-ejecuta con cada cambio; recrear el canal aquí
       lanzaba "cannot add callbacks after subscribe()" y rompía el socket)
       Debounce 600 ms (Bloque 7): una ráfaga de eventos (asignación + evento +
       tracking) dispara UNA sola recarga en lugar de una por evento. */
    if (!window.__treegoPedsChannel) {
      const recargar = () => {
        clearTimeout(window.__treegoPedsReload);
        window.__treegoPedsReload = setTimeout(() => loadPedidos(restId), 600);
      };
      window.__treegoPedsChannel = window.treego_db
        .channel(`pedidos-rest-${restId}`)
        .on('postgres_changes', {
          event: '*', schema: 'public', table: 'pedidos',
          filter: `restaurante_id=eq.${restId}`,
        }, recargar)
        .subscribe();
    }
    return window.__treegoPedsChannel;
  }

  async function loadDrivers(restId) {
    /* "Mis repartidores" = solo los que han trabajado con ESTE restaurante.
       Un restaurante nuevo (sin pedidos) no tiene repartidores asociados. */
    const { data: peds } = await window.treego_db
      .from('pedidos')
      .select('repartidor_id')
      .eq('restaurante_id', restId)
      .not('repartidor_id', 'is', null);
    const countById = {};
    (peds || []).forEach(p => { countById[p.repartidor_id] = (countById[p.repartidor_id] || 0) + 1; });
    const ids = Object.keys(countById).map(Number);
    if (!ids.length) { setDrivers([]); Object.assign(window, { DRIVERS: [] }); return; }

    const [{ data }, punts] = await Promise.all([
      window.treego_db
        .from('repartidores')
        .select('*')
        .in('id', ids)
        .order('calificacion_promedio', { ascending: false }),
      window.treego_db.rpc('puntualidad_repartidores').then(({ data: p }) => p || [], () => []),
    ]);

    if (!data) { setDrivers([]); Object.assign(window, { DRIVERS: [] }); return; }
    /* puntualidad REAL (F2 punto 12): "—" con menos de 5 mediciones */
    const punctMap = {};
    punts.forEach(p => { if (Number(p.n) >= 5) punctMap[p.repartidor_id] = Number(p.puntual_pct); });
    /* registro global nombre → foto: lo usa <Avatar> en todo el panel */
    window.TREEGO_FOTOS = window.TREEGO_FOTOS || {};
    data.forEach(d => { if (d.foto_url) window.TREEGO_FOTOS[d.nombre_completo] = d.foto_url; });
    const mapped = data.map(d => ({
      name:       d.nombre_completo,
      foto:       d.foto_url || null,
      years:      `${Math.max(1, new Date().getFullYear() - new Date(d.fecha_ingreso).getFullYear())} año(s)`,
      rating:     d.calificacion_promedio ? d.calificacion_promedio.toFixed(2) : '5.00',
      deliveries: countById[d.id] || 0,
      punct:      punctMap[d.id] != null ? punctMap[d.id].toFixed(1).replace('.', ',') + '%' : '—',
      hyg:        '100%',
      phone:      d.telefono,
      trainings:  ['Manejo de alimentos', 'Atención al cliente'],
      badge:      'Verificado',
      recent:     [],
    }));

    setDrivers(mapped);
    Object.assign(window, { DRIVERS: mapped });
  }

  async function loadHistorial(restId) {
    const { data } = await window.treego_db
      .from('pedidos')
      .select('*, repartidores(nombre_completo), direcciones(municipio, barrio)')
      .eq('restaurante_id', restId)
      .eq('estado_actual', 'entregado')
      .order('fecha_cierre', { ascending: false })
      .limit(30);

    if (!data) return;
    const rows = data.map(p => {
      const arrival = p.fecha_cierre
        ? Math.round((new Date(p.fecha_cierre) - new Date(p.fecha_creacion)) / 60000)
        : 10;
      const fStr = new Date(p.fecha_cierre || p.fecha_creacion)
        .toLocaleDateString('es-CO', { day: 'numeric', month: 'short' });
      const row = [
        p.codigo_restaurante || String(p.id),
        fStr,
        p.nombre_cliente,
        p.repartidores ? p.repartidores.nombre_completo : '—',
        `${arrival} min`,
        'Entregado',
        p.valor_pedido,
      ];
      row.pedido_id = p.id;
      row.restaurante_id = p.restaurante_id;
      row.repartidor_id = p.repartidor_id;
      return row;
    });

    setHistorial(rows);
    Object.assign(window, { HISTORY: rows });
  }

  async function loadFines(restId) {
    const { data } = await window.treego_db
      .from('multas')
      .select('*, pedidos(codigo_restaurante, repartidores(nombre_completo))')
      .eq('restaurante_id', restId)
      .eq('estado', 'pendiente')
      .order('fecha_generacion', { ascending: false });

    if (!data) return;
    const mapped = data.map(f => ({
      id:      f.pedidos ? (f.pedidos.codigo_restaurante || String(f.pedido_id)) : String(f.pedido_id),
      date:    new Date(f.fecha_generacion).toLocaleDateString('es-CO', { day: 'numeric', month: 'short' }),
      driver:  f.pedidos && f.pedidos.repartidores ? f.pedidos.repartidores.nombre_completo : '—',
      waited:  f.minutos_exceso + (S.plan ? S.plan.tolerance : 5),
      fee:     f.valor_multa,
      minutes: f.minutos_exceso,
    }));

    setFines(mapped);
    Object.assign(window, { FINES: mapped });
  }

  /* Loading screen while restaurant data is being fetched */
  if (!planData && window.treego_db) {
    return (
      <div style={{
        minHeight: '100vh', display: 'flex', alignItems: 'center',
        justifyContent: 'center', background: 'var(--bg)',
      }}>
        <img loading="lazy" src="/assets/streego-icon.png?v=2"
          style={{ width: 40, opacity: 0.5, animation: 'fadeIn .6s ease' }} alt="" />
      </div>
    );
  }

  /* Antes del panel: 1) onboarding si faltan datos, 2) plan pendiente del admin,
     3) intro de bienvenida (una sola vez). */
  if (needsSetup) {
    return <OnboardingWizard rest={restRow} theme={theme} onToggleTheme={onToggleTheme} onLogout={onLogout}
      onDone={() => { setNeedsSetup(false); setPlanData(null); loadRestaurantData(); }} />;
  }
  if (planFalta) {
    return <PlanPendiente theme={theme} onToggleTheme={onToggleTheme} onLogout={onLogout} />;
  }
  if (!introVisto) {
    return <WelcomeIntro theme={theme} onToggleTheme={onToggleTheme}
      onDone={() => {
        setIntroVisto(true); /* nunca vuelve a aparecer, complete o no (Saltar / X también) */
        try { if (window.treego_db) window.treego_db.rpc('marcar_onboarding_completado'); } catch (e) {}
      }} />;
  }

  const titleEntry = TITLES[view] || ["—", ""];
  const [t1, t2]   = titleEntry;
  const activeCount = orders.filter(o => o.stage < 4).length;

  return (
    <div className="shell">
      <aside className="sidebar">
        <div className="sb-head">
          <img loading="lazy" className="sb-logo-full"
            src={theme === "light" ? "/assets/streego-logo.png?v=2" : "/assets/treego-logo-dark.png?v=2"}
            alt="Treego" />
          <img loading="lazy" className="sb-logo-mini" src="/assets/streego-icon.png?v=2" alt="Treego" />
        </div>
        {NAV.map(([sec, items]) =>
          <React.Fragment key={sec}>
            <div className="sb-section">{sec}</div>
            <nav className="sb-nav">
              {items.map(([id, label, ic]) =>
                <button key={id}
                  className={"sb-link" + (view === id ? " on" : "")}
                  onClick={() => setView(id)}>
                  <Icon name={ic} /><span>{label}</span>
                  {id === "pedidos"   && (activeCount + mkNuevos) > 0 && <span className="badge">{activeCount + mkNuevos}</span>}
                  {id === "plan"      && fines.length > 0         && <span className="badge">{fines.length}</span>}
                  {id === "analitica" && S.plan && !S.plan.advanced &&
                    <span className="badge badge-lock"><Icon name="lock" style={{ width: 12, height: 12 }} /></span>}
                </button>
              )}
            </nav>
          </React.Fragment>
        )}
        <div className="sb-foot">
          <div className="sb-rest">
            <span className="av"><Avatar name={S.rest ? S.rest.name : '?'} shape="tile" /></span>
            <div>
              <div className="nm">{S.rest ? S.rest.name : '...'}</div>
              <div className="pl">{S.plan ? S.plan.name  : '...'}</div>
            </div>
            <button className="lo" title="Cerrar sesión" onClick={onLogout}>
              <Icon name="logout" style={{ width: 17, height: 17 }} />
            </button>
          </div>
        </div>
      </aside>

      <div className="main">
        <header className="topbar">
          <div className="tt"><h1>{t1}</h1><p>{t2}</p></div>
          <div className="spacer"></div>
          {/* Buscador funcional (F2 punto 15): pedidos, historial y repartidores */}
          {window.BuscadorGlobal ? (
            <window.BuscadorGlobal
              placeholder="Buscar pedido, cliente…"
              buscar={(q) => {
                const res = [];
                orders.forEach((o) => {
                  if (bgCoincide(o.id + " " + o.client + " " + o.driver, q)) {
                    res.push({ tipo: "pedido", clave: o.id, icono: "pkg", titulo: `Pedido #${o.id} · ${o.client}`, sub: "Activo · " + (STAGES[o.stage] || ""), data: o });
                  }
                });
                deliveredToday.forEach((o) => {
                  if (bgCoincide(o.id + " " + o.client + " " + o.driver, q)) {
                    res.push({ tipo: "pedido", clave: "e" + o.id, icono: "check", titulo: `Pedido #${o.id} · ${o.client}`, sub: "Entregado hoy", data: o });
                  }
                });
                historial.forEach((r) => {
                  if (bgCoincide(r[0] + " " + r[2] + " " + r[3], q)) {
                    res.push({ tipo: "hist", clave: "h" + r[0], icono: "clock", titulo: `Pedido #${r[0]} · ${r[2]}`, sub: "Historial · " + r[1], data: r });
                  }
                });
                drivers.forEach((d) => {
                  if (bgCoincide(d.name, q)) {
                    res.push({ tipo: "repartidor", clave: d.name, icono: "truck", titulo: d.name, sub: "Repartidor · ★ " + d.rating, data: d });
                  }
                });
                return res;
              }}
              onPick={(r) => {
                if (r.tipo === "pedido") setOrderDetail(r.data.id);
                else if (r.tipo === "hist") { setView("historial"); setOrderDetail(r.data[0]); }
                else setView("repartidores");
              }}
            />
          ) : (
            <div className="search"><Icon name="search" /><input placeholder="Buscar pedido, cliente…" /></div>
          )}
          <button className="icon-btn" onClick={onToggleTheme}
            title={theme === "light" ? "Modo oscuro" : "Modo claro"}>
            <Icon name={theme === "light" ? "moon" : "sun"} />
          </button>
          {/* Centro de notificaciones unificado (Bloque 8) */}
          {window.CampanaNotificaciones && <window.CampanaNotificaciones />}
          <button className="icon-btn" title="Multas del periodo" onClick={() => setView("plan")}>
            <Icon name="flag" />
            {fines.length > 0 && <span className="dot"></span>}
          </button>
        </header>
        <div className="content">
          {view === "dashboard"    && <Dashboard orders={orders} deliveredToday={deliveredToday} fines={fines} selId={sel} setSel={setSel} fineOpen={fineOpen} setFineOpen={setFineOpen} />}
          {view === "solicitar"    && <PedirDomiciliario usuario={usuario} restId={planData ? planData.restId : null} onCreated={() => { loadRestaurantData(); setView("pedidos"); }} />}
          {view === "analitica"    && <AnaliticaAvanzada hasData={orders.length > 0 || deliveredToday.length > 0 || historial.length > 0} />}
          {view === "pedidos"      && (
            <React.Fragment>
              {/* cola del marketplace (confirmar/rechazar/listo) fusionada con Pedidos */}
              <MkPanelPedidosMk restId={planData ? planData.restId : null} usuario={usuario} modo="integrado" />
              <Pedidos orders={orders} deliveredToday={deliveredToday} setSel={setSel} openDetail={setOrderDetail} />
            </React.Fragment>
          )}
          {view === "repartidores" && <Repartidores drivers={drivers} onOpen={setDriver} />}
          {view === "historial"    && <Historial historial={historial} openDetail={setOrderDetail} />}
          {view === "reclamaciones" && <MisPQRsView usuario={usuario} restId={planData ? planData.restId : null} />}
          {view === "plan"         && <MiPlan fines={fines} />}
          {view === "config"       && <Configuracion usuario={usuario} />}
          {view === "mk-productos" && <MkPanelProductos restId={planData ? planData.restId : null} />}
          {view === "mk-fotos"     && <MkPanelFotos restId={planData ? planData.restId : null} />}
          {view === "mk-perfil"    && <MkPanelPerfil restId={planData ? planData.restId : null} />}
          {view === "mk-pagos"     && <MkPanelPagos restId={planData ? planData.restId : null} />}
        </div>
      </div>

      {driver      && <DriverDrawer name={driver} drivers={drivers} onClose={() => setDriver(null)} />}
      {orderDetail && <OrderDrawer id={orderDetail} orders={orders} deliveredToday={deliveredToday} onClose={() => setOrderDetail(null)} />}
    </div>
  );
}

/* PanelApp: receives auth state from TreegoPlatform — no longer manages auth itself */
function PanelApp({ usuario, onLogout, theme, onToggleTheme }) {
  return <Shell usuario={usuario} onLogout={onLogout} theme={theme} onToggleTheme={onToggleTheme} />;
}

Object.assign(window, { PanelApp, Login, Register, WelcomeIntro, ESTADO_LOGIN_MSG, PWRESET_MSG, pwresetEnUrl });

/* Self-render only in standalone panel.html (not unified index.html) */
if (!window.TREEGO_UNIFIED_MODE) {
  /* Standalone mode: use Supabase auth locally */
  function StandalonePanelApp() {
    const { session, usuario, loading, bloqueo, terminosPend, aceptarTerminos, signIn, signOut } = useAuth();
    const [theme, setTheme] = React.useState(
      () => localStorage.getItem("treego_theme") || "dark"
    );
    /* login | registro — #registro en la URL abre el formulario directo
       (links de marketing "¿Eres restaurante? Regístrate") */
    const [vistaAuth, setVistaAuth] = React.useState(
      () => (window.location.hash === '#registro' ? 'registro' : 'login')
    );
    /* useLayoutEffect: aplica el tema ANTES de pintar — sin flash al cargar */
    React.useLayoutEffect(() => {
      document.body.classList.toggle("theme-light", theme === "light");
      localStorage.setItem("treego_theme", theme);
    }, [theme]);
    const toggleTheme = () => setTheme(t => t === "light" ? "dark" : "light");
    /* rol 'admin' → consola de operaciones (página propia) */
    React.useEffect(() => {
      if (!loading && usuario && usuario.rol === 'admin') {
        window.location.replace('/pages/admin.html');
      }
    }, [loading, usuario]);
    if (loading) return null;
    if (terminosPend) {
      return <TerminosGate onAceptar={aceptarTerminos} onCancelar={signOut} theme={theme} />;
    }
    if (!session || !usuario) {
      /* mensaje de expiración por inactividad (F2 p45) — bandera de un solo uso */
      const expiradaMsg = window.__treegoExpMsg !== undefined
        ? window.__treegoExpMsg
        : (window.__treegoExpMsg = (window.treegoMotivoExpiracion ? window.treegoMotivoExpiracion() : ""));
      if (vistaAuth === 'registro') {
        return <Register theme={theme} onToggleTheme={toggleTheme}
          onBack={() => { setVistaAuth('login'); if (window.location.hash === '#registro') history.replaceState(null, '', window.location.pathname); }} />;
      }
      return <Login onLogin={signIn} theme={theme} onToggleTheme={toggleTheme}
        onGoRegister={() => setVistaAuth('registro')}
        notice={bloqueo ? ESTADO_LOGIN_MSG[bloqueo] : expiradaMsg}
        info={pwresetEnUrl() ? PWRESET_MSG : ""} />;
    }
    if (usuario.rol === 'admin') return null; /* redirigiendo a la consola */
    return <PanelApp usuario={usuario} onLogout={signOut} theme={theme} onToggleTheme={toggleTheme} />;
  }
  ReactDOM.createRoot(document.getElementById("root")).render(<StandalonePanelApp />);
}
