/* Onboarding flow — 5 steps */

const Onboarding = ({ onExit }) => {
  const [step, setStep] = React.useState(0);
  const [authState, setAuthState] = React.useState("idle"); // idle | mfa | syncing | done
  const [bleState, setBleState] = React.useState("idle"); // idle | scanning | results | configuring | active

  React.useEffect(() => {
    if (authState === "syncing") {
      let n = 0;
      const i = setInterval(() => { n += 4; setSyncProgress(Math.min(87, n)); if (n >= 87) clearInterval(i); }, 80);
      return () => clearInterval(i);
    }
  }, [authState]);
  const [syncProgress, setSyncProgress] = React.useState(12);

  return (
    <div style={{ minHeight: "100vh", background: "var(--bg)", display: "flex", flexDirection: "column" }}>
      {/* Header */}
      <header style={{ padding: "18px 32px", borderBottom: "1px solid var(--line)", display: "flex", alignItems: "center", gap: 12 }}>
        <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
          <window.FtiMindLogo.Mark size={24} />
          <window.FtiMindLogo.Wordmark size={14} />
        </div>
        <div style={{ flex: 1 }} />
        <div style={{ display: "flex", gap: 4 }}>
          {Array.from({ length: 5 }).map((_, i) => (
            <div key={i} style={{
              width: 24, height: 3, borderRadius: 2,
              background: i <= step ? "var(--accent)" : "var(--line)",
              transition: "all 0.3s",
            }} />
          ))}
        </div>
        <span style={{ fontSize: 11.5, fontFamily: "var(--font-mono)", color: "var(--text-3)" }}>
          paso {step + 1} de 5
        </span>
        <Btn size="sm" variant="ghost" onClick={onExit}>Salir</Btn>
      </header>

      <div style={{ flex: 1, display: "grid", placeItems: "center", padding: "40px 32px" }}>
        <div style={{ width: "100%", maxWidth: 720 }}>
          {step === 0 && <StepWelcome onNext={() => setStep(1)} />}
          {step === 1 && <StepToken onNext={() => setStep(2)} />}
          {step === 2 && (
            <StepProvider
              authState={authState} setAuthState={setAuthState}
              bleState={bleState} setBleState={setBleState}
              syncProgress={syncProgress}
              onNext={() => setStep(3)}
            />
          )}
          {step === 3 && <StepLLM onNext={() => setStep(4)} />}
          {step === 4 && <StepDone onExit={onExit} />}
        </div>
      </div>
    </div>
  );
};

const StepWelcome = ({ onNext }) => (
  <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 60, alignItems: "center" }}>
    <div>
      <div className="eyebrow" style={{ marginBottom: 14 }}>BIENVENIDO A API_FIT</div>
      <h1 className="display" style={{ margin: 0, fontSize: 44, lineHeight: 1.05, letterSpacing: "-0.025em", marginBottom: 18, fontWeight: 400 }}>
        Tus datos. Tu casa. <span style={{ color: "var(--text-3)" }}>Una IA que ve el cuadro completo.</span>
      </h1>
      <div style={{ fontSize: 14, color: "var(--text-2)", lineHeight: 1.6, marginBottom: 28 }}>
        FtiMind corre en tu NAS, Raspberry Pi o computador. Lee tus relojes y básculas, lo guarda en una base local privada y te asesora con coach, nutricionista y médico que <strong>conocen tu historia completa</strong>.
      </div>
      <Btn variant="primary" size="lg" onClick={onNext} icon={<Icon name="arrow" size={14} />}>Empezar</Btn>
      <div style={{ marginTop: 14, fontSize: 11.5, color: "var(--text-3)" }}>~5 minutos · podés saltar pasos</div>
    </div>
    <Card style={{ background: "var(--bg-2)" }}>
      <div className="eyebrow" style={{ marginBottom: 14 }}>QUÉ VAMOS A HACER</div>
      <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
        {[
          { n: "01", l: "Confirmar tu cuenta", d: "La sesión que iniciaste está lista" },
          { n: "02", l: "Conectar tu primer dispositivo", d: "Garmin, Eufy, otros vienen pronto" },
          { n: "03", l: "Configurar LLM (opcional)", d: "Sin esto: solo dashboards. Con esto: coach completo." },
        ].map((s, i) => (
          <div key={i} style={{ display: "flex", gap: 12, alignItems: "flex-start" }}>
            <div className="num" style={{ fontSize: 11, color: "var(--text-3)", fontFamily: "var(--font-mono)", paddingTop: 2 }}>{s.n}</div>
            <div>
              <div style={{ fontSize: 13, fontWeight: 500 }}>{s.l}</div>
              <div style={{ fontSize: 11.5, color: "var(--text-3)", marginTop: 2 }}>{s.d}</div>
            </div>
          </div>
        ))}
      </div>
      <div style={{ marginTop: 20, padding: 12, background: "var(--good-soft)", borderRadius: "var(--r-2)", fontSize: 11.5, color: "var(--text-2)", display: "flex", gap: 8 }}>
        <Icon name="lock" size={13} color="var(--good)" />
        Todos tus datos viven en tu máquina. Solo lo que envíes al LLM sale.
      </div>
    </Card>
  </div>
);

const StepToken = ({ onNext }) => {
  // Tras el cambio a auth por email+password, este paso ya no pide token: el usuario
  // ya está autenticado al llegar acá (pasó por Login.jsx). Mostramos su sesión.
  const user = API.getUser() || {};
  const inDemo = API.demoMode();
  return (
    <div style={{ maxWidth: 560, margin: "0 auto" }}>
      <div className="eyebrow" style={{ marginBottom: 14 }}>PASO 1 · TU CUENTA</div>
      <h2 className="display" style={{ margin: 0, fontSize: 32, fontWeight: 400, letterSpacing: "-0.02em", marginBottom: 12 }}>
        {inDemo ? "Estás en modo demo." : "Tu cuenta está lista."}
      </h2>
      <div style={{ fontSize: 13.5, color: "var(--text-2)", marginBottom: 24, lineHeight: 1.55 }}>
        {inDemo
          ? "Vas a navegar la app con datos de muestra. Para conectar Garmin/Eufy de verdad, salí del modo demo desde el topbar."
          : "Tus credenciales viven en la base local de FtiMind. El token de sesión expira automáticamente y se renueva al iniciar sesión."}
      </div>
      <div style={{
        padding: 14, background: "var(--bg-1)", border: "1px solid var(--line)",
        borderRadius: "var(--r-2)", marginBottom: 18,
      }}>
        <div style={{ fontSize: 11, color: "var(--text-3)", fontFamily: "var(--font-mono)", marginBottom: 6 }}>SESIÓN ACTUAL</div>
        <div style={{ fontSize: 13.5, fontWeight: 500 }}>{user.name || (inDemo ? "Usuario demo" : "—")}</div>
        <div style={{ fontSize: 12, color: "var(--text-2)", fontFamily: "var(--font-mono)" }}>{user.email || "—"}</div>
      </div>
      <div style={{ display: "flex", justifyContent: "flex-end", marginTop: 24 }}>
        <Btn variant="primary" onClick={onNext} icon={<Icon name="arrow" size={13} />}>Continuar</Btn>
      </div>
    </div>
  );
};

const StepProvider = ({ authState, setAuthState, bleState, setBleState, syncProgress, onNext }) => {
  const [picked, setPicked] = React.useState(null);
  const [providers, setProviders] = React.useState(null);

  React.useEffect(() => {
    if (API.demoMode()) return;
    let alive = true;
    (async () => {
      try {
        const data = await API.listProviders();
        const list = Array.isArray(data) ? data : (data?.providers || []);
        if (alive) setProviders(list);
      } catch (_) { /* fallback al hardcoded */ }
    })();
    return () => { alive = false; };
  }, []);

  if (picked === "garmin") return <GarminAuth state={authState} setState={setAuthState} onBack={() => setPicked(null)} onDone={onNext} progress={syncProgress} />;
  if (picked === "eufy") return <EufyPair state={bleState} setState={setBleState} onBack={() => setPicked(null)} onDone={onNext} />;

  // Si tenemos providers reales del backend los usamos para decidir disponibilidad,
  // si no, hardcodeamos los 4 conocidos para que la UI no se rompa.
  const known = providers
    ? providers.map(p => (typeof p === "string" ? p : p.name)).filter(Boolean).map(n => n.toLowerCase())
    : null;
  const has = (n) => known ? known.includes(n) : (n === "garmin" || n === "eufy");

  return (
    <div>
      <div className="eyebrow" style={{ marginBottom: 14 }}>PASO 2 · CONECTÁ TU PRIMER DISPOSITIVO</div>
      <h2 className="display" style={{ margin: 0, fontSize: 32, fontWeight: 400, letterSpacing: "-0.02em", marginBottom: 12 }}>
        ¿Qué tenés a mano?
      </h2>
      <div style={{ fontSize: 13.5, color: "var(--text-2)", marginBottom: 28 }}>
        Podés conectar más después. Para el MVP soportamos Garmin + Eufy. Otros vienen pronto.
      </div>
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 14 }}>
        <ProviderPick name="Garmin" desc="Reloj/pulsera · vía cloud" caps={["sleep","hrv","rhr","activities","streams","load"]} mode="POLL" available={has("garmin")} onClick={() => setPicked("garmin")} />
        <ProviderPick name="Eufy" desc="Báscula inteligente · BLE local" caps={["weight","body_fat","muscle","bone","water"]} mode="EVENT" available={has("eufy")} onClick={() => setPicked("eufy")} />
        <ProviderPick name="Whoop" desc="Pulsera · vía cloud" caps={["recovery","strain","sleep"]} mode="POLL" available={has("whoop")} />
        <ProviderPick name="Oura" desc="Anillo · vía cloud" caps={["sleep","hrv","temperature"]} mode="POLL" available={has("oura")} />
      </div>
      <div style={{ marginTop: 24, textAlign: "center" }}>
        <Btn variant="ghost" onClick={onNext}>Saltar este paso</Btn>
      </div>
    </div>
  );
};

const ProviderPick = ({ name, desc, caps, mode, available, onClick }) => (
  <button onClick={available ? onClick : undefined} disabled={!available} style={{
    padding: 18, textAlign: "left",
    background: available ? "var(--bg-1)" : "var(--bg-2)",
    border: "1px solid var(--line)",
    borderRadius: "var(--r-3)",
    cursor: available ? "pointer" : "not-allowed",
    opacity: available ? 1 : 0.55,
    transition: "all 0.15s",
  }}
    onMouseEnter={(e) => { if (available) e.currentTarget.style.borderColor = "var(--accent)"; }}
    onMouseLeave={(e) => { if (available) e.currentTarget.style.borderColor = "var(--line)"; }}
  >
    <div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 12 }}>
      <div style={{ width: 36, height: 36, borderRadius: 8, background: "var(--bg-3)", display: "grid", placeItems: "center", fontFamily: "var(--font-mono)", fontWeight: 600, fontSize: 14 }}>
        {name[0]}
      </div>
      <div style={{ flex: 1 }}>
        <div style={{ fontSize: 14, fontWeight: 500 }}>{name}</div>
        <div style={{ fontSize: 11, color: "var(--text-3)" }}>{desc}</div>
      </div>
      <Badge tone={available ? "default" : "default"} style={{ fontSize: 9 }}>{available ? mode : "soon"}</Badge>
    </div>
    <div style={{ display: "flex", gap: 5, flexWrap: "wrap" }}>
      {caps.map(c => (
        <span key={c} style={{ padding: "2px 6px", borderRadius: 3, fontSize: 9.5, fontFamily: "var(--font-mono)", background: "var(--bg-2)", color: "var(--text-3)" }}>{c}</span>
      ))}
    </div>
  </button>
);

const GarminAuth = ({ state, setState, onBack, onDone, progress }) => {
  const [garminEmail, setGarminEmail] = React.useState("");
  const [garminPassword, setGarminPassword] = React.useState("");
  const [mfaCode, setMfaCode] = React.useState("");
  const [authError, setAuthError] = React.useState(null);

  const startGarmin = async () => {
    setAuthError(null);
    if (API.demoMode()) {
      setState("mfa");
      return;
    }
    try {
      const res = await API.startAuth("garmin", {
        style: "interactive",
        credentials: { email: garminEmail, password: garminPassword },
      });
      // Backend devuelve { status: "logged_in" | "awaiting_mfa" | "error" | ... }.
      if (res?.status === "logged_in" || res?.status === "ok" || res?.mfa_required === false) {
        setState("syncing");
      } else if (res?.status === "error") {
        setAuthError(res.error || "No se pudo iniciar sesión con Garmin.");
      } else {
        setState("mfa");
      }
    } catch (e) {
      if (e.network) { setState("mfa"); return; } // demo path
      setAuthError(e.detail?.detail || e.message || "No se pudo iniciar sesión con Garmin.");
    }
  };

  const submitMfa = async () => {
    setAuthError(null);
    if (API.demoMode()) {
      setState("syncing");
      return;
    }
    try {
      await API.submitMfa("garmin", { code: mfaCode });
      setState("syncing");
    } catch (e) {
      if (e.network) { setState("syncing"); return; }
      setAuthError(e.detail?.detail || e.message || "Código inválido.");
    }
  };

  return (
  <div style={{ maxWidth: 520, margin: "0 auto" }}>
    <button onClick={onBack} style={{ background: "none", border: "none", color: "var(--text-3)", fontSize: 12, padding: 0, marginBottom: 12 }}>← Volver</button>
    <div className="eyebrow" style={{ marginBottom: 12 }}>CONECTAR GARMIN</div>
    {authError && (
      <div style={{
        padding: "10px 12px", marginBottom: 12,
        background: "var(--bad-soft)",
        border: "1px solid color-mix(in srgb, var(--bad) 30%, transparent)",
        borderRadius: "var(--r-2)", fontSize: 12, color: "var(--text-2)",
      }}>
        <strong style={{ color: "var(--bad)" }}>Error:</strong> {authError}
      </div>
    )}
    {state === "idle" && (
      <>
        <h2 className="display" style={{ margin: 0, fontSize: 28, fontWeight: 400, letterSpacing: "-0.02em", marginBottom: 18 }}>
          Iniciá sesión con tu cuenta Garmin Connect.
        </h2>
        <div style={{ display: "flex", flexDirection: "column", gap: 10, marginBottom: 18 }}>
          <input type="email" value={garminEmail} onChange={(e) => setGarminEmail(e.target.value)} placeholder="email@dominio.com" style={{ ...fieldStyle3 }} />
          <input type="password" value={garminPassword} onChange={(e) => setGarminPassword(e.target.value)} placeholder="contraseña" style={{ ...fieldStyle3 }} />
        </div>
        <Btn variant="primary" size="lg" onClick={startGarmin}>Iniciar sesión</Btn>
      </>
    )}
    {state === "mfa" && (
      <>
        <h2 className="display" style={{ margin: 0, fontSize: 28, fontWeight: 400, letterSpacing: "-0.02em", marginBottom: 12 }}>
          Código de verificación.
        </h2>
        <div style={{ fontSize: 13, color: "var(--text-2)", marginBottom: 18 }}>
          Te enviamos un código a tu email. <span style={{ color: "var(--accent)" }}>Expira en 04:42.</span>
        </div>
        <input type="text" value={mfaCode} onChange={(e) => setMfaCode(e.target.value)} placeholder="••••••" maxLength={6} style={{ ...fieldStyle3, fontFamily: "var(--font-mono)", fontSize: 22, letterSpacing: "0.5em", textAlign: "center" }} />
        <div style={{ marginTop: 18, display: "flex", justifyContent: "space-between" }}>
          <Btn variant="ghost" onClick={startGarmin}>Reenviar código</Btn>
          <Btn variant="primary" onClick={submitMfa}>Verificar</Btn>
        </div>
      </>
    )}
    {state === "syncing" && (
      <>
        <h2 className="display" style={{ margin: 0, fontSize: 28, fontWeight: 400, letterSpacing: "-0.02em", marginBottom: 12 }}>
          Sincronizando tu historial...
        </h2>
        <div style={{ fontSize: 13, color: "var(--text-2)", marginBottom: 18 }}>
          Bajando actividades, sueño y métricas diarias. Podés cerrar esto, sigue en background.
        </div>
        <div style={{ height: 8, background: "var(--bg-2)", borderRadius: 4, overflow: "hidden", marginBottom: 8 }}>
          <div style={{ height: "100%", width: `${progress}%`, background: "var(--accent)", transition: "width 0.2s" }} />
        </div>
        <div style={{ fontSize: 11.5, fontFamily: "var(--font-mono)", color: "var(--text-3)", display: "flex", justifyContent: "space-between" }}>
          <span>{progress}/87 actividades</span>
          <span>~{Math.max(0, 87 - progress) * 2}s restantes</span>
        </div>
        <div style={{ marginTop: 24, textAlign: "right" }}>
          <Btn variant="primary" onClick={onDone}>Continuar (sigue en background)</Btn>
        </div>
      </>
    )}
  </div>
  );
};

// Modelos soportados por eufylife-ble-client. La key es el `model_id` que
// el backend espera en `configure`; el value es el nombre comercial.
const EUFY_MODELS = [
  { id: "eufy T9120", name: "Smart Scale A1" },
  { id: "eufy T9130", name: "Smart Scale C20" },
  { id: "eufy T9140", name: "Smart Scale" },
  { id: "eufy T9146", name: "Smart Scale C1" },
  { id: "eufy T9147", name: "Smart Scale P1" },
  { id: "eufy T9148", name: "Smart Scale P2" },
  { id: "eufy T9149", name: "Smart Scale P2 Pro" },
  { id: "eufy T9150", name: "Smart Scale P3" },
];

const _eufyModelLabel = (modelId) => {
  const hit = EUFY_MODELS.find((m) => m.id === modelId);
  return hit ? `${hit.id.replace("eufy ", "")} · ${hit.name}` : (modelId || "modelo desconocido");
};

const EufyPair = ({ state, setState, onBack, onDone }) => {
  const [discovered, setDiscovered] = React.useState([]);
  const [selectedAddress, setSelectedAddress] = React.useState(null);
  const [modelId, setModelId] = React.useState("eufy T9146"); // C1 = T9146; default sensato
  const [mode, setMode] = React.useState("local_ble");
  const [manualAddress, setManualAddress] = React.useState("");
  const [manualMode, setManualMode] = React.useState(false);
  const [sex, setSex] = React.useState("male");
  const [age, setAge] = React.useState(32);
  const [heightCm, setHeightCm] = React.useState(178);
  const [pairError, setPairError] = React.useState(null);
  const [submitting, setSubmitting] = React.useState(false);
  const [includeAll, setIncludeAll] = React.useState(false);

  const startScan = async (allFlag = includeAll) => {
    setPairError(null);
    setDiscovered([]);
    setSelectedAddress(null);
    setManualMode(false);
    setState("scanning");
    if (API.demoMode()) {
      // Modo demo: simulamos un C1 detectado.
      setTimeout(() => {
        setDiscovered([{ address: "CF:35:1A:B8:42:7D", name: "eufy T9146 Scale", rssi: -52, model_guess: "eufy T9146" }]);
        setSelectedAddress("CF:35:1A:B8:42:7D");
        setModelId("eufy T9146");
        setState("results");
      }, 1800);
      return;
    }
    try {
      const res = await API.extras("eufy", "discover", { seconds: 10, include_all: allFlag });
      const found = Array.isArray(res?.devices) ? res.devices : [];
      setDiscovered(found);
      if (found.length > 0) {
        setSelectedAddress(found[0].address);
        if (found[0].model_guess) setModelId(found[0].model_guess);
      }
      setState("results");
    } catch (e) {
      if (e.network) {
        setPairError("No hay conexión con el backend. Probá levantar el server o usar modo demo.");
        setState("idle");
        return;
      }
      const msg = e.detail?.detail || e.message || "El escaneo BLE falló.";
      // En macOS la causa más común es que el host no le dio permisos de
      // Bluetooth al proceso uvicorn → bleak devuelve un error específico.
      const hint = /permission|authoriz|bluetooth.*not.*on|adapter|hci/i.test(msg)
        ? " · Pista: probá con `address` manual o asegurate que el host (Linux: /dev/hci0; macOS: permisos BT) tiene BLE habilitado."
        : "";
      setPairError(`${msg}${hint}`);
      setState("idle");
    }
  };

  const confirmPair = async () => {
    setPairError(null);
    setSubmitting(true);
    try {
      const address = manualMode ? manualAddress.trim() : selectedAddress;
      if (!address) {
        setPairError("Falta la dirección BLE de la báscula.");
        return;
      }
      // Validación mínima. Aceptamos dos formatos:
      //   - MAC clásica (Linux/Windows): AA:BB:CC:DD:EE:FF
      //   - UUID CoreBluetooth (macOS): 12345678-1234-1234-1234-123456789ABC
      // En macOS la MAC real está oculta y bleak entrega un UUID per-app.
      const isMac = /^([0-9a-f]{2}[:\-]){5}[0-9a-f]{2}$/i.test(address);
      const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(address);
      if (!isMac && !isUuid) {
        setPairError("La dirección no parece válida. Acepto MAC (AA:BB:CC:DD:EE:FF) o UUID CoreBluetooth (8-4-4-4-12 hex con guiones).");
        return;
      }
      if (API.demoMode()) { onDone(); return; }
      const payload = {
        address,
        mode,
        model_id: modelId,
        sex,
        age: Number(age),
        height_cm: Number(heightCm),
      };
      await API.extras("eufy", "configure", payload);
      try {
        await API.startListener("eufy");
      } catch (le) {
        // configure ok pero el listener no arrancó (BLE no disponible en el
        // host). Mostramos el error pero seguimos: la config quedó persistida
        // y el usuario puede reintentar desde Configuración → "Reiniciar listener".
        const msg = le.detail?.detail || le.message || "El listener BLE no pudo arrancar.";
        setPairError(`Configuración guardada, pero el listener falló: ${msg}. Reiniciá desde Configuración cuando el BLE esté disponible.`);
        return;
      }
      onDone();
    } catch (e) {
      if (e.network) { onDone(); return; }
      setPairError(e.detail?.detail || e.message || "No se pudo guardar la configuración Eufy.");
    } finally {
      setSubmitting(false);
    }
  };

  return (
    <div style={{ maxWidth: 600, margin: "0 auto" }}>
      <button onClick={onBack} style={{ background: "none", border: "none", color: "var(--text-3)", fontSize: 12, padding: 0, marginBottom: 12 }}>← Volver</button>
      <div className="eyebrow" style={{ marginBottom: 12 }}>EMPAREJAR EUFY · BLE LOCAL</div>
      {pairError && (
        <div style={{
          padding: "10px 12px", marginBottom: 12,
          background: "var(--bad-soft)",
          border: "1px solid color-mix(in srgb, var(--bad) 30%, transparent)",
          borderRadius: "var(--r-2)", fontSize: 12, color: "var(--text-2)",
        }}>
          <strong style={{ color: "var(--bad)" }}>Error:</strong> {pairError}
        </div>
      )}
      {state === "idle" && (
        <>
          <h2 className="display" style={{ margin: 0, fontSize: 28, fontWeight: 400, letterSpacing: "-0.02em", marginBottom: 12 }}>
            Despertá la báscula y subite encima.
          </h2>
          <div style={{ fontSize: 13, color: "var(--text-2)", marginBottom: 18, lineHeight: 1.55 }}>
            Vamos a escanear BLE durante ~10 segundos. La báscula se anuncia mientras alguien está sobre ella o justo después de pisarla. Si no aparece, podés ver todos los devices BLE o ingresar la dirección a mano.
          </div>
          <div style={{ display: "flex", gap: 10, flexWrap: "wrap" }}>
            <Btn variant="primary" size="lg" onClick={() => startScan(false)} icon={<Icon name="bluetooth" size={14} />}>Iniciar escaneo</Btn>
            <Btn variant="default" size="lg" onClick={() => { setIncludeAll(true); startScan(true); }}>Ver todos los BLE</Btn>
            <Btn variant="ghost" size="lg" onClick={() => { setManualMode(true); setState("results"); }}>Ingresar manualmente</Btn>
          </div>
          <div style={{ fontSize: 11, color: "var(--text-3)", marginTop: 14, lineHeight: 1.5 }}>
            <strong>macOS</strong> oculta la MAC real de los devices BLE y la reemplaza por un UUID aleatorio, por eso a veces el filtro Eufy/Anker no encuentra la báscula. En ese caso usá <strong>"Ver todos los BLE"</strong> y elegila por nombre o RSSI más fuerte mientras estás parado encima.
          </div>
        </>
      )}
      {state === "scanning" && (
        <div style={{ padding: 40, textAlign: "center" }}>
          <div style={{ width: 60, height: 60, borderRadius: "50%", border: "3px solid var(--line)", borderTopColor: "var(--info)", animation: "spin 1s linear infinite", margin: "0 auto 18px" }} />
          <div className="display" style={{ fontSize: 22 }}>Escaneando dispositivos BLE...</div>
          <div style={{ fontSize: 12, color: "var(--text-3)", marginTop: 6, fontFamily: "var(--font-mono)" }}>timeout 10s · subite a la báscula ahora</div>
        </div>
      )}
      {state === "results" && (
        <>
          {!manualMode && discovered.length === 0 && (
            <>
              <h2 className="display" style={{ margin: 0, fontSize: 22, fontWeight: 400, letterSpacing: "-0.02em", marginBottom: 10 }}>
                {includeAll ? "El scan no detectó ningún BLE." : "No encontré básculas Eufy con el filtro."}
              </h2>
              <div style={{ fontSize: 12.5, color: "var(--text-2)", marginBottom: 18, lineHeight: 1.55 }}>
                {includeAll
                  ? "Asegurate que el host tiene Bluetooth encendido y permisos BLE concedidos al proceso. En macOS: Configuración → Privacidad → Bluetooth."
                  : "En macOS la MAC real está oculta, así que el filtro Eufy/Anker puede fallar aunque la báscula esté presente. Probá \"Ver todos los BLE\" para listar todo lo que hay alrededor."}
              </div>
              <div style={{ display: "flex", gap: 10 }}>
                <Btn variant="ghost" onClick={() => { setIncludeAll(false); setState("idle"); }}>Volver a escanear</Btn>
                {!includeAll && (
                  <Btn variant="default" onClick={() => { setIncludeAll(true); startScan(true); }}>
                    Ver todos los BLE
                  </Btn>
                )}
                <Btn variant="default" onClick={() => setManualMode(true)}>Ingresar manualmente</Btn>
              </div>
            </>
          )}

          {!manualMode && discovered.length > 0 && (
            <>
              <h2 className="display" style={{ margin: 0, fontSize: 24, fontWeight: 400, letterSpacing: "-0.02em", marginBottom: 14 }}>
                {discovered.length === 1 ? "Encontré 1 báscula. Confirmá:" : `Encontré ${discovered.length} bácsulas. Elegí cuál:`}
              </h2>
              <Card padded={false}>
                {discovered.map((d, i) => {
                  const isSel = selectedAddress === d.address;
                  const tone = d.rssi == null ? "default" : d.rssi >= -60 ? "good" : d.rssi >= -75 ? "warn" : "bad";
                  return (
                    <div key={d.address}
                      onClick={() => {
                        setSelectedAddress(d.address);
                        if (d.model_guess) setModelId(d.model_guess);
                      }}
                      style={{
                        padding: 14, display: "flex", alignItems: "center", gap: 14, cursor: "pointer",
                        borderBottom: i === discovered.length - 1 ? "none" : "1px solid var(--line-soft)",
                        background: isSel ? "color-mix(in srgb, var(--accent) 6%, transparent)" : "transparent",
                      }}>
                      <Icon name="weight" size={22} color={isSel ? "var(--accent)" : "var(--text)"} />
                      <div style={{ flex: 1, minWidth: 0 }}>
                        <div style={{ fontSize: 14, fontWeight: 500 }}>{d.name || "Eufy Scale"}</div>
                        <div style={{ fontSize: 11, color: "var(--text-3)", fontFamily: "var(--font-mono)", overflow: "hidden", textOverflow: "ellipsis" }}>
                          {d.address} · RSSI {d.rssi ?? "?"} dBm{d.model_guess ? ` · ${d.model_guess.replace("eufy ", "")}` : ""}
                        </div>
                      </div>
                      <Badge tone={tone}><Dot tone={tone} /> {d.rssi == null ? "?" : d.rssi >= -60 ? "fuerte" : d.rssi >= -75 ? "ok" : "débil"}</Badge>
                    </div>
                  );
                })}
              </Card>
              <div style={{ marginTop: 8 }}>
                <Btn variant="ghost" size="sm" onClick={() => setManualMode(true)}>¿No es ninguna? Ingresar manualmente</Btn>
              </div>
            </>
          )}

          {manualMode && (
            <>
              <h2 className="display" style={{ margin: 0, fontSize: 22, fontWeight: 400, letterSpacing: "-0.02em", marginBottom: 12 }}>
                Ingresá la dirección de la báscula
              </h2>
              <div style={{ fontSize: 12, color: "var(--text-3)", marginBottom: 14 }}>
                Formato: <code>AA:BB:CC:DD:EE:FF</code>. La encontrás en la app de Eufy o pisando "i" sobre el dispositivo en una app BLE genérica.
              </div>
              <input
                type="text"
                value={manualAddress}
                onChange={(e) => setManualAddress(e.target.value)}
                placeholder="AA:BB:CC:DD:EE:FF"
                style={{ ...fieldStyle3, fontFamily: "var(--font-mono)", letterSpacing: "0.05em" }}
              />
              <div style={{ marginTop: 8 }}>
                <Btn variant="ghost" size="sm" onClick={() => { setManualMode(false); }}>Volver a la lista escaneada</Btn>
              </div>
            </>
          )}

          <div style={{ marginTop: 18 }}>
            <div className="eyebrow" style={{ marginBottom: 8 }}>MODELO</div>
            <select
              value={modelId}
              onChange={(e) => setModelId(e.target.value)}
              style={{ ...fieldStyle3, width: "100%" }}
            >
              {EUFY_MODELS.map((m) => (
                <option key={m.id} value={m.id}>{_eufyModelLabel(m.id)}</option>
              ))}
            </select>
            <div style={{ fontSize: 11, color: "var(--text-3)", marginTop: 6 }}>
              Para la <strong>Eufy Smart Scale C1</strong> elegí <code>T9146</code>. La selección define cómo se decodifica el payload BLE.
            </div>
          </div>

          <div style={{ marginTop: 16 }}>
            <div className="eyebrow" style={{ marginBottom: 8 }}>MODO DE LECTURA</div>
            <div style={{ display: "flex", gap: 10 }}>
              <button type="button" onClick={() => setMode("local_ble")} style={{
                flex: 1, padding: "12px 14px", textAlign: "left",
                border: mode === "local_ble" ? "1.5px solid var(--accent)" : "1px solid var(--line)",
                background: mode === "local_ble" ? "color-mix(in srgb, var(--accent) 8%, var(--bg-1))" : "var(--bg-1)",
                borderRadius: "var(--r-2)", cursor: "pointer",
              }}>
                <div style={{ fontFamily: "var(--font-mono)", fontSize: 12, fontWeight: 500, color: mode === "local_ble" ? "var(--accent)" : "var(--text)" }}>local_ble</div>
                <div style={{ fontSize: 11, color: "var(--text-3)", marginTop: 4 }}>Lectura directa desde el contenedor (host con BT)</div>
              </button>
              <button type="button" onClick={() => setMode("esphome_proxy")} style={{
                flex: 1, padding: "12px 14px", textAlign: "left",
                border: mode === "esphome_proxy" ? "1.5px solid var(--accent)" : "1px solid var(--line)",
                background: mode === "esphome_proxy" ? "color-mix(in srgb, var(--accent) 8%, var(--bg-1))" : "var(--bg-1)",
                borderRadius: "var(--r-2)", cursor: "pointer",
              }}>
                <div style={{ fontFamily: "var(--font-mono)", fontSize: 12, fontWeight: 500, color: mode === "esphome_proxy" ? "var(--accent)" : "var(--text)" }}>esphome_proxy</div>
                <div style={{ fontSize: 11, color: "var(--text-3)", marginTop: 4 }}>Vía proxy ESPHome (host sin BT)</div>
              </button>
            </div>
          </div>

          <div style={{ marginTop: 20 }}>
            <div className="eyebrow" style={{ marginBottom: 8 }}>DATOS PARA BMR/BMI</div>
            <div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 10 }}>
              <Field label="Sexo">
                <select value={sex} onChange={(e) => setSex(e.target.value)} style={fieldStyle3}>
                  <option value="male">Masculino</option>
                  <option value="female">Femenino</option>
                </select>
              </Field>
              <Field label="Edad">
                <input type="number" value={age} onChange={(e) => setAge(e.target.value)} style={fieldStyle3} />
              </Field>
              <Field label="Altura cm">
                <input type="number" value={heightCm} onChange={(e) => setHeightCm(e.target.value)} style={fieldStyle3} />
              </Field>
            </div>
          </div>
          <div style={{ marginTop: 24, display: "flex", justifyContent: "space-between" }}>
            <Btn variant="ghost" onClick={() => setState("idle")}>Volver a escanear</Btn>
            <Btn variant="primary" onClick={confirmPair} disabled={submitting}>
              {submitting ? "Guardando…" : "Confirmar y arrancar listener"}
            </Btn>
          </div>
        </>
      )}
    </div>
  );
};

const RadioCard = ({ l, d, picked }) => (
  <button style={{
    flex: 1, padding: "12px 14px", textAlign: "left",
    border: picked ? "1.5px solid var(--accent)" : "1px solid var(--line)",
    background: picked ? "color-mix(in srgb, var(--accent) 8%, var(--bg-1))" : "var(--bg-1)",
    borderRadius: "var(--r-2)", cursor: "pointer",
  }}>
    <div style={{ fontFamily: "var(--font-mono)", fontSize: 12, fontWeight: 500, color: picked ? "var(--accent)" : "var(--text)" }}>{l}</div>
    <div style={{ fontSize: 11, color: "var(--text-3)", marginTop: 4 }}>{d}</div>
  </button>
);

const StepLLM = ({ onNext }) => {
  const [provider, setProvider] = React.useState("OpenAI");
  const [model, setModel] = React.useState("gpt-4o-mini");
  const [apiKey, setApiKey] = React.useState("");
  const [saving, setSaving] = React.useState(false);
  const [error, setError] = React.useState(null);

  const saveAndContinue = async () => {
    if (!apiKey || apiKey.length < 6) {
      // Sin API key, simplemente saltar
      onNext();
      return;
    }
    if (API.demoMode()) { onNext(); return; }
    setSaving(true);
    setError(null);
    try {
      await API.setLLMConfig({
        provider: provider.toLowerCase(),
        default_model: model,
        api_key: apiKey,
      });
      onNext();
    } catch (e) {
      if (e.network) { onNext(); return; }
      setError(e.detail?.detail || e.message || "No se pudo guardar la configuración LLM.");
    } finally {
      setSaving(false);
    }
  };

  return (
  <div style={{ maxWidth: 600, margin: "0 auto" }}>
    <div className="eyebrow" style={{ marginBottom: 12 }}>PASO 3 · LLM (OPCIONAL)</div>
    <h2 className="display" style={{ margin: 0, fontSize: 32, fontWeight: 400, letterSpacing: "-0.02em", marginBottom: 12 }}>
      ¿Activamos al coach ahora?
    </h2>
    <div style={{ fontSize: 13.5, color: "var(--text-2)", marginBottom: 22, lineHeight: 1.55 }}>
      Sin LLM: dashboards, métricas y planes manuales. <strong>Con LLM</strong>: coach, nutricionista y médico que cruzan tus datos. Podés saltarlo y configurarlo después.
    </div>
    {error && (
      <div style={{
        padding: "10px 12px", marginBottom: 12,
        background: "var(--bad-soft)",
        border: "1px solid color-mix(in srgb, var(--bad) 30%, transparent)",
        borderRadius: "var(--r-2)", fontSize: 12, color: "var(--text-2)",
      }}>
        <strong style={{ color: "var(--bad)" }}>Error:</strong> {error}
      </div>
    )}
    <Card>
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
        <Field label="Provider">
          <select value={provider} onChange={(e) => setProvider(e.target.value)} style={fieldStyle3}>
            <option>OpenAI</option><option>Anthropic</option><option>Ollama</option>
          </select>
        </Field>
        <Field label="Modelo">
          <select value={model} onChange={(e) => setModel(e.target.value)} style={fieldStyle3}>
            <option>gpt-4o-mini</option><option>gpt-4o</option><option>claude-haiku-4-5</option>
          </select>
        </Field>
        <Field label="API key" full>
          <input type="password" value={apiKey} onChange={(e) => setApiKey(e.target.value)} placeholder="sk-..." style={fieldStyle3} />
        </Field>
      </div>
      <div style={{ marginTop: 12, fontSize: 11.5, color: "var(--text-3)", display: "flex", alignItems: "flex-start", gap: 8 }}>
        <Icon name="cloud" size={12} color="var(--info)" />
        <span>El provider procesará tus datos en sus servidores. Vas a poder activar "Anonimizar" después.</span>
      </div>
    </Card>
    <div style={{ marginTop: 24, display: "flex", justifyContent: "space-between" }}>
      <Btn variant="ghost" onClick={onNext}>Saltar por ahora</Btn>
      <Btn variant="primary" disabled={saving} onClick={saveAndContinue}>{saving ? "Guardando…" : "Guardar y continuar"}</Btn>
    </div>
  </div>
  );
};

/* StepDone — saluda con el nombre/email real del usuario y describe qué pasa
 * después en términos genéricos. Antes hardcodeaba "Daniel" + "87 actividades"
 * a TODOS los usuarios. */
const StepDone = ({ onExit }) => {
  const user = (typeof API !== "undefined" && API.getUser) ? API.getUser() : null;
  const greeting = (() => {
    if (!user) return "Todo listo.";
    const name = user.name || user.full_name;
    if (name && name.trim()) return `Todo listo, ${name.trim().split(/\s+/)[0]}.`;
    if (user.email) return `Todo listo, ${user.email.split("@")[0]}.`;
    return "Todo listo.";
  })();

  return (
    <div style={{ textAlign: "center", maxWidth: 520, margin: "0 auto" }}>
      <div style={{
        width: 56, height: 56, borderRadius: "50%",
        background: "var(--good-soft)", color: "var(--good)",
        display: "grid", placeItems: "center", margin: "0 auto 18px",
      }}>
        <Icon name="check" size={28} color="var(--good)" />
      </div>
      <h1 className="display" style={{ margin: 0, fontSize: 36, fontWeight: 400, letterSpacing: "-0.025em", marginBottom: 12 }}>
        {greeting}
      </h1>
      <div style={{ fontSize: 14, color: "var(--text-2)", marginBottom: 30, lineHeight: 1.6 }}>
        Las cuentas que conectaste se están sincronizando en background.<br />
        En unos minutos verás tus datos en el dashboard.
      </div>
      <Btn variant="primary" size="lg" onClick={onExit} icon={<Icon name="arrow" size={14} />}>Ir al dashboard</Btn>
    </div>
  );
};

const fieldStyle3 = {
  width: "100%", padding: "10px 12px", background: "var(--bg-2)", border: "1px solid var(--line)",
  borderRadius: "var(--r-2)", fontSize: 13, color: "var(--text)", outline: "none", marginTop: 4,
};

window.Onboarding = Onboarding;
// Expose EufyPair so screen-settings.jsx can render it inline en su ConnectModal
// y evitar el redirect a /onboarding (que rompía el flujo modal).
window.EufyPair = EufyPair;
window.fieldStyle3 = fieldStyle3;
