/* ═══════════════════════════════════════════════════════════════
   FoxBot Pro — app shell + entry
   ═══════════════════════════════════════════════════════════════ */

const { useState: useStateApp, useEffect: useEffectApp } = React;

/* ─── Error Boundary : empêche un crash écran de tuer toute l'app ─── */
class ErrorBoundary extends React.Component {
  constructor(props) { super(props); this.state = { err: null }; }
  static getDerivedStateFromError(err) { return { err }; }
  componentDidCatch(err, info) { console.error("[ErrorBoundary]", err, info); }
  render() {
    if (this.state.err) {
      return (
        <div className="page" style={{ display:"grid", placeItems:"center", padding:60, gap:16, textAlign:"center", minHeight:"60vh" }}>
          <div style={{ width:64, height:64, borderRadius:"50%", background:"rgba(239,68,68,0.12)", display:"grid", placeItems:"center" }}>
            <Icon.AlertTriangle size={28} style={{ color:"var(--bear)" }}/>
          </div>
          <div>
            <h3 style={{ margin:0, color:"var(--text-primary)" }}>Cet écran a planté</h3>
            <p className="muted" style={{ fontSize:12, marginTop:6, maxWidth:340 }}>
              {String(this.state.err && this.state.err.message || this.state.err)}
            </p>
          </div>
          <div style={{ display:"flex", gap:8 }}>
            <button className="btn ghost" onClick={()=>this.setState({ err: null })}>Réessayer</button>
            <button className="btn primary" onClick={()=>window.location.hash="#/dashboard"}>Retour dashboard</button>
          </div>
        </div>
      );
    }
    return this.props.children;
  }
}

function App() {
  const [route, setRoute] = useStateApp(() => {
    const h = window.location.hash.replace("#/", "") || "dashboard";
    return ["dashboard","signaux","volatile","trading","charts","journal","stats","settings"].includes(h) ? h : "dashboard";
  });
  const [paletteOpen, setPaletteOpen] = useStateApp(false);
  const store = window.useStore ? window.useStore() : window.MOCK;
  const mode  = store.MODE || "PAPER";

  // Route → hash
  useEffectApp(() => { window.location.hash = "#/" + route; }, [route]);

  // ⌘K / Ctrl+K → ouvre command palette
  useEffectApp(() => {
    function onKey(e) {
      if ((e.metaKey || e.ctrlKey) && e.key === "k") {
        e.preventDefault();
        setPaletteOpen(o => !o);
      }
    }
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, []);

  async function handleAction(kind, signal) {
    const isPaper = kind.startsWith("paper");
    const dir     = kind.endsWith("long") ? "LONG" : "SHORT";
    try {
      const res = await window.fbAction(isPaper ? "paper" : "real", dir, signal.fullPair || (signal.pair + "/USDT:USDT"));
      const isReal = !isPaper;
      emitToast({
        type: res.ok ? (isReal ? "success" : "info") : "error",
        msg: res.ok ? `${isPaper ? "Paper trade" : "Position réelle"} · ${signal.pair} ${dir}` : `Erreur · ${res.msg || "ouverture impossible"}`,
        sub: res.ok ? `Entry ${fmtNum(signal.entry)} · SL ${fmtNum(signal.sl)} · TP ${fmtNum(signal.tp1)}` : null,
      });
    } catch (e) {
      emitToast({ type: "error", msg: `Erreur · ${e.message}` });
    }
  }

  return (
    <div className="app">
      <Sidebar route={route} onRoute={setRoute} mode={mode}/>
      <div className="main">
        <Topbar route={route} mode={mode} onOpenPalette={()=>setPaletteOpen(true)}/>
        {store.CONN_LOST && <ConnectionBanner/>}
        <ErrorBoundary key={route}>
          {route === "dashboard" && <DashboardScreen onAction={handleAction}/>}
          {route === "signaux"   && <SignauxScreen onAction={handleAction}/>}
          {route === "volatile"  && <VolatileScreen onAction={handleAction}/>}
          {route === "trading"   && <TradingScreen/>}
          {route === "charts"    && <ChartsScreen/>}
          {route === "journal"   && <JournalScreen/>}
          {route === "stats"     && <StatsScreen/>}
          {route === "settings"  && <SettingsScreen/>}
        </ErrorBoundary>
      </div>
      <BottomNav route={route} onRoute={setRoute}/>
      <ToastHost/>
      {paletteOpen && <CommandPalette onClose={()=>setPaletteOpen(false)} onRoute={setRoute}/>}
    </div>
  );
}

/* ─── Sidebar ─── */
function Sidebar({ route, onRoute, mode }) {
  const store = window.useStore ? window.useStore() : {};
  const signalsCount = (store.SIGNALS || []).filter(s => s.state === "active").length;
  const volCount     = (store.VOLATILE || []).filter(s => s.state === "active").length;
  const posCount     = (store.POSITIONS || []).length;
  const capital      = store.STATUS?.capital ?? "—";
  const autoTrade    = !!store.STATUS?.auto_trade;
  const minConf      = store.STATUS?.auto_min_confidence ?? 8;

  const items = [
    { id: "dashboard", icon: Icon.Home,    label: "Dashboard" },
    { id: "signaux",   icon: Icon.Zap,     label: "Signaux",  badge: signalsCount || null },
    { id: "volatile",  icon: Icon.Flame,   label: "Volatile", badge: volCount || null },
    { id: "trading",   icon: Icon.TrendUp, label: "Trading",  badge: posCount || null },
    { id: "charts",    icon: Icon.Chart,   label: "Charts" },
    { id: "journal",   icon: Icon.Book,    label: "Journal" },
  ];
  const items2 = [
    { id: "stats",     icon: Icon.Stats,    label: "Stats" },
    { id: "settings",  icon: Icon.Settings, label: "Settings" },
  ];
  return (
    <aside className="sidebar">
      <div className="brand">
        <FoxMark size={36}/>
        <div className="brand-text">
          <span className="brand-name">FoxBot</span>
          <span className="brand-sub">PRO · TERMINAL</span>
        </div>
      </div>

      <div className="account-pill">
        <span className={`live-dot ${mode === "PAPER" ? "paper-dot" : ""}`}></span>
        <span>{mode === "PAPER" ? `PAPER · ${capital} €` : `LIVE · ${(store.STATUS?.usdc||0).toFixed(0)} $`}</span>
      </div>

      <nav className="nav">
        <div className="nav-section-label">TRADING</div>
        {items.map(it => (
          <div key={it.id} className={`nav-item ${route===it.id?"active":""}`} onClick={()=>onRoute(it.id)}>
            <it.icon size={15} className="icon"/>
            <span>{it.label}</span>
            {it.badge != null && <span className="badge">{it.badge}</span>}
          </div>
        ))}
        <div className="nav-section-label">SYSTÈME</div>
        {items2.map(it => (
          <div key={it.id} className={`nav-item ${route===it.id?"active":""}`} onClick={()=>onRoute(it.id)}>
            <it.icon size={15} className="icon"/>
            <span>{it.label}</span>
          </div>
        ))}
      </nav>

      <div className="sidebar-foot">
        <div className="card pad" style={{ padding:"10px 12px", display:"flex", flexDirection:"column", gap:8 }}>
          <div style={{ display:"flex", alignItems:"center", justifyContent:"space-between" }}>
            <span className="caption">AUTO-TRADE</span>
            <ToggleMini value={autoTrade} onClick={()=>window.fbToggle("auto")}/>
          </div>
          <div style={{ display:"flex", alignItems:"center", justifyContent:"space-between" }}>
            <span className="caption">SEUIL</span>
            <span className="mono primary" style={{ fontSize:12 }}>≥ {minConf}.0</span>
          </div>
        </div>
        <div className="user-card" onClick={()=>{ if(confirm("Se déconnecter ?")) window.fbLogout(); }} style={{ cursor:"pointer" }} title="Cliquer pour se déconnecter">
          <div className="avatar">{(store.AUTH?.user?.display_name || store.AUTH?.user?.email || "FB").slice(0,2).toUpperCase()}</div>
          <div className="user-meta">
            <span className="user-name">{store.AUTH?.user?.display_name || store.AUTH?.user?.email?.split("@")[0] || "Trader"}</span>
            <span className="user-tier" style={{ display:"flex", alignItems:"center", gap:6 }}>
              <span style={{
                width:6, height:6, borderRadius:"50%",
                background: store.CONN_LOST ? "var(--bear)" : "var(--bull)",
                boxShadow: store.CONN_LOST ? "0 0 6px var(--bear)" : "0 0 6px var(--bull)",
                animation: store.CONN_LOST ? "none" : "pulse-dot 2s ease-in-out infinite",
              }}></span>
              OKX · {store.CONN_LOST ? "RECONNEXION..." : "LIVE"}
            </span>
          </div>
        </div>
      </div>
    </aside>
  );
}

function ToggleMini({ value, onClick }) {
  return (
    <div onClick={onClick} style={{
      width: 28, height: 16, borderRadius: 999,
      background: value ? "var(--accent-500)" : "var(--bg-surface)",
      position: "relative", cursor: onClick ? "pointer" : "default",
      transition: "background 220ms",
      boxShadow: value ? "0 0 8px var(--accent-glow)" : "inset 0 0 0 1px var(--border-base)",
    }}>
      <span style={{
        position: "absolute", top: 2, left: value ? 14 : 2,
        width: 12, height: 12, borderRadius: "50%",
        background: "white",
        transition: "left 220ms cubic-bezier(0.4, 0, 0.2, 1)",
      }}></span>
    </div>
  );
}

function FoxMark({ size = 16 }) {
  return (
    <img src="/static/icon-192.png" alt="FoxBot Pro" width={size} height={size}
      style={{ display:"block", borderRadius: Math.max(4, size * 0.18), objectFit:"contain" }}/>
  );
}

/* ─── Topbar ─── */
function Topbar({ route, mode, onOpenPalette }) {
  const titles = {
    dashboard: "Dashboard", signaux: "Signaux", volatile: "Volatile",
    charts: "Charts", journal: "Journal", stats: "Stats", settings: "Settings"
  };
  // Live ticker — re-render toutes les 500ms pour afficher le delta WS
  const store = window.useStore ? window.useStore() : {};
  const [, force] = useStateApp(0);
  useEffectApp(() => {
    const t = setInterval(() => force(n => n + 1), 500);
    return () => clearInterval(t);
  }, []);
  const lastTs = store.LAST_WS_TS || 0;
  const deltaMs = lastTs ? Date.now() - lastTs : null;
  const live = deltaMs != null && deltaMs < 2000;
  return (
    <header className="topbar">
      <div className="topbar-title">
        <span className="crumb">FoxBot</span>
        <Icon.ChevronRight size={11} className="dim"/>
        <h1>{titles[route] || route}</h1>
      </div>
      <div className="topbar-tick" style={{
        display:"flex", alignItems:"center", gap:6,
        padding:"4px 10px",
        background:"var(--bg-elevated)", border:"1px solid var(--border-subtle)",
        borderRadius:"var(--radius-full)",
        fontFamily:"var(--font-mono)", fontSize:11,
      }}>
        <span style={{
          width:6, height:6, borderRadius:"50%",
          background: live ? "var(--bull)" : "var(--bear)",
          boxShadow: live ? "0 0 6px var(--bull-glow)" : "0 0 6px var(--bear-glow)",
          animation: live ? "pulse-dot 1s ease-in-out infinite" : "none",
        }}></span>
        <span style={{ color: live ? "var(--bull)" : "var(--bear)" }}>
          {deltaMs == null ? "WAIT" : deltaMs < 1000 ? `${deltaMs}ms` : `${(deltaMs/1000).toFixed(1)}s`}
        </span>
      </div>
      <div className="search" onClick={onOpenPalette} style={{ cursor:"pointer" }}>
        <Icon.Search size={13}/>
        <span>Rechercher cryptos, pages, actions…</span>
        <kbd>⌘K</kbd>
      </div>
      <div className="topbar-right">
        <button className={`mode-pill ${mode==="LIVE"?"live":"paper"}`} onClick={()=>window.fbToggle("paper")}>
          <span className={`live-dot ${mode==="PAPER"?"paper-dot":""}`}></span>
          {mode}
        </button>
        <button className="icon-btn" data-tip="Notifications">
          <Icon.Bell size={15}/>
          <span className="notif-dot"></span>
        </button>
        <button className="icon-btn" data-tip="Refresh" onClick={()=>window.fbRefresh && window.fbRefresh()}><Icon.Refresh size={15}/></button>
        <div className="avatar" style={{ width:28, height:28 }}>FB</div>
      </div>
    </header>
  );
}

function ConnectionBanner() {
  return (
    <div className="incident">
      <Icon.AlertTriangle size={14}/>
      <span><strong>Connexion perdue.</strong> Tentative de reconnexion…</span>
    </div>
  );
}

/* ─── Bottom nav (mobile) avec menu Plus ─── */
function BottomNav({ route, onRoute }) {
  const [moreOpen, setMoreOpen] = useStateApp(false);
  const items = [
    { id:"dashboard", i:Icon.Home,    l:"Home" },
    { id:"signaux",   i:Icon.Zap,     l:"Signaux" },
    { id:"trading",   i:Icon.TrendUp, l:"Trading" },
    { id:"charts",    i:Icon.Chart,   l:"Charts" },
    { id:"__more",    i:Icon.Menu,    l:"Plus" },
  ];
  const extraPages = [
    { id:"volatile", i:Icon.Flame,    l:"Volatile" },
    { id:"journal",  i:Icon.Book,     l:"Journal" },
    { id:"stats",    i:Icon.Stats,    l:"Stats" },
    { id:"settings", i:Icon.Settings, l:"Settings" },
  ];
  const isExtraActive = ["volatile","journal","stats","settings"].includes(route);
  return (
    <>
      <nav className="bottom-nav">
        <div className="bottom-nav-inner">
          {items.map(it => {
            const active = it.id === "__more" ? (isExtraActive || moreOpen) : (route === it.id);
            return (
              <div key={it.id}
                   className={`bn-item ${active?"active":""}`}
                   onClick={()=>{
                     if (it.id === "__more") setMoreOpen(o => !o);
                     else { onRoute(it.id); setMoreOpen(false); }
                   }}>
                <it.i size={18} className="icon"/>
                <span>{it.l}</span>
              </div>
            );
          })}
        </div>
      </nav>
      {moreOpen && (
        <div onClick={()=>setMoreOpen(false)} style={{
          position:"fixed", inset:0, zIndex:50,
          background:"rgba(8,8,15,0.6)", backdropFilter:"blur(8px)",
          display:"flex", alignItems:"flex-end",
        }}>
          <div onClick={e=>e.stopPropagation()} style={{
            width:"100%",
            background:"var(--bg-elevated)",
            borderTopLeftRadius:"var(--radius-xl)", borderTopRightRadius:"var(--radius-xl)",
            border:"1px solid var(--border-base)",
            paddingBottom: "calc(env(safe-area-inset-bottom) + 16px)",
            animation: "sheet-up 240ms cubic-bezier(0.2,0.8,0.2,1)",
          }}>
            <div style={{ display:"flex", justifyContent:"center", padding:"8px 0" }}>
              <div style={{ width:36, height:4, borderRadius:2, background:"var(--border-strong)" }}></div>
            </div>
            <div style={{ padding:"4px 12px 16px" }}>
              <div className="caption" style={{ padding:"8px 12px" }}>NAVIGATION</div>
              {extraPages.map(p => {
                const active = route === p.id;
                return (
                  <div key={p.id}
                       onClick={()=>{ onRoute(p.id); setMoreOpen(false); }}
                       style={{
                         display:"flex", alignItems:"center", gap:14,
                         padding:"14px 16px",
                         borderRadius:"var(--radius-md)",
                         background: active ? "var(--accent-soft)" : "transparent",
                         color: active ? "var(--accent-400)" : "var(--text-primary)",
                         fontWeight:500, fontSize:15,
                         cursor:"pointer",
                       }}>
                    <p.i size={20}/>
                    <span style={{ flex:1 }}>{p.l}</span>
                    <Icon.ChevronRight size={14} className="muted"/>
                  </div>
                );
              })}
            </div>
          </div>
        </div>
      )}
    </>
  );
}

// Animation slide-up — injecté une fois
if (typeof document !== "undefined" && !document.getElementById("sheet-up-kf")) {
  const s = document.createElement("style");
  s.id = "sheet-up-kf";
  s.textContent = "@keyframes sheet-up { from { transform: translateY(100%); } to { transform: translateY(0); } }";
  document.head.appendChild(s);
}

/* ─── Stats + Settings (placeholders simples — vraies fonctionnalités via Dashboard/Journal) ─── */
function StatsScreen() {
  const store = window.useStore ? window.useStore() : {};
  const s = store.DBSTATS || {};
  return (
    <div className="page">
      <div className="page-header"><div><h2>Stats</h2><div className="sub mono">Statistiques SQLite · cumul historique</div></div></div>
      <div className="kpi-grid">
        <div className="card pad kpi-card"><div className="kpi-label"><span className="caption">SIGNAUX 24H</span></div><div className="kpi-main"><span className="mono primary" style={{ fontSize:30, fontWeight:600 }}>{s.signals_24h ?? "—"}</span></div></div>
        <div className="card pad kpi-card"><div className="kpi-label"><span className="caption">TRADES 7J</span></div><div className="kpi-main"><span className="mono primary" style={{ fontSize:30, fontWeight:600 }}>{s.trades_7d ?? "—"}</span></div></div>
        <div className="card pad kpi-card"><div className="kpi-label"><span className="caption">WIN RATE</span></div><div className="kpi-main"><span className="mono primary" style={{ fontSize:30, fontWeight:600 }}>{s.winrate_pct != null ? s.winrate_pct.toFixed(1) + "%" : "—"}</span></div></div>
        <div className="card pad kpi-card"><div className="kpi-label"><span className="caption">PnL TOTAL</span></div><div className="kpi-main"><span className="mono" style={{ fontSize:30, fontWeight:600, color: (s.pnl_total||0)>=0?"var(--bull)":"var(--bear)" }}>{s.pnl_total != null ? (s.pnl_total>=0?"+":"")+s.pnl_total.toFixed(2)+"€" : "—"}</span></div></div>
      </div>
    </div>
  );
}

function SettingsScreen() {
  const store    = window.useStore ? window.useStore() : {};
  const st       = store.STATUS || {};
  const scanMode = st.scan_mode || "NORMAL";
  const [minConf, setMinConf]   = useStateApp(st.auto_min_confidence ?? 8);
  const [interval, setInterval_]= useStateApp(st.scan_interval_s ?? 120);
  const [capital, setCapital]   = useStateApp(st.capital ?? 200);
  useEffectApp(() => { if (st.auto_min_confidence != null) setMinConf(st.auto_min_confidence); }, [st.auto_min_confidence]);
  useEffectApp(() => { if (st.scan_interval_s != null) setInterval_(st.scan_interval_s); }, [st.scan_interval_s]);
  useEffectApp(() => { if (st.capital != null) setCapital(st.capital); }, [st.capital]);

  const Section = ({ title, danger, children }) => (
    <div className="card">
      <div className="card-header">
        <h3 style={{ textTransform:"none", fontSize:13, color: danger ? "var(--bear)" : "var(--text-primary)", letterSpacing:0, margin:0 }}>{title}</h3>
      </div>
      <div className="card-body">{children}</div>
    </div>
  );
  const Row = ({ label, hint, children }) => (
    <div style={{ display:"flex", alignItems:"center", justifyContent:"space-between", gap:16, padding:"10px 0", borderBottom:"1px solid var(--border-subtle)" }}>
      <div style={{ display:"flex", flexDirection:"column", gap:2, minWidth:0 }}>
        <span style={{ fontSize:13, color:"var(--text-primary)", fontWeight:500 }}>{label}</span>
        {hint && <span className="muted" style={{ fontSize:11 }}>{hint}</span>}
      </div>
      <div style={{ display:"flex", alignItems:"center", gap:8, flexShrink:0 }}>{children}</div>
    </div>
  );

  return (
    <div className="page">
      <div className="page-header">
        <div><h2>Settings</h2><div className="sub mono">Configuration FoxBot Pro · {Object.keys(st).length ? "synchronisé" : "chargement..."}</div></div>
      </div>

      <Section title="🎯 Scanner & signaux">
        <Row label="Mode scanner" hint="SAFE = R/R≥3 · NORMAL = R/R≥2 · AVANCÉ = R/R≥4">
          {["SAFE","NORMAL","AVANCE"].map(m => (
            <button key={m} className={`btn sm ${scanMode===m?"primary":"ghost"}`} onClick={()=>window.fbSetScanMode(m)}>{m}</button>
          ))}
        </Row>
        <Row label="Score minimum auto-trade" hint={`Seuil ≥ ${minConf}/10 pour ouvrir une position en auto`}>
          <input type="range" min="5" max="10" step="1" value={minConf} onChange={e => setMinConf(parseInt(e.target.value))}
            onMouseUp={()=>fetch(`/api/set/auto_confidence/${minConf}`, { method:"POST", credentials:"include" }).then(()=>window.fbRefresh && window.fbRefresh())}
            style={{ width:140 }}/>
          <span className="mono primary" style={{ minWidth:30, textAlign:"right" }}>{minConf}</span>
        </Row>
        <Row label="Intervalle de scan" hint={`Le bot scanne toutes les ${interval}s (60-1800)`}>
          <input type="range" min="60" max="1800" step="60" value={interval} onChange={e => setInterval_(parseInt(e.target.value))}
            onMouseUp={()=>fetch(`/api/set/scan_interval/${interval}`, { method:"POST", credentials:"include" }).then(()=>window.fbRefresh && window.fbRefresh())}
            style={{ width:140 }}/>
          <span className="mono primary" style={{ minWidth:60, textAlign:"right" }}>{interval}s</span>
        </Row>
      </Section>

      <Section title="⚡ Levier & capital">
        <Row label="Levier par défaut" hint="x5 prudent · x10 max MyOKX Europe · Auto adapté à la volatilité">
          {[5, 10, 0].map(v => (
            <button key={v} className={`btn sm ${st.levier===v||(v===0&&!st.levier)?"primary":"ghost"}`} onClick={()=>window.fbSetLevier(v)}>
              {v===0 ? "Auto" : `x${v}`}
            </button>
          ))}
        </Row>
        <Row label="Capital paper" hint="Capital simulé pour les trades PAPER">
          <input type="number" min="10" max="100000" step="10" value={capital} onChange={e => setCapital(parseInt(e.target.value) || 0)}
            style={{ background:"var(--bg-base)", color:"var(--text-primary)", border:"1px solid var(--border-base)", borderRadius:6, padding:"6px 10px", fontSize:13, width:100, fontFamily:"var(--font-mono)" }}/>
          <button className="btn sm primary" onClick={()=>window.fbSetCapital(capital).then(()=>emitToast({ type:"success", msg:`Capital = ${capital}€` }))}>OK</button>
        </Row>
        <Row label="USDC réel disponible" hint="Synchronisé depuis OKX (read-only)">
          <span className="mono primary">{(st.usdc || 0).toFixed(2)} $</span>
        </Row>
      </Section>

      <Section title="🤖 Modes d'exécution">
        <Row label="Paper trading" hint="Trades simulés sans risque, équivalents au réel">
          <ToggleMini value={!!st.paper_trade} onClick={()=>window.fbToggle("paper")}/>
        </Row>
        <Row label="Auto-trade" hint={`Bot ouvre tout seul les signaux ≥ ${minConf}/10`}>
          <ToggleMini value={!!st.auto_trade} onClick={()=>window.fbToggle("auto")}/>
        </Row>
      </Section>

      <Section title="📊 Statut système">
        <Row label="Trades aujourd'hui" hint={`${st.trades_today || 0} / ${st.max_trades || 5} max quotidien`}>
          <span className="mono primary">{st.trades_today || 0}/{st.max_trades || 5}</span>
        </Row>
        <Row label="Pertes consécutives" hint="Pause auto activée à partir de 2 pertes consécutives">
          <span className={`mono ${(st.consecutive_losses||0) >= 2 ? "bear" : "primary"}`}>{st.consecutive_losses || 0}</span>
        </Row>
        <Row label="P&L journalier" hint="Daily PnL agrégé (paper + réel)">
          <span className={`mono ${(st.daily_pnl||0) >= 0 ? "bull" : "bear"}`}>{(st.daily_pnl||0) >= 0 ? "+" : ""}{(st.daily_pnl||0).toFixed(2)}€</span>
        </Row>
      </Section>

      <Section title="🛑 Arrêt d'urgence" danger>
        <button className="btn" style={{ background:"var(--bear)", color:"white", border:"1px solid var(--bear)", width:"100%" }}
          onClick={()=>{if(confirm("Stopper auto + fermer toutes positions réelles ?")) window.fbEmergencyStop().then(()=>emitToast({type:"warn",msg:"Emergency stop déclenché"}));}}>
          🛑 Stop auto + fermer tous les réels
        </button>
      </Section>

      <Section title="🔗 Connexion OKX (votre compte)">
        <OkxCredsForm/>
      </Section>

      <Section title="🔔 Notifications Telegram">
        <TelegramLinkForm/>
      </Section>

      <Section title="🆔 Connexion biométrique (Face ID / Touch ID)">
        <WebauthnSection/>
      </Section>

      <Section title="ℹ️ Statut système">
        <Row label="WebSocket OKX" hint="Connexion publique du bot, indépendante de tes clés">
          <span style={{ display:"flex", alignItems:"center", gap:6, fontSize:12 }}>
            <span style={{ width:6, height:6, borderRadius:"50%", background:store.CONN_LOST?"var(--bear)":"var(--bull)", boxShadow: store.CONN_LOST?"none":"0 0 6px var(--bull-glow)", animation: store.CONN_LOST?"none":"pulse-dot 2s ease-in-out infinite" }}></span>
            <span className={store.CONN_LOST?"bear":"bull"}>{store.CONN_LOST ? "Reconnexion..." : "Live"}</span>
          </span>
        </Row>
      </Section>
    </div>
  );
}

/* ─── WebAuthn UI section (Phase 4) ─── */
function WebauthnSection() {
  const [creds, setCreds] = useStateApp([]);
  const [busy, setBusy]   = useStateApp(false);

  async function load() {
    try {
      const j = await fetch("/api/webauthn/credentials", { credentials: "include" }).then(r => r.json());
      setCreds(j.credentials || []);
    } catch {}
  }
  useEffectApp(() => { load(); }, []);

  const isHttps = window.location.protocol === "https:" || window.location.hostname === "localhost";

  async function enroll() {
    if (!isHttps) {
      emitToast({ type:"warn", msg:"Face ID nécessite HTTPS — pas activable sur connexion HTTP" });
      return;
    }
    setBusy(true);
    try {
      const nickname = prompt("Nom de cet appareil ?", "iPhone Face ID") || "Device";
      const j = await window.fbWebauthnRegister(nickname);
      if (j.ok) { emitToast({ type:"success", msg:"Biométrie enregistrée" }); load(); }
      else emitToast({ type:"error", msg: j.msg || "Erreur" });
    } catch (e) { emitToast({ type:"error", msg: e.message }); }
    setBusy(false);
  }
  async function remove(id) {
    if (!confirm("Supprimer cet enregistrement ?")) return;
    await fetch(`/api/webauthn/credentials/${id}`, { method:"DELETE", credentials:"include" });
    load();
  }

  return (
    <div style={{ display:"flex", flexDirection:"column", gap:10 }}>
      {!isHttps && (
        <div style={{ padding:"8px 12px", background:"rgba(245,158,11,0.10)", border:"1px solid rgba(245,158,11,0.3)", borderRadius:8, fontSize:11, color:"var(--warn)" }}>
          ⚠️ Face ID / Touch ID nécessite HTTPS. Activez-le après avoir configuré un domaine + Let's Encrypt sur votre VPS.
        </div>
      )}
      {!isWebauthnSupported() && (
        <div style={{ padding:"8px 12px", background:"rgba(239,68,68,0.10)", border:"1px solid rgba(239,68,68,0.3)", borderRadius:8, fontSize:11, color:"var(--bear)" }}>
          ❌ Votre navigateur ne supporte pas WebAuthn.
        </div>
      )}

      {creds.length === 0 ? (
        <p className="muted" style={{ fontSize:12, margin:0 }}>Aucun appareil biométrique enregistré.</p>
      ) : (
        <div style={{ display:"flex", flexDirection:"column", gap:6 }}>
          {creds.map(c => (
            <div key={c.id} style={{ display:"flex", alignItems:"center", justifyContent:"space-between", padding:"8px 12px", background:"var(--bg-base)", borderRadius:8 }}>
              <div>
                <div style={{ fontSize:13, color:"var(--text-primary)", fontWeight:500 }}>{c.nickname || "Device"}</div>
                <div className="muted" style={{ fontSize:10 }}>
                  Enregistré le {c.created_at ? new Date(c.created_at).toLocaleDateString("fr-FR") : "—"}
                  {c.last_used_at ? ` · utilisé ${new Date(c.last_used_at).toLocaleDateString("fr-FR")}` : ""}
                </div>
              </div>
              <button className="icon-btn" onClick={()=>remove(c.id)}><Icon.X size={14}/></button>
            </div>
          ))}
        </div>
      )}

      <button className="btn primary" onClick={enroll} disabled={busy || !isWebauthnSupported()}
        style={{ alignSelf:"flex-start" }}>
        {busy ? "..." : "🆔 Activer Face ID / Touch ID"}
      </button>
    </div>
  );
}

/* ─── WebAuthn / Face ID helpers (Phase 4) ─── */
// WebAuthn nécessite HTTPS (sauf localhost). Le client le détecte via window.PublicKeyCredential.
function isWebauthnSupported() {
  return typeof window !== "undefined" && !!window.PublicKeyCredential;
}
function b64urlDecode(s) {
  s = s.replace(/-/g, "+").replace(/_/g, "/");
  while (s.length % 4) s += "=";
  return Uint8Array.from(atob(s), c => c.charCodeAt(0));
}
function b64urlEncode(buf) {
  let s = "";
  const bytes = new Uint8Array(buf);
  for (let i = 0; i < bytes.byteLength; i++) s += String.fromCharCode(bytes[i]);
  return btoa(s).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
}

async function webauthnRegister(nickname) {
  if (!isWebauthnSupported()) throw new Error("WebAuthn non supporté par ce navigateur");
  const optsRaw = await fetch("/api/webauthn/register/begin", { method: "POST", credentials: "include" });
  const opts = await optsRaw.json();
  // Convertir challenge + user.id + excludeCredentials.id en ArrayBuffer
  opts.challenge = b64urlDecode(opts.challenge);
  opts.user.id   = b64urlDecode(opts.user.id);
  if (opts.excludeCredentials) {
    opts.excludeCredentials = opts.excludeCredentials.map(c => ({ ...c, id: b64urlDecode(c.id) }));
  }
  const cred = await navigator.credentials.create({ publicKey: opts });
  const att  = cred.response;
  const credJson = {
    id: cred.id,
    rawId: b64urlEncode(cred.rawId),
    type: cred.type,
    response: {
      clientDataJSON: b64urlEncode(att.clientDataJSON),
      attestationObject: b64urlEncode(att.attestationObject),
    },
  };
  const r = await fetch("/api/webauthn/register/complete", {
    method: "POST", credentials: "include",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      expectedChallenge: b64urlEncode(opts.challenge),
      credential: credJson, nickname,
    }),
  });
  return r.json();
}

async function webauthnLogin(email) {
  if (!isWebauthnSupported()) throw new Error("WebAuthn non supporté par ce navigateur");
  const optsRaw = await fetch("/api/webauthn/login/begin", {
    method: "POST", credentials: "include",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ email: email || "" }),
  });
  const opts = await optsRaw.json();
  opts.challenge = b64urlDecode(opts.challenge);
  if (opts.allowCredentials) {
    opts.allowCredentials = opts.allowCredentials.map(c => ({ ...c, id: b64urlDecode(c.id) }));
  }
  const cred = await navigator.credentials.get({ publicKey: opts });
  const a    = cred.response;
  const credJson = {
    id: cred.id,
    rawId: b64urlEncode(cred.rawId),
    type: cred.type,
    response: {
      clientDataJSON: b64urlEncode(a.clientDataJSON),
      authenticatorData: b64urlEncode(a.authenticatorData),
      signature: b64urlEncode(a.signature),
      userHandle: a.userHandle ? b64urlEncode(a.userHandle) : null,
    },
  };
  const r = await fetch("/api/webauthn/login/complete", {
    method: "POST", credentials: "include",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      expectedChallenge: b64urlEncode(opts.challenge),
      credential: credJson,
    }),
  });
  return r.json();
}

window.fbWebauthnRegister = webauthnRegister;
window.fbWebauthnLogin    = webauthnLogin;

/* ─── Telegram link form (Phase 3) ─── */
function TelegramLinkForm() {
  const [status, setStatus] = useStateApp(null);
  const [code, setCode]     = useStateApp(null);
  const [countdown, setCD]  = useStateApp(0);
  const [loading, setLoad]  = useStateApp(false);

  async function loadStatus() {
    try { setStatus(await fetch("/api/telegram/link/status", { credentials: "include" }).then(r=>r.json())); }
    catch {}
  }
  useEffectApp(() => { loadStatus(); }, []);

  // Countdown du code + polling status pendant la durée de vie
  useEffectApp(() => {
    if (!code) return;
    setCD(600);
    const t = setInterval(() => setCD(c => Math.max(0, c-1)), 1000);
    const poll = setInterval(loadStatus, 3000);
    return () => { clearInterval(t); clearInterval(poll); };
  }, [code]);
  useEffectApp(() => {
    if (status?.linked && code) { setCode(null); emitToast({ type:"success", msg:"Telegram lié !" }); }
  }, [status?.linked]);
  useEffectApp(() => { if (countdown === 0 && code) setCode(null); }, [countdown, code]);

  async function generate() {
    setLoad(true);
    try {
      const j = await fetch("/api/telegram/link/generate", { method:"POST", credentials:"include" }).then(r=>r.json());
      if (j.ok) setCode({ value: j.code, bot: j.bot_username });
      else emitToast({ type:"error", msg: j.msg || "Erreur" });
    } catch (e) { emitToast({ type:"error", msg:e.message }); }
    setLoad(false);
  }
  async function unlink() {
    if (!confirm("Délier Telegram ?")) return;
    await fetch("/api/telegram/link", { method:"DELETE", credentials:"include" });
    emitToast({ type:"info", msg:"Telegram délié" });
    loadStatus();
  }
  async function testNotif() {
    const j = await fetch("/api/telegram/test", { method:"POST", credentials:"include" }).then(r=>r.json());
    emitToast({ type: j.ok ? "success" : "error", msg: j.ok ? "Test envoyé sur Telegram" : (j.msg || "Erreur") });
  }

  if (status?.linked) {
    return (
      <div style={{ display:"flex", flexDirection:"column", gap:12 }}>
        <div style={{ display:"flex", alignItems:"center", justifyContent:"space-between" }}>
          <div style={{ display:"flex", alignItems:"center", gap:8 }}>
            <span style={{ width:8, height:8, borderRadius:"50%", background:"var(--bull)", boxShadow:"0 0 8px var(--bull-glow)" }}></span>
            <span style={{ color:"var(--text-primary)", fontSize:13, fontWeight:500 }}>
              Lié{status.username ? ` à @${status.username}` : ""}
            </span>
          </div>
          <span className="muted" style={{ fontSize:11 }}>
            {status.notifications_enabled ? "Notifs activées" : "Notifs en pause"}
          </span>
        </div>
        <div style={{ display:"flex", gap:8 }}>
          <button className="btn ghost" onClick={testNotif}>📨 Envoyer un test</button>
          <button className="btn ghost" onClick={unlink}>🔗 Délier</button>
        </div>
      </div>
    );
  }

  if (code) {
    const mm = String(Math.floor(countdown / 60)).padStart(2, "0");
    const ss = String(countdown % 60).padStart(2, "0");
    return (
      <div style={{ display:"flex", flexDirection:"column", gap:12, alignItems:"center", padding:16,
                    border:"1px dashed var(--accent-500)", borderRadius:12, background:"var(--accent-soft)" }}>
        <div className="caption">CODE DE LIAISON · EXPIRE DANS {mm}:{ss}</div>
        <div className="mono" style={{ fontSize:36, fontWeight:600, color:"var(--accent-400)", letterSpacing:6 }}>
          {code.value}
        </div>
        <ol style={{ margin:0, paddingLeft:20, fontSize:12, color:"var(--text-muted)", lineHeight:1.7 }}>
          <li>Ouvre Telegram → cherche <b className="mono">@{code.bot}</b></li>
          <li>Envoie : <code style={{ background:"var(--bg-base)", padding:"2px 6px", borderRadius:4 }}>/link {code.value}</code></li>
          <li>Reviens ici, c'est automatique</li>
        </ol>
        <button className="btn ghost sm" onClick={() => setCode(null)}>Annuler</button>
      </div>
    );
  }

  return (
    <div style={{ display:"flex", flexDirection:"column", gap:10 }}>
      <p className="muted" style={{ fontSize:12, margin:0, lineHeight:1.5 }}>
        Reçois tes notifications de trading (signaux, trades, TP/SL atteints) directement sur Telegram.
      </p>
      <button className="btn primary" onClick={generate} disabled={loading} style={{ alignSelf:"flex-start" }}>
        {loading ? "..." : "🔔 Lier mon Telegram"}
      </button>
    </div>
  );
}

/* ─── OKX credentials form (Phase 2) ─── */
function OkxCredsForm() {
  const [status, setStatus]   = useStateApp(null);
  const [apiKey, setApiKey]   = useStateApp("");
  const [secret, setSecret]   = useStateApp("");
  const [pass, setPass]       = useStateApp("");
  const [isDemo, setIsDemo]   = useStateApp(false);
  const [show, setShow]       = useStateApp({ k: false, s: false, p: false });
  const [testing, setTesting] = useStateApp(false);
  const [saving, setSaving]   = useStateApp(false);
  const [testRes, setTestRes] = useStateApp(null);

  async function loadStatus() {
    try {
      const j = await fetch("/api/okx/credentials/status", { credentials: "include" }).then(r => r.json());
      setStatus(j);
    } catch {}
  }
  useEffectApp(() => { loadStatus(); }, []);

  async function doTest() {
    setTesting(true); setTestRes(null);
    try {
      const j = await fetch("/api/okx/test-connection", {
        method: "POST", credentials: "include",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ api_key: apiKey, secret, passphrase: pass, is_demo: isDemo }),
      }).then(r => r.json());
      setTestRes(j);
    } catch (e) { setTestRes({ ok: false, msg: e.message }); }
    setTesting(false);
  }
  async function doSave() {
    setSaving(true);
    try {
      const j = await fetch("/api/okx/credentials", {
        method: "POST", credentials: "include",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ api_key: apiKey, secret, passphrase: pass, is_demo: isDemo }),
      }).then(r => r.json());
      if (j.ok) {
        emitToast({ type: "success", msg: "OKX connecté", sub: j.usdc != null ? `USDC: ${j.usdc.toFixed(2)}$` : "" });
        setApiKey(""); setSecret(""); setPass("");
        await loadStatus();
      } else {
        emitToast({ type: "error", msg: j.msg || "Erreur" });
      }
    } catch (e) { emitToast({ type: "error", msg: e.message }); }
    setSaving(false);
  }
  async function doDelete() {
    if (!confirm("Supprimer tes clés OKX ?")) return;
    try {
      await fetch("/api/okx/credentials", { method: "DELETE", credentials: "include" });
      emitToast({ type: "info", msg: "Clés OKX supprimées" });
      await loadStatus();
    } catch (e) { emitToast({ type: "error", msg: e.message }); }
  }

  if (status?.connected) {
    return (
      <div style={{ display:"flex", flexDirection:"column", gap:12 }}>
        <div style={{ display:"flex", alignItems:"center", justifyContent:"space-between", padding:"10px 0" }}>
          <div style={{ display:"flex", alignItems:"center", gap:8 }}>
            <span style={{ width:8, height:8, borderRadius:"50%", background:status.is_valid?"var(--bull)":"var(--warn)", boxShadow:status.is_valid?"0 0 8px var(--bull-glow)":"" }}></span>
            <span style={{ color:"var(--text-primary)", fontSize:13, fontWeight:500 }}>
              Connecté · {status.is_demo ? "DEMO" : "LIVE"} {!status.is_valid && "(à re-tester)"}
            </span>
          </div>
          <span className="mono muted" style={{ fontSize:11 }}>{status.api_key_masked}</span>
        </div>
        <button className="btn ghost" onClick={doDelete} style={{ alignSelf:"flex-start" }}>
          <Icon.X size={13}/> Déconnecter OKX
        </button>
      </div>
    );
  }
  return (
    <div style={{ display:"flex", flexDirection:"column", gap:12 }}>
      <p className="muted" style={{ fontSize:12, margin:0, lineHeight:1.5 }}>
        Crée une API Key OKX avec permissions <b style={{ color:"var(--bull)" }}>Read + Trade</b> uniquement
        (PAS Withdraw). Configure une whitelist IP côté OKX pour sécurité maximale.
      </p>
      {[
        { key:"k", label:"API Key",       v: apiKey, set: setApiKey, type:"text" },
        { key:"s", label:"Secret Key",    v: secret, set: setSecret, type:"password" },
        { key:"p", label:"Passphrase",    v: pass,   set: setPass,   type:"password" },
      ].map(f => (
        <label key={f.key} style={{ display:"flex", flexDirection:"column", gap:4 }}>
          <span className="caption">{f.label}</span>
          <div style={{ position:"relative" }}>
            <input type={show[f.key] ? "text" : "password"} value={f.v} onChange={e=>f.set(e.target.value)}
              autoComplete="off"
              style={{ ...inputStyle, width:"100%", paddingRight:36, fontFamily:"var(--font-mono)" }}/>
            <button onClick={()=>setShow({...show,[f.key]:!show[f.key]})}
              style={{ position:"absolute", right:8, top:"50%", transform:"translateY(-50%)", background:"transparent", border:"none", color:"var(--text-muted)", cursor:"pointer" }}>
              <Icon.Eye size={14}/>
            </button>
          </div>
        </label>
      ))}
      <label style={{ display:"flex", alignItems:"center", gap:8, fontSize:13, color:"var(--text-base)" }}>
        <input type="checkbox" checked={isDemo} onChange={e=>setIsDemo(e.target.checked)}/>
        Mode DEMO (paper trading OKX, pas de vrai trading)
      </label>
      {testRes && (
        <div style={{ padding:"8px 12px", borderRadius:8,
          background: testRes.ok ? "rgba(16,185,129,0.10)" : "rgba(239,68,68,0.10)",
          border: testRes.ok ? "1px solid rgba(16,185,129,0.3)" : "1px solid rgba(239,68,68,0.3)",
          color: testRes.ok ? "var(--bull)" : "var(--bear)", fontSize:12 }}>
          {testRes.ok ? `✓ Connexion validée${testRes.usdc != null ? ` · USDC : ${testRes.usdc.toFixed(2)}$` : ""}` : `✗ ${testRes.msg}`}
        </div>
      )}
      <div style={{ display:"flex", gap:8 }}>
        <button className="btn ghost" onClick={doTest} disabled={testing || !apiKey || !secret || !pass}>
          {testing ? "Test..." : "Tester la connexion"}
        </button>
        <button className="btn primary" onClick={doSave} disabled={saving || !apiKey || !secret || !pass}>
          {saving ? "..." : "Sauvegarder"}
        </button>
      </div>
    </div>
  );
}

/* ─── Command Palette ⌘K ─────────────────────────────────────────────────── */
function CommandPalette({ onClose, onRoute }) {
  const [q, setQ]      = useStateApp("");
  const [idx, setIdx]  = useStateApp(0);
  const inputRef       = React.useRef(null);
  const store          = window.useStore ? window.useStore() : {};

  useEffectApp(() => { inputRef.current && inputRef.current.focus(); }, []);

  // Build searchable items
  const items = React.useMemo(() => {
    const pages = ["dashboard","signaux","volatile","trading","charts","journal","stats","settings"].map(p => ({
      kind:"page", label: p.charAt(0).toUpperCase()+p.slice(1), sub:"Page · navigation",
      action:()=>{ onRoute(p); onClose(); }
    }));
    const cryptos = ["BTC","ETH","SOL","XAU","DOGE","XRP","ADA","SUI","LTC"].map(s => ({
      kind:"crypto", label:`${s}/USDT`, sub:`Chart · ${s}`,
      action:()=>{ onRoute("charts"); onClose(); }
    }));
    const actions = [
      { kind:"action", label:"Lancer un scan", sub:"Force un scan immédiat", action:()=>{ window.fbScan().then(()=>emitToast({ type:"success", msg:"Scan terminé" })); onClose(); } },
      { kind:"action", label:"Refresh complet", sub:"Recharge toutes les données", action:()=>{ window.fbRefresh(); onClose(); } },
      { kind:"action", label:"Toggle Paper trading", sub:"Bascule paper / live", action:()=>{ window.fbToggle("paper"); onClose(); } },
      { kind:"action", label:"Toggle Auto-trade", sub:"Active/désactive l'auto", action:()=>{ window.fbToggle("auto"); onClose(); } },
      { kind:"action", label:"🛑 Emergency Stop", sub:"Stop auto + ferme tous les réels", action:()=>{ if(confirm("Confirmer ?")) window.fbEmergencyStop(); onClose(); } },
    ];
    return [...pages, ...cryptos, ...actions];
  }, []);

  const filtered = React.useMemo(() => {
    if (!q) return items;
    const lower = q.toLowerCase();
    return items.filter(it => it.label.toLowerCase().includes(lower) || it.sub.toLowerCase().includes(lower));
  }, [q, items]);

  React.useEffect(() => { setIdx(0); }, [q]);

  function onKey(e) {
    if (e.key === "Escape") { onClose(); }
    else if (e.key === "ArrowDown") { setIdx(i => Math.min(i+1, filtered.length-1)); e.preventDefault(); }
    else if (e.key === "ArrowUp")   { setIdx(i => Math.max(i-1, 0)); e.preventDefault(); }
    else if (e.key === "Enter")     { filtered[idx] && filtered[idx].action(); e.preventDefault(); }
  }

  const grouped = filtered.reduce((acc, it) => { (acc[it.kind] = acc[it.kind] || []).push(it); return acc; }, {});
  const groupOrder = ["page","crypto","action"];
  const groupLabels = { page:"PAGES", crypto:"CRYPTOS", action:"ACTIONS" };
  let runningIdx = -1;

  return (
    <div onClick={onClose} style={{
      position:"fixed", inset:0, background:"rgba(8,8,15,0.7)", backdropFilter:"blur(8px)",
      zIndex:100, display:"flex", justifyContent:"center", alignItems:"flex-start", paddingTop:"15vh",
    }}>
      <div onClick={e => e.stopPropagation()} onKeyDown={onKey} style={{
        background:"var(--bg-elevated)", border:"1px solid var(--border-base)",
        borderRadius:"var(--radius-lg)", width:"min(540px, 92vw)", overflow:"hidden",
        boxShadow:"var(--shadow-lg)",
      }}>
        <div style={{ display:"flex", alignItems:"center", gap:10, padding:"14px 18px", borderBottom:"1px solid var(--border-subtle)" }}>
          <Icon.Search size={16} className="muted"/>
          <input ref={inputRef} value={q} onChange={e=>setQ(e.target.value)}
            placeholder="Rechercher cryptos, pages, actions…"
            style={{ flex:1, background:"transparent", border:"none", outline:"none", color:"var(--text-primary)", fontSize:14, fontFamily:"var(--font-sans)" }}/>
          <kbd style={{ background:"var(--bg-base)", border:"1px solid var(--border-base)", borderRadius:4, padding:"2px 6px", fontSize:10, color:"var(--text-muted)", fontFamily:"var(--font-mono)" }}>ESC</kbd>
        </div>
        <div style={{ maxHeight:"50vh", overflowY:"auto", padding:"6px 0" }}>
          {filtered.length === 0 ? (
            <div style={{ padding:"24px", textAlign:"center", color:"var(--text-muted)", fontSize:13 }}>Aucun résultat</div>
          ) : groupOrder.map(g => grouped[g] && (
            <div key={g}>
              <div style={{ padding:"8px 18px 4px", fontSize:10, letterSpacing:"0.14em", color:"var(--text-dim)", fontWeight:600 }}>{groupLabels[g]}</div>
              {grouped[g].map(it => {
                runningIdx++;
                const active = runningIdx === idx;
                return (
                  <div key={it.label} onClick={it.action} onMouseEnter={()=>setIdx(runningIdx)} style={{
                    display:"flex", alignItems:"center", justifyContent:"space-between",
                    padding:"8px 18px", cursor:"pointer",
                    background: active ? "var(--accent-soft)" : "transparent",
                    borderLeft: active ? "2px solid var(--accent-500)" : "2px solid transparent",
                  }}>
                    <div style={{ display:"flex", flexDirection:"column", gap:2 }}>
                      <span style={{ color: active ? "var(--accent-400)" : "var(--text-primary)", fontSize:13, fontWeight:500 }}>{it.label}</span>
                      <span style={{ color:"var(--text-muted)", fontSize:11 }}>{it.sub}</span>
                    </div>
                    {active && <Icon.ChevronRight size={13} style={{ color:"var(--accent-400)" }}/>}
                  </div>
                );
              })}
            </div>
          ))}
        </div>
        <div style={{ padding:"8px 18px", borderTop:"1px solid var(--border-subtle)", display:"flex", gap:14, fontSize:10, color:"var(--text-muted)" }}>
          <span><kbd style={{ background:"var(--bg-base)", border:"1px solid var(--border-base)", borderRadius:3, padding:"1px 4px", fontFamily:"var(--font-mono)" }}>↑↓</kbd> naviguer</span>
          <span><kbd style={{ background:"var(--bg-base)", border:"1px solid var(--border-base)", borderRadius:3, padding:"1px 4px", fontFamily:"var(--font-mono)" }}>↵</kbd> valider</span>
          <span><kbd style={{ background:"var(--bg-base)", border:"1px solid var(--border-base)", borderRadius:3, padding:"1px 4px", fontFamily:"var(--font-mono)" }}>esc</kbd> fermer</span>
        </div>
      </div>
    </div>
  );
}

window.StatsScreen = StatsScreen;
window.SettingsScreen = SettingsScreen;

/* ─── AuthScreen : login + signup quand non authentifié ─── */
function AuthScreen() {
  const [mode, setMode]       = useStateApp("login");   // 'login' | 'signup'
  const [email, setEmail]     = useStateApp("");
  const [pwd, setPwd]         = useStateApp("");
  const [name, setName]       = useStateApp("");
  const [loading, setLoading] = useStateApp(false);
  const [err, setErr]         = useStateApp("");

  async function submit(e) {
    e.preventDefault();
    setErr(""); setLoading(true);
    try {
      const fn  = mode === "signup" ? window.fbSignup : window.fbLogin;
      const res = mode === "signup" ? await fn(email, pwd, name) : await fn(email, pwd);
      if (!res.ok) setErr(res.msg || "Erreur");
    } catch (e) { setErr(e.message); }
    setLoading(false);
  }

  const isMobile = window.matchMedia && window.matchMedia("(max-width: 880px)").matches;

  return (
    <div style={{
      minHeight:"100vh", display:"grid", placeItems:"center",
      padding:"24px",
      background: "radial-gradient(60% 40% at 30% 0%, rgba(168,85,247,0.12), transparent 60%), radial-gradient(50% 40% at 100% 100%, rgba(236,72,153,0.10), transparent 60%), var(--bg-base)",
    }}>
      <div style={{
        width:"100%", maxWidth:380,
        background:"var(--bg-elevated)",
        border:"1px solid var(--border-subtle)",
        borderRadius:"var(--radius-lg)",
        padding:isMobile?"24px":"32px",
      }}>
        <div style={{ display:"flex", alignItems:"center", gap:12, marginBottom:24 }}>
          <FoxMark size={48}/>
          <div>
            <h2 style={{ margin:0, fontSize:20, color:"var(--text-primary)" }}>FoxBot Pro</h2>
            <span className="caption">{mode === "signup" ? "CRÉATION DE COMPTE" : "CONNEXION"}</span>
          </div>
        </div>

        <form onSubmit={submit} style={{ display:"flex", flexDirection:"column", gap:14 }}>
          {mode === "signup" && (
            <label style={{ display:"flex", flexDirection:"column", gap:6 }}>
              <span className="caption">Nom (optionnel)</span>
              <input type="text" value={name} onChange={e=>setName(e.target.value)}
                placeholder="Trader Pro"
                style={inputStyle}/>
            </label>
          )}
          <label style={{ display:"flex", flexDirection:"column", gap:6 }}>
            <span className="caption">Email</span>
            <input type="email" required value={email} onChange={e=>setEmail(e.target.value)}
              autoComplete={mode==="signup"?"email":"username"}
              placeholder="vous@email.com" style={inputStyle}/>
          </label>
          <label style={{ display:"flex", flexDirection:"column", gap:6 }}>
            <span className="caption">Mot de passe</span>
            <input type="password" required minLength={8} value={pwd} onChange={e=>setPwd(e.target.value)}
              autoComplete={mode==="signup"?"new-password":"current-password"}
              placeholder="••••••••" style={inputStyle}/>
          </label>
          {err && (
            <div style={{ padding:"8px 12px", background:"rgba(239,68,68,0.12)", border:"1px solid rgba(239,68,68,0.3)", borderRadius:8, color:"var(--bear)", fontSize:12 }}>
              {err}
            </div>
          )}
          <button type="submit" className="btn primary" disabled={loading}
            style={{ height:44, fontSize:14, fontWeight:600, marginTop:6 }}>
            {loading ? "…" : (mode === "signup" ? "Créer le compte" : "Se connecter")}
          </button>
        </form>

        {mode === "login" && isWebauthnSupported() && (
          <button type="button" className="btn ghost"
            onClick={async () => {
              setErr(""); setLoading(true);
              try {
                const j = await window.fbWebauthnLogin(email);
                if (j.ok) {
                  await window.fbAuthRefresh();
                  await window.fbRefresh();
                } else setErr(j.msg || "Échec Face ID");
              } catch (e) { setErr(e.message); }
              setLoading(false);
            }}
            style={{ height:42, marginTop:8, width:"100%", fontSize:13, gap:8 }}>
            🆔 Se connecter avec Face ID / Touch ID
          </button>
        )}

        <div style={{ marginTop:18, textAlign:"center", fontSize:13, color:"var(--text-muted)" }}>
          {mode === "login" ? "Pas de compte ?" : "Déjà inscrit ?"}{" "}
          <a onClick={(e)=>{e.preventDefault(); setMode(mode==="login"?"signup":"login"); setErr("");}}
             style={{ color:"var(--accent-400)", cursor:"pointer", textDecoration:"none", fontWeight:500 }}>
            {mode === "login" ? "Créer un compte" : "Se connecter"}
          </a>
        </div>
      </div>
    </div>
  );
}
const inputStyle = {
  background:"var(--bg-base)", color:"var(--text-primary)",
  border:"1px solid var(--border-base)", borderRadius:8,
  padding:"10px 12px", fontSize:14, outline:"none",
  fontFamily:"var(--font-sans)",
};

/* ─── Root : bascule entre App (desktop) et MobileApp selon largeur écran ─── */
function Root() {
  // Re-render sur changement STORE (sinon les écrans figés)
  const store     = window.useStore ? window.useStore() : window.MOCK;
  const auth      = store.AUTH || { authenticated: false, checking: true };
  const isMobile  = window.useIsMobile ? window.useIsMobile() : false;
  const mode      = store.MODE || "PAPER";

  // Si non authentifié ET pas de KEY URL → écran de login
  if (!auth.authenticated && !window.KEY) {
    if (auth.checking) {
      return <div style={{ display:"grid", placeItems:"center", minHeight:"100vh", color:"var(--text-muted)" }}>Chargement…</div>;
    }
    return <AuthScreen/>;
  }
  // Tweaks compat pour le shell mobile (qui attend tweaks.mode + setTweak)
  const tweaks    = { mode, autoTrade: !!store.STATUS?.auto_trade };
  function setTweak(patch) {
    if (patch.mode !== undefined) window.fbToggle("paper");  // toggle paper côté serveur
  }
  async function handleAction(kind, signal) {
    const isPaper = kind.startsWith("paper");
    const dir     = kind.endsWith("long") ? "LONG" : "SHORT";
    try {
      const res = await window.fbAction(isPaper ? "paper" : "real", dir, signal.fullPair || (signal.pair + "/USDT:USDT"));
      const isReal = !isPaper;
      if (window.HAPTIC) (res.ok ? window.HAPTIC.success : window.HAPTIC.err)();
      emitToast({
        type: res.ok ? (isReal ? "success" : "info") : "error",
        msg: res.ok ? `${isPaper ? "Paper trade" : "Position réelle"} · ${signal.pair} ${dir}` : `Erreur · ${res.msg || "ouverture impossible"}`,
        sub: res.ok ? `Entry ${fmtNum(signal.entry)} · SL ${fmtNum(signal.sl)} · TP ${fmtNum(signal.tp1)}` : null,
      });
    } catch (e) {
      emitToast({ type: "error", msg: `Erreur · ${e.message}` });
    }
  }
  if (isMobile && window.MobileApp) {
    return <ErrorBoundary><window.MobileApp tweaks={tweaks} setTweak={setTweak} onAction={handleAction}/></ErrorBoundary>;
  }
  return <App/>;
}
ReactDOM.createRoot(document.getElementById("root")).render(<Root/>);
