// ============================================================
// DIRECTION 1 — QUANT TERMINAL
// Dense, monospace-heavy, vibe Bloomberg / quant pro
// ============================================================

const QT = {
  bg: "#06080F",
  panel: "#0B0F1C",
  panel2: "#10162A",
  border: "rgba(124, 158, 255, 0.12)",
  borderStrong: "rgba(124, 158, 255, 0.22)",
  text: "#E5EAFF",
  textDim: "#8A93B8",
  textMute: "#5A6388",
  indigo: "#7C9EFF",
  indigoSoft: "rgba(124, 158, 255, 0.12)",
  violet: "#A78BFA",
  green: "#5BD99A",
  red: "#FF6B7A",
  amber: "#FFB454",
  mono: "'JetBrains Mono', ui-monospace, monospace",
  sans: "'Inter', system-ui, sans-serif",
};

const qtSign = (n) => (n > 0 ? "+" : n < 0 ? "-" : "");

// ──────────────────────────────────────────────────────────────
// Paramètres d'affichage — store global partagé par toute l'app
// (thème, densité, format numérique, animations, mode trader).
// Persisté dans localStorage sous la clé lue par l'écran PARAMÈTRES.
// ──────────────────────────────────────────────────────────────
const QT_THEMES = {
  DARK: {
    bg: "#06080F", panel: "#0B0F1C", panel2: "#10162A",
    border: "rgba(124, 158, 255, 0.12)", borderStrong: "rgba(124, 158, 255, 0.22)",
    text: "#E5EAFF", textDim: "#8A93B8", textMute: "#5A6388",
    indigo: "#7C9EFF", indigoSoft: "rgba(124, 158, 255, 0.12)",
    violet: "#A78BFA", green: "#5BD99A", red: "#FF6B7A", amber: "#FFB454",
  },
  "TAMISÉ": {
    bg: "#13141C", panel: "#191B26", panel2: "#20232F",
    border: "rgba(124, 158, 255, 0.14)", borderStrong: "rgba(124, 158, 255, 0.24)",
    text: "#D9DCEF", textDim: "#9A9FC0", textMute: "#6B7092",
    indigo: "#8FACFF", indigoSoft: "rgba(143, 172, 255, 0.13)",
    violet: "#B49BFA", green: "#6FDBA8", red: "#FF8590", amber: "#FFC172",
  },
  TERMINAL: {
    bg: "#000000", panel: "#040604", panel2: "#0A0F0A",
    border: "rgba(91, 217, 154, 0.18)", borderStrong: "rgba(91, 217, 154, 0.32)",
    text: "#C8FFDD", textDim: "#6FAE87", textMute: "#3E6350",
    indigo: "#5BD99A", indigoSoft: "rgba(91, 217, 154, 0.14)",
    violet: "#5BD99A", green: "#5BD99A", red: "#FF6B7A", amber: "#FFD166",
  },
};

const QT_LOCALES = { fr: "fr-FR", us: "en-US", de: "de-DE" };
let qtNumberLocale = "fr-FR";

const qtFmt = (n, d = 2) => Number(n).toLocaleString(qtNumberLocale, { minimumFractionDigits: d, maximumFractionDigits: d });

const QT_DISPLAY_KEY = "nomerus_user_settings_v1";
const QT_DISPLAY_DEFAULTS = { theme: "DARK", density: "NORMAL", number_format: "fr", reduce_motion: false, trader_mode: true };
const QT_DISPLAY_LISTENERS = new Set();

// Applique thème/locale/densité/animations/mode trader — mute QT en place
// (les composants lisent QT.xxx à chaque rendu, donc la mutation se
// répercute dès le prochain rendu déclenché par un des listeners).
const applyQtDisplaySettings = (s) => {
  Object.assign(QT, QT_THEMES[s.theme] || QT_THEMES.DARK);
  qtNumberLocale = QT_LOCALES[s.number_format] || "fr-FR";
  if (typeof document !== "undefined" && document.body) {
    document.body.dataset.qtTheme = s.theme;
    document.body.dataset.qtDensity = s.density;
    document.body.dataset.qtReduceMotion = String(!!s.reduce_motion);
    document.body.dataset.qtTraderMode = String(!!s.trader_mode);
  }
};

const loadQtDisplaySettings = () => {
  let stored = {};
  try {
    const raw = localStorage.getItem(QT_DISPLAY_KEY);
    if (raw) stored = JSON.parse(raw);
  } catch (e) {}
  return { ...QT_DISPLAY_DEFAULTS, ...stored };
};

window.nomerusDisplaySettings = loadQtDisplaySettings();
applyQtDisplaySettings(window.nomerusDisplaySettings);

// Écrit une préférence d'affichage : maj store global + localStorage (en
// ne touchant que cette clé, pour cohabiter avec l'écran PARAMÈTRES qui
// persiste séparément risque/notifs sous la même clé) + notifie les
// composants abonnés via useNomerusDisplaySettings().
window.nomerusSetSetting = (k, v) => {
  window.nomerusDisplaySettings = { ...window.nomerusDisplaySettings, [k]: v };
  try {
    const raw = localStorage.getItem(QT_DISPLAY_KEY);
    const full = raw ? JSON.parse(raw) : {};
    full[k] = v;
    localStorage.setItem(QT_DISPLAY_KEY, JSON.stringify(full));
  } catch (e) {}
  applyQtDisplaySettings(window.nomerusDisplaySettings);
  QT_DISPLAY_LISTENERS.forEach(fn => fn(window.nomerusDisplaySettings));
};

const useNomerusDisplaySettings = () => {
  const [, tick] = React.useState(0);
  React.useEffect(() => {
    const fn = () => tick(t => t + 1);
    QT_DISPLAY_LISTENERS.add(fn);
    return () => QT_DISPLAY_LISTENERS.delete(fn);
  }, []);
  return window.nomerusDisplaySettings;
};
window.useNomerusDisplaySettings = useNomerusDisplaySettings;

// ──────────────────────────────────────────────────────────────
// Sidebar
// ──────────────────────────────────────────────────────────────
const QTSidebar = ({ active, setActive }) => {
  const items = [
    { id: "home",      label: "ACCUEIL",     icon: "dashboard" },
    { id: "journal",   label: "JOURNAL",     icon: "analyze" },
    { id: "analytics", label: "ANALYSE",     icon: "analyze" },
    { id: "coach",     label: "COACH IA",    icon: "ai" },
    { id: "news",      label: "LE BRIEF",    icon: "news" },
    { id: "setup",     label: "CONNEXION",   icon: "bot" },
    { id: "account",   label: "COMPTE",      icon: "account" },
    { id: "settings",  label: "PARAMÈTRES",  icon: "settings" },
  ];
  // L'écran « trade » est un sous-écran de Journal — on garde Journal actif.
  const navActive = active === "trade" ? "journal" : active;
  return (
    <aside style={{
      width: 220, background: QT.panel, borderRight: `1px solid ${QT.border}`,
      display: "flex", flexDirection: "column", flexShrink: 0,
    }}>
      <div style={{ padding: "20px 18px 22px", borderBottom: `1px solid ${QT.border}`, display: "flex", alignItems: "center", gap: 10 }}>
        <div style={{
          width: 30, height: 30, borderRadius: 6, border: `1.5px solid ${QT.indigo}`,
          display: "flex", alignItems: "center", justifyContent: "center",
          color: QT.indigo, fontFamily: QT.sans, fontWeight: 800, fontSize: 16, letterSpacing: -0.5,
        }}>V</div>
        <div>
          <div style={{ fontFamily: QT.sans, fontSize: 14, fontWeight: 700, color: QT.text, letterSpacing: 0.5 }}>NOMERUS</div>
          <div style={{ fontFamily: QT.mono, fontSize: 9, color: QT.textMute, letterSpacing: 1 }}>QUANT TERMINAL</div>
        </div>
      </div>
      <nav style={{ padding: "16px 10px", flex: 1, display: "flex", flexDirection: "column", gap: 2 }}>
        <div style={{ fontFamily: QT.mono, fontSize: 9, color: QT.textMute, letterSpacing: 1.5, padding: "0 10px 8px" }}>// NAVIGATION</div>
        {items.map(it => (
          <button key={it.id} onClick={() => setActive(it.id)}
            className={navActive === it.id ? "" : "qt-nav-btn"}
            style={{
              display: "flex", alignItems: "center", gap: 10, padding: "10px 12px",
              background: navActive === it.id ? QT.indigoSoft : "transparent",
              border: "none", borderRadius: 4, cursor: "pointer",
              color: navActive === it.id ? QT.indigo : QT.textDim,
              fontFamily: QT.mono, fontSize: 11, letterSpacing: 1, fontWeight: 600,
              position: "relative", textAlign: "left", transition: "background 120ms, color 120ms",
            }}>
            {navActive === it.id && <div style={{ position: "absolute", left: 0, top: 6, bottom: 6, width: 2, background: QT.indigo, borderRadius: 2 }}/>}
            <Icon name={it.icon} size={14}/>
            {it.label}
          </button>
        ))}
      </nav>
      <div style={{ padding: 12, borderTop: `1px solid ${QT.border}`, fontFamily: QT.mono, fontSize: 10, color: QT.textMute }}>
        <div style={{ display: "flex", justifyContent: "space-between", padding: "4px 0" }}>
          <span>VERSION</span><span style={{ color: QT.text }}>v1.0.0</span>
        </div>
        <div style={{ display: "flex", justifyContent: "space-between", padding: "4px 0" }}>
          <span>STATUT</span><span style={{ color: QT.green }}>BETA PUBLIQUE</span>
        </div>
      </div>
    </aside>
  );
};

const qtIconBtn = {
  width: 30, height: 30, background: "transparent", border: `1px solid ${QT.border}`,
  borderRadius: 4, color: QT.textDim, cursor: "pointer",
  display: "flex", alignItems: "center", justifyContent: "center",
};

// ──────────────────────────────────────────────────────────────
// Building blocks
// ──────────────────────────────────────────────────────────────
const QTPanel = ({ title, right, children, pad = true, style = {} }) => (
  <div style={{
    background: QT.panel, border: `1px solid ${QT.border}`, borderRadius: 4,
    display: "flex", flexDirection: "column", ...style,
  }}>
    {title && (
      <div style={{
        padding: "10px 14px", borderBottom: `1px solid ${QT.border}`,
        display: "flex", alignItems: "center", justifyContent: "space-between", gap: 8,
        fontFamily: QT.mono, fontSize: 10, color: QT.textDim, letterSpacing: 2,
      }}>
        <span>{title}</span>
        {right}
      </div>
    )}
    <div style={{ padding: pad ? 14 : 0, flex: 1, minHeight: 0 }}>{children}</div>
  </div>
);

const QTStat = ({ label, value, change, mono = true }) => (
  <div style={{ padding: "12px 14px", borderRight: `1px solid ${QT.border}`, flex: 1, minWidth: 0, overflow: "hidden" }}>
    <div style={{ fontFamily: QT.mono, fontSize: 9, color: QT.textMute, letterSpacing: 1.5, marginBottom: 6, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{label}</div>
    <div style={{ fontFamily: mono ? QT.mono : QT.sans, fontSize: 17, color: QT.text, fontWeight: 600, letterSpacing: -0.3, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{value}</div>
    {change !== undefined && (
      <div style={{ fontFamily: QT.mono, fontSize: 10, color: change >= 0 ? QT.green : QT.red, marginTop: 4 }}>
        {qtSign(change)}{qtFmt(Math.abs(change), 2)}%
      </div>
    )}
  </div>
);

// Sparkline / equity curve — version non interactive (cartes par-compte, etc.)
const QTSpark = ({ data, color = QT.indigo, height = 60, fill = true }) => {
  if (!data || data.length < 2) {
    return <div style={{ height, fontFamily: QT.mono, fontSize: 10, color: QT.textMute, display: "flex", alignItems: "center", justifyContent: "center" }}>—</div>;
  }
  const min = Math.min(...data), max = Math.max(...data);
  const range = max - min || 1;
  const w = 100;
  const points = data.map((v, i) => `${(i / (data.length - 1)) * w},${height - ((v - min) / range) * height}`).join(" ");
  const area = `0,${height} ${points} ${w},${height}`;
  return (
    <svg viewBox={`0 0 ${w} ${height}`} preserveAspectRatio="none" style={{ width: "100%", height, display: "block" }}>
      {fill && <polygon points={area} fill={color} fillOpacity={0.08}/>}
      <polyline points={points} pathLength="100" fill="none" stroke={color} strokeWidth="0.8" vectorEffect="non-scaling-stroke"/>
    </svg>
  );
};

// Génère une courbe d'équité qui monte (placeholder lorsque pas de données réelles)
const qtMakeCurve = (n, base, drift, vol) => {
  const out = [base]; let v = base;
  for (let i = 1; i < n; i++) {
    v += drift + (Math.sin(i * 0.6) + Math.cos(i * 0.31) + (Math.random() - 0.5)) * vol;
    out.push(v);
  }
  return out;
};

// ──────────────────────────────────────────────────────────────
// QTInteractiveCurve — courbe avec hover tooltip
// data : [{ value, date }] ou [number]
// ──────────────────────────────────────────────────────────────
const QTInteractiveCurve = ({ data, color = QT.indigo, height = 232, fill = true, formatValue }) => {
  const fmt = formatValue || ((v) => qtFmt(v, 2));
  const points = React.useMemo(() => {
    return (data || []).map((d, i) => {
      if (typeof d === "number") return { value: d, date: null, i };
      return { value: Number(d.value) || 0, date: d.date || null, i };
    });
  }, [data]);

  const [hover, setHover] = React.useState(null);
  const containerRef = React.useRef(null);

  if (points.length < 2) {
    return <div style={{ height, fontFamily: QT.mono, fontSize: 10, color: QT.textMute, display: "flex", alignItems: "center", justifyContent: "center" }}>Pas assez de données.</div>;
  }

  const values = points.map(p => p.value);
  const min = Math.min(...values), max = Math.max(...values);
  const range = max - min || 1;
  const w = 100, h = height;
  // Abscisses proportionnelles au temps quand les dates sont connues
  // (sinon la courbe s'arrête visuellement au dernier trade et les
  // périodes creuses sont compressées) ; repli sur l'index sinon.
  const timeScaled = points.every(p => p.date);
  let xs;
  if (timeScaled) {
    const t0 = new Date(points[0].date).getTime();
    const t1 = new Date(points[points.length - 1].date).getTime();
    const span = (t1 - t0) || 1;
    xs = points.map(p => ((new Date(p.date).getTime() - t0) / span) * w);
  } else {
    xs = points.map((_, i) => (i / (points.length - 1)) * w);
  }
  const xy = points.map((p, i) => ({
    x: xs[i],
    y: h - ((p.value - min) / range) * h,
    p,
  }));
  const polyline = xy.map(pt => `${pt.x},${pt.y}`).join(" ");
  const area = `0,${h} ${polyline} ${w},${h}`;

  const onMove = (e) => {
    if (!containerRef.current) return;
    const rect = containerRef.current.getBoundingClientRect();
    const xPct = (e.clientX - rect.left) / rect.width;
    const xTarget = Math.max(0, Math.min(w, xPct * w));
    // Point le plus proche en abscisse (les points ne sont plus équidistants)
    let idx = 0;
    for (let i = 1; i < xy.length; i++) {
      if (Math.abs(xy[i].x - xTarget) < Math.abs(xy[idx].x - xTarget)) idx = i;
    }
    setHover({ idx, pixelX: e.clientX - rect.left, pixelY: rect.height * (xy[idx].y / h) });
  };

  const hoverPt = hover ? xy[hover.idx] : null;

  return (
    <div ref={containerRef}
      onMouseMove={onMove}
      onMouseLeave={() => setHover(null)}
      style={{ position: "relative", width: "100%", height, cursor: "crosshair" }}>
      <svg viewBox={`0 0 ${w} ${h}`} preserveAspectRatio="none"
        style={{ width: "100%", height: "100%", display: "block" }}>
        {fill && <polygon points={area} fill={color} fillOpacity={0.08}/>}
        <polyline points={polyline} fill="none" stroke={color} strokeWidth="0.8" vectorEffect="non-scaling-stroke"/>
        {hoverPt && (
          <>
            <line x1={hoverPt.x} x2={hoverPt.x} y1={0} y2={h}
              stroke={QT.borderStrong} strokeWidth="0.4" vectorEffect="non-scaling-stroke"/>
            <circle cx={hoverPt.x} cy={hoverPt.y} r="0.9" fill={color} stroke={QT.bg} strokeWidth="0.4" vectorEffect="non-scaling-stroke"/>
          </>
        )}
      </svg>
      {hoverPt && (
        <div style={{
          position: "absolute",
          left: Math.min(Math.max(hover.pixelX, 50), (containerRef.current ? containerRef.current.clientWidth : 200) - 110),
          top: 4,
          background: QT.panel2, border: `1px solid ${QT.borderStrong}`, borderRadius: 4,
          padding: "6px 8px", pointerEvents: "none",
          fontFamily: QT.mono, fontSize: 10, color: QT.text,
          whiteSpace: "nowrap",
        }}>
          <div style={{ color: QT.indigo, fontWeight: 600 }}>{fmt(hoverPt.p.value)}</div>
          {hoverPt.p.date && (
            <div style={{ color: QT.textDim, marginTop: 2 }}>
              {new Date(hoverPt.p.date).toLocaleDateString("fr-FR", { day: "2-digit", month: "short", year: "numeric" })}
            </div>
          )}
        </div>
      )}
    </div>
  );
};

// ──────────────────────────────────────────────────────────────
// DASHBOARD (accueil)
// ──────────────────────────────────────────────────────────────
const QTDashboard = ({ go } = {}) => {
  // Données live depuis Supabase (trades + KPIs).
  const [loaded, setLoaded] = React.useState(false);
  const [live, setLive] = React.useState({ kpis: null, trades: [], allTrades: [], accounts: null });

  React.useEffect(() => {
    let cancelled = false;
    (async () => {
      try {
        const sb = window.nomerusSb();
        const [kpis, trades] = await Promise.all([
          window.nomerusData.kpis(),
          window.nomerusData.recentTrades(10),
        ]);
        // Récupération de tous les trades pour la courbe filtrable + comptes
        let allTrades = [];
        let accounts = null;
        if (sb) {
          const { data } = await sb
            .from("trades")
            .select("id, date, asset, direction, pnl, account, platform, session, result, is_demo")
            .order("date", { ascending: true });
          allTrades = data || [];

          // Tente de lire la table broker_connections (peut ne pas exister)
          try {
            const { data: bc } = await sb
              .from("broker_connections")
              .select("id, broker, label, asset_class, status, created_at");
            if (bc) accounts = bc;
          } catch (e) { /* table optionnelle */ }
        }
        if (!cancelled) setLive({ kpis, trades: trades || [], allTrades, accounts });
      } catch (e) {
        console.warn("[dashboard] fetch live:", e);
      } finally {
        if (!cancelled) setLoaded(true);
      }
    })();
    return () => { cancelled = true; };
  }, []);

  // Détection trades démo (pour le bandeau d'item 15)
  const demoCount = (live.allTrades || []).filter(t => t && t.is_demo).length;
  const [demoBannerDismissed, setDemoBannerDismissed] = React.useState(false);
  const [demoDeleting, setDemoDeleting] = React.useState(false);
  const onDeleteDemo = async () => {
    if (demoDeleting) return;
    setDemoDeleting(true);
    const res = await window.nomerusDemoSeeder.clear();
    setDemoDeleting(false);
    if (res.ok) {
      // Recharge la page pour repartir propre
      window.location.reload();
    } else {
      alert(res.error || "Erreur lors de la suppression.");
    }
  };

  // Formatage des trades cloud → format attendu par le tableau « 7 DERNIERS »
  const liveTrades = (live.trades || []).slice(0, 7).map(t => {
    const dt = t.date ? new Date(t.date) : null;
    const hh = dt ? dt.toLocaleTimeString("fr-FR", { hour: "2-digit", minute: "2-digit", second: "2-digit" }) : "—";
    return {
      id: t.id,
      time: hh,
      account: t.account || t.platform || "—",
      pair: t.asset || "—",
      side: (t.direction || "LONG").toLowerCase(),
      entry: t.entry_price != null ? t.entry_price : "—",
      exit: t.exit_price != null ? t.exit_price : "—",
      size: t.volume != null && t.volume !== 0 ? t.volume : "—",
      pnl: Number(t.pnl) || 0,
      duration: "—",
    };
  });

  // KPI : réels uniquement — zéros tant que rien n'est chargé (pas de mock en app loggée)
  const kpis = live.kpis;
  const tot = kpis ? {
    equity: kpis.equity,
    pnlToday: kpis.pnlToday,
    pnlTodayPct: 0,
    pnl30d: kpis.pnl30d,
    pnl30dPct: 0,
    openTrades: 0,
    winrateAll: kpis.winRate,
    sharpeAll: 0,
    maxDD: kpis.maxDdPct || 0,
  } : { equity: 0, pnlToday: 0, pnlTodayPct: 0, pnl30d: 0, pnl30dPct: 0, openTrades: 0, winrateAll: 0, sharpeAll: 0, maxDD: 0 };

  // ── Equity curve avec filtres de période ────────────────────────────
  const [period, setPeriod] = React.useState("90J");
  const periods = ["1J", "7J", "30J", "90J", "1A", "TOUT"];
  const periodMs = { "1J": 86400e3, "7J": 7*86400e3, "30J": 30*86400e3, "90J": 90*86400e3, "1A": 365*86400e3, "TOUT": Infinity };

  // Reconstitue la courbe à partir des trades filtrés. Sans trades : courbe vide
  // (état vide explicite, jamais de données inventées en app loggée).
  // La courbe est ancrée au début de la période (équité cumulée avant la
  // période) et prolongée jusqu'à maintenant : l'équité ne « s'arrête » pas
  // au dernier trade, elle reste plate tant qu'on ne trade pas.
  const equityPoints = React.useMemo(() => {
    const trades = (live.allTrades || []).filter(t => t.date);
    if (trades.length === 0) return [];
    const now = Date.now();
    const cutoff = period === "TOUT"
      ? new Date(trades[0].date).getTime()
      : now - periodMs[period];
    let equity = 0;
    const pts = [];
    for (const t of trades) {
      const ts = new Date(t.date).getTime();
      if (ts >= cutoff && pts.length === 0) {
        pts.push({ value: equity, date: new Date(cutoff).toISOString() });
      }
      equity += Number(t.pnl) || 0;
      if (ts >= cutoff) pts.push({ value: equity, date: t.date });
    }
    // Aucun trade dans la période : ligne plate au niveau d'équité courant
    if (pts.length === 0) pts.push({ value: equity, date: new Date(cutoff).toISOString() });
    pts.push({ value: equity, date: new Date(now).toISOString() });
    return pts;
  }, [live.allTrades, period]);

  // Axe X (libellés)
  const xAxisLabels = React.useMemo(() => {
    if (equityPoints.length < 2) return [];
    const first = new Date(equityPoints[0].date);
    const last = new Date(equityPoints[equityPoints.length - 1].date);
    const fmt = (d) => d.toLocaleDateString("fr-FR", { day: "2-digit", month: "short" });
    const span = last - first;
    const ticks = 5;
    const out = [];
    for (let i = 0; i < ticks; i++) {
      const t = new Date(first.getTime() + (span * i) / (ticks - 1));
      out.push(fmt(t));
    }
    return out;
  }, [equityPoints]);

  // Axe Y (5 paliers réguliers)
  const yAxisLabels = React.useMemo(() => {
    if (equityPoints.length < 2) return ["—","—","—","—","—"];
    const vals = equityPoints.map(p => p.value);
    const mn = Math.min(...vals), mx = Math.max(...vals);
    const out = [];
    for (let i = 4; i >= 0; i--) {
      const v = mn + ((mx - mn) * i) / 4;
      out.push(Math.abs(v) >= 1000 ? `${qtFmt(v / 1000, 1)}k CHF` : `${qtFmt(v, 0)} CHF`);
    }
    return out;
  }, [equityPoints]);

  // ── Stats du jour, calculées sur les vrais trades ───────────────────
  const todayStats = React.useMemo(() => {
    const start = new Date(); start.setHours(0, 0, 0, 0);
    const todays = (live.allTrades || []).filter(t => t.date && new Date(t.date) >= start);
    let pnl = 0, wins = 0, best = -Infinity, worst = Infinity, eq = 0;
    const curve = [];
    for (const t of todays) {
      const p = Number(t.pnl) || 0;
      pnl += p; eq += p; curve.push(eq);
      if (p >= 0) wins += 1;
      best = Math.max(best, p); worst = Math.min(worst, p);
    }
    return {
      count: todays.length, pnl, curve,
      winRate: todays.length > 0 ? (wins / todays.length) * 100 : 0,
      best: isFinite(best) ? best : 0,
      worst: isFinite(worst) ? worst : 0,
    };
  }, [live.allTrades]);

  // ── Insights calculés sur les vrais trades (aucun insight inventé) ──
  const insights = React.useMemo(() => {
    const trades = (live.allTrades || []).filter(t => t.date);
    if (trades.length < 10) return [];
    const out = [];
    // Paire la plus / moins rentable
    const byPair = new Map();
    for (const t of trades) {
      const k = t.asset || "?";
      const s = byPair.get(k) || { pnl: 0, n: 0 };
      s.pnl += Number(t.pnl) || 0; s.n += 1;
      byPair.set(k, s);
    }
    const pairs = Array.from(byPair.entries()).filter(([, s]) => s.n >= 3);
    if (pairs.length > 0) {
      const best = pairs.reduce((a, b) => (b[1].pnl > a[1].pnl ? b : a));
      out.push({
        impact: "high", type: "edge", title: `${best[0]} est ton actif le plus rentable`,
        body: `${qtSign(best[1].pnl)}${qtFmt(Math.abs(best[1].pnl))} CHF cumulés sur ${best[1].n} trades.`,
      });
      const worst = pairs.reduce((a, b) => (b[1].pnl < a[1].pnl ? b : a));
      if (worst[1].pnl < 0) {
        out.push({
          impact: "med", type: "risk", title: `${worst[0]} te coûte de l'argent`,
          body: `${qtSign(worst[1].pnl)}${qtFmt(Math.abs(worst[1].pnl))} CHF cumulés sur ${worst[1].n} trades.`,
        });
      }
    }
    // Jour de semaine le moins rentable
    const dows = [0, 0, 0, 0, 0, 0, 0];
    const dowN = [0, 0, 0, 0, 0, 0, 0];
    for (const t of trades) {
      const i = (new Date(t.date).getDay() + 6) % 7;
      dows[i] += Number(t.pnl) || 0; dowN[i] += 1;
    }
    const labels = ["lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi", "dimanche"];
    let wi = -1;
    for (let i = 0; i < 7; i++) if (dowN[i] >= 3 && dows[i] < 0 && (wi < 0 || dows[i] < dows[wi])) wi = i;
    if (wi >= 0) {
      out.push({
        impact: "med", type: "risk", title: `Le ${labels[wi]} te coûte cher`,
        body: `${qtSign(dows[wi])}${qtFmt(Math.abs(dows[wi]))} CHF cumulés le ${labels[wi]} (${dowN[wi]} trades).`,
      });
    }
    return out.slice(0, 2);
  }, [live.allTrades]);

  // ── Alerte perte journalière (réglage « LIMITE DE PERTE JOURNALIÈRE ») ──
  const dailyLossLimit = React.useMemo(() => {
    try {
      const s = JSON.parse(localStorage.getItem("nomerus_user_settings_v1") || "{}");
      const v = Number(s.daily_loss_limit);
      return v > 0 ? v : null;
    } catch (e) { return null; }
  }, []);
  const dailyLossBreached = dailyLossLimit != null && todayStats.pnl <= -dailyLossLimit;

  // ── Comptes connectés (Supabase uniquement — état vide sinon) ──────
  const accounts = (live.accounts || []).map(a => ({
    id: a.id,
    name: a.label || a.broker,
    broker: a.broker,
    asset: a.asset_class || "—",
    color: QT.indigo,
    status: a.status || "pending",
    pnl: 0, pnlPct: 0, trades: 0, winrate: 0, sharpe: 0, dd: 0,
  }));

  const hasLiveTrades = liveTrades.length > 0;

  return (
    <div style={{ padding: 18, display: "grid", gridTemplateColumns: "1fr 1fr 1fr 1fr", gap: 14, gridAutoRows: "min-content" }}>
      {/* Bandeau démo (item 15) */}
      {demoCount > 0 && !demoBannerDismissed && (
        <div style={{
          gridColumn: "1 / -1", padding: "12px 16px", background: QT.indigoSoft,
          border: `1px solid ${QT.borderStrong}`, borderLeft: `3px solid ${QT.indigo}`, borderRadius: 4,
          display: "flex", alignItems: "center", gap: 14, flexWrap: "wrap",
        }}>
          <div style={{ flex: 1, minWidth: 220 }}>
            <div style={{ fontFamily: QT.sans, fontSize: 13, color: QT.text, fontWeight: 600 }}>
              Vous avez encore {demoCount} trades de démo sur votre compte.
            </div>
            <div style={{ fontFamily: QT.mono, fontSize: 10, color: QT.textDim, letterSpacing: 1, marginTop: 4 }}>
              Une fois ton vrai broker connecté, tu peux les supprimer pour ne garder que tes vrais trades.
            </div>
          </div>
          <button onClick={onDeleteDemo} disabled={demoDeleting} style={{
            background: QT.indigo, color: QT.bg, border: "none", borderRadius: 3,
            padding: "8px 14px", fontFamily: QT.mono, fontSize: 10, fontWeight: 700, letterSpacing: 1.5,
            cursor: demoDeleting ? "wait" : "pointer", opacity: demoDeleting ? 0.6 : 1,
          }}>{demoDeleting ? "SUPPRESSION…" : "SUPPRIMER LES TRADES DÉMO"}</button>
          <button onClick={() => setDemoBannerDismissed(true)} title="Fermer" style={{
            background: "transparent", color: QT.textDim, border: `1px solid ${QT.border}`,
            borderRadius: 3, padding: "6px 8px", cursor: "pointer",
          }}><Icon name="close" size={12}/></button>
        </div>
      )}

      {/* Alerte coupe-circuit : perte du jour au-delà de la limite réglée */}
      {dailyLossBreached && (
        <div style={{
          gridColumn: "1 / -1", padding: "12px 16px", background: "rgba(255, 107, 122, 0.08)",
          border: "1px solid rgba(255, 107, 122, 0.35)", borderLeft: `3px solid ${QT.red}`, borderRadius: 4,
          display: "flex", alignItems: "center", gap: 12,
        }}>
          <div style={{ fontFamily: QT.mono, fontSize: 10, color: QT.red, fontWeight: 700, letterSpacing: 1.5 }}>⚠ LIMITE DE PERTE JOURNALIÈRE DÉPASSÉE</div>
          <div style={{ fontFamily: QT.sans, fontSize: 12, color: QT.textDim }}>
            P&L du jour {qtSign(todayStats.pnl)}{qtFmt(Math.abs(todayStats.pnl))} CHF — limite réglée à -{qtFmt(dailyLossLimit)} CHF (Paramètres → Gestion du risque).
          </div>
        </div>
      )}

      {/* Ligne KPI (réelle uniquement) */}
      <div style={{ gridColumn: "1 / -1", display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(150px, 1fr))", background: QT.panel, border: `1px solid ${QT.border}`, borderRadius: 4 }}>
        <QTStat label="ÉQUITÉ · CHF" value={`${qtSign(tot.equity)}${qtFmt(Math.abs(tot.equity))}`}/>
        <QTStat label="P&L · AUJOURD'HUI" value={`${qtSign(tot.pnlToday)}${qtFmt(Math.abs(tot.pnlToday))} CHF`} change={tot.pnlTodayPct}/>
        <QTStat label="P&L · 30J" value={`${qtSign(tot.pnl30d)}${qtFmt(Math.abs(tot.pnl30d))} CHF`} change={tot.pnl30dPct}/>
        <QTStat label="TRADES · 30J" value={kpis ? String(kpis.nbTrades30d) : String(tot.openTrades)}/>
        <QTStat label="TX RÉUSSITE" value={`${qtFmt(tot.winrateAll, 1)}%`}/>
        <QTStat label="TOTAL TRADES" value={kpis ? String(kpis.nbTradesAll) : "—"}/>
        <div style={{ padding: "12px 14px", minWidth: 0, overflow: "hidden" }}>
          <div style={{ fontFamily: QT.mono, fontSize: 9, color: QT.textMute, letterSpacing: 1.5, marginBottom: 6, whiteSpace: "nowrap" }}>DRAWDOWN MAX</div>
          <div style={{ fontFamily: QT.mono, fontSize: 17, color: QT.red, fontWeight: 600, letterSpacing: -0.3, whiteSpace: "nowrap" }}>{qtFmt(Math.abs(tot.maxDD), 1)}%</div>
          <div style={{ fontFamily: QT.mono, fontSize: 10, color: QT.textMute, marginTop: 4 }}>{kpis ? "live" : "démo"}</div>
        </div>
      </div>

      {/* Equity chart */}
      <div style={{ gridColumn: "1 / 4" }}>
        <QTPanel title="COURBE D'ÉQUITÉ" pad={false} right={
          <div style={{ display: "flex", gap: 4 }}>
            {periods.map(p => (
              <button key={p} onClick={() => setPeriod(p)} style={{
                padding: "2px 8px", fontSize: 9, letterSpacing: 1,
                color: p === period ? QT.indigo : QT.textMute,
                background: p === period ? QT.indigoSoft : "transparent",
                border: "none", borderRadius: 3, cursor: "pointer",
                fontFamily: QT.mono, fontWeight: 600,
              }}>{p}</button>
            ))}
          </div>
        }>
          <div style={{ position: "relative", height: 280, padding: "16px 0 0" }}>
            {/* Axe Y */}
            <div style={{ position: "absolute", right: 12, top: 16, bottom: 32, display: "flex", flexDirection: "column", justifyContent: "space-between", fontFamily: QT.mono, fontSize: 9, color: QT.textMute, textAlign: "right", pointerEvents: "none" }}>
              {yAxisLabels.map((y, i) => <span key={i}>{y}</span>)}
            </div>
            {/* Grille */}
            <svg style={{ position: "absolute", inset: "16px 50px 32px 14px", width: "calc(100% - 64px)" }} preserveAspectRatio="none" viewBox="0 0 100 100">
              {[0, 25, 50, 75, 100].map(y => (
                <line key={y} x1="0" y1={y} x2="100" y2={y} stroke={QT.border} strokeWidth="0.3"/>
              ))}
            </svg>
            <div style={{ position: "absolute", inset: "16px 50px 32px 14px" }}>
              {equityPoints.length > 1 ? (
                <QTInteractiveCurve data={equityPoints} height={232}
                  formatValue={(v) => `${qtSign(v)}${qtFmt(Math.abs(v))} CHF`}/>
              ) : (
                <div style={{ height: 232, display: "flex", alignItems: "center", justifyContent: "center", fontFamily: QT.mono, fontSize: 10, color: QT.textMute, letterSpacing: 1.5 }}>
                  {loaded ? "AUCUN TRADE ENREGISTRÉ" : "CHARGEMENT…"}
                </div>
              )}
            </div>
            {/* Axe X */}
            <div style={{ position: "absolute", left: 14, right: 50, bottom: 8, display: "flex", justifyContent: "space-between", fontFamily: QT.mono, fontSize: 9, color: QT.textMute }}>
              {(xAxisLabels.length === 5 ? xAxisLabels : ["—","—","—","—","—"]).map((l, i) => <span key={i}>{l}</span>)}
            </div>
          </div>
        </QTPanel>
      </div>

      {/* Snapshot aujourd'hui */}
      <div>
        <QTPanel title="AUJOURD'HUI · INTRADAY">
          <div style={{ fontFamily: QT.mono, fontSize: 10, color: QT.textMute, letterSpacing: 1.5, marginBottom: 4 }}>P&L</div>
          <div style={{ fontFamily: QT.mono, fontSize: 28, color: todayStats.pnl >= 0 ? QT.green : QT.red, fontWeight: 600, marginBottom: 12 }}>
            {qtSign(todayStats.pnl)}{qtFmt(Math.abs(todayStats.pnl))} CHF
          </div>
          {todayStats.curve.length > 1 ? (
            <QTSpark data={todayStats.curve} color={todayStats.pnl >= 0 ? QT.green : QT.red} height={70}/>
          ) : (
            <div style={{ height: 70, display: "flex", alignItems: "center", justifyContent: "center", fontFamily: QT.mono, fontSize: 10, color: QT.textMute, letterSpacing: 1.5 }}>
              {todayStats.count === 0 ? "AUCUN TRADE AUJOURD'HUI" : "—"}
            </div>
          )}
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8, marginTop: 14, fontFamily: QT.mono, fontSize: 10 }}>
            <div><div style={{ color: QT.textMute }}>TRADES</div><div style={{ color: QT.text, fontSize: 14 }}>{todayStats.count}</div></div>
            <div><div style={{ color: QT.textMute }}>TX RÉUSSITE</div><div style={{ color: QT.text, fontSize: 14 }}>{todayStats.count > 0 ? `${qtFmt(todayStats.winRate, 0)}%` : "—"}</div></div>
            <div><div style={{ color: QT.textMute }}>MEILLEUR</div><div style={{ color: QT.green, fontSize: 14 }}>{todayStats.count > 0 ? `${qtSign(todayStats.best)}${qtFmt(Math.abs(todayStats.best), 0)} CHF` : "—"}</div></div>
            <div><div style={{ color: QT.textMute }}>PIRE</div><div style={{ color: QT.red, fontSize: 14 }}>{todayStats.count > 0 ? `${qtSign(todayStats.worst)}${qtFmt(Math.abs(todayStats.worst), 0)} CHF` : "—"}</div></div>
          </div>
        </QTPanel>
      </div>

      {/* Tableau comptes connectés */}
      <div style={{ gridColumn: "1 / 4" }}>
        <QTPanel title={`BROKERS · SYNC AUTOMATIQUE : BIENTÔT`} pad={false} right={
          <button onClick={() => go && go("setup")} style={{
            color: QT.indigo, cursor: "pointer", fontSize: 9, letterSpacing: 2,
            background: "transparent", border: "none", fontFamily: QT.mono, fontWeight: 600,
          }}>SYNC : BIENTÔT →</button>
        }>
          <div style={{ overflowX: "auto" }}>
            <table style={{ width: "100%", minWidth: 880, borderCollapse: "collapse", fontFamily: QT.mono, fontSize: 11, tableLayout: "auto" }}>
              <thead>
                <tr style={{ color: QT.textMute, fontSize: 9, letterSpacing: 1.5 }}>
                  {["", "COMPTE", "BROKER", "ACTIF", "P&L CHF", "P&L %", "TRADES", "TX %", "SHARPE", "DD %", "FLUX"].map((h, idx) => (
                    <th key={idx} style={{
                      textAlign: idx <= 1 ? "left" : "right",
                      padding: "8px 14px", fontWeight: 500, borderBottom: `1px solid ${QT.border}`,
                      whiteSpace: "nowrap",
                    }}>{h}</th>
                  ))}
                </tr>
              </thead>
              <tbody>
                {accounts.length === 0 && (
                  <tr>
                    <td colSpan={11} style={{ padding: 18, fontFamily: QT.mono, fontSize: 10, color: QT.textMute, letterSpacing: 1 }}>
                      <span style={{ color: QT.amber }}>// SYNC BROKER : BIENTÔT</span> · La synchronisation automatique arrive (beta privée). En attendant, importe tes trades par CSV ou saisis-les à la main dans le Journal.
                    </td>
                  </tr>
                )}
                {accounts.map(b => (
                  <tr key={b.id} className="qt-row" style={{ borderBottom: `1px solid ${QT.border}`, transition: "background 120ms" }}>
                    <td style={{ padding: "10px 0 10px 14px", width: 14 }}>
                      <div style={{ width: 6, height: 6, borderRadius: "50%", background: b.color || QT.indigo }}/>
                    </td>
                    <td style={{ padding: "10px 14px", color: QT.text, fontFamily: QT.sans, fontWeight: 500 }}>{b.name}</td>
                    <td style={{ padding: "10px 14px", color: QT.textDim, textAlign: "right" }}>{b.broker}</td>
                    <td style={{ padding: "10px 14px", color: QT.textDim, textAlign: "right" }}>{b.asset}</td>
                    <td style={{ padding: "10px 14px", color: b.pnl >= 0 ? QT.green : QT.red, textAlign: "right" }}>{qtSign(b.pnl)}{qtFmt(Math.abs(b.pnl))}</td>
                    <td style={{ padding: "10px 14px", color: b.pnl >= 0 ? QT.green : QT.red, textAlign: "right" }}>{qtSign(b.pnlPct)}{qtFmt(Math.abs(b.pnlPct), 2)}%</td>
                    <td style={{ padding: "10px 14px", color: QT.text, textAlign: "right" }}>{b.trades}</td>
                    <td style={{ padding: "10px 14px", color: QT.text, textAlign: "right" }}>{b.winrate}</td>
                    <td style={{ padding: "10px 14px", color: QT.text, textAlign: "right" }}>{b.sharpe}</td>
                    <td style={{ padding: "10px 14px", color: QT.red, textAlign: "right" }}>{b.dd}</td>
                    <td style={{ padding: "10px 14px", textAlign: "right" }}>
                      <span style={{
                        fontSize: 9, padding: "2px 6px", borderRadius: 2, letterSpacing: 1,
                        color: b.status === "active" ? QT.green : QT.amber,
                        background: b.status === "active" ? "rgba(91, 217, 154, 0.1)" : "rgba(255, 180, 84, 0.1)",
                        border: `1px solid ${b.status === "active" ? "rgba(91, 217, 154, 0.3)" : "rgba(255, 180, 84, 0.3)"}`,
                      }}>{b.status === "active" ? "● SYNCHRO" : "❚❚ EN PAUSE"}</span>
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </QTPanel>
      </div>

      {/* Insights IA */}
      <div>
        <QTPanel title="◆ INSIGHTS · CALCULÉS SUR TES TRADES">
          <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
            {insights.length === 0 && (
              <div style={{ padding: 10, fontFamily: QT.mono, fontSize: 10, color: QT.textMute, letterSpacing: 1, lineHeight: 1.6 }}>
                Pas encore assez de trades pour dégager des tendances (minimum 10).
              </div>
            )}
            {insights.map((ins, i) => (
              <div key={i} style={{
                padding: 10, background: QT.panel2, borderLeft: `2px solid ${ins.impact === "high" ? QT.indigo : ins.impact === "med" ? QT.amber : QT.textMute}`,
                borderRadius: "0 3px 3px 0",
              }}>
                <div style={{ fontFamily: QT.mono, fontSize: 9, color: QT.textMute, letterSpacing: 1, marginBottom: 4 }}>
                  [{ins.impact === "high" ? "ÉLEVÉ" : ins.impact === "med" ? "MOYEN" : "FAIBLE"}] · {ins.type.toUpperCase()}
                </div>
                <div style={{ fontFamily: QT.sans, fontSize: 12, color: QT.text, fontWeight: 600, marginBottom: 4 }}>{ins.title}</div>
                <div style={{ fontFamily: QT.sans, fontSize: 11, color: QT.textDim, lineHeight: 1.5 }}>{ins.body}</div>
              </div>
            ))}
          </div>
        </QTPanel>
      </div>

      {/* Derniers trades (cliquables) */}
      <div style={{ gridColumn: "1 / -1" }}>
        <QTPanel title="DERNIERS TRADES" pad={false} right={
          <button onClick={() => go && go("journal")} style={{
            color: QT.indigo, fontSize: 9, letterSpacing: 2, cursor: "pointer",
            background: "transparent", border: "none", fontFamily: QT.mono, fontWeight: 600,
          }}>VOIR TOUT →</button>
        }>
          {!loaded && (
            <div style={{ padding: 18, fontFamily: QT.mono, fontSize: 10, color: QT.textMute, letterSpacing: 1.5 }}>
              CHARGEMENT DES TRADES…
            </div>
          )}
          {loaded && !hasLiveTrades && (
            <div style={{ padding: 18, background: QT.panel2, borderBottom: `1px solid ${QT.border}`, fontFamily: QT.mono, fontSize: 10, color: QT.textDim, letterSpacing: 1, lineHeight: 1.6 }}>
              <span style={{ color: QT.amber }}>// AUCUN TRADE</span> · Importe un CSV ou ajoute un trade dans le Journal pour voir tes vraies stats.
            </div>
          )}
          <div style={{ overflowX: "auto" }}>
            <table style={{ width: "100%", minWidth: 760, borderCollapse: "collapse", fontFamily: QT.mono, fontSize: 11 }}>
              <thead>
                <tr style={{ color: QT.textMute, fontSize: 9, letterSpacing: 1.5 }}>
                  {["HEURE", "COMPTE", "PAIRE", "SENS", "ENTRÉE", "SORTIE", "TAILLE", "DURÉE", "P&L"].map((h, idx) => (
                    <th key={idx} style={{
                      textAlign: ["ENTRÉE","SORTIE","TAILLE","P&L"].includes(h) ? "right" : "left",
                      padding: "8px 14px", fontWeight: 500, borderBottom: `1px solid ${QT.border}`, whiteSpace: "nowrap",
                    }}>{h}</th>
                  ))}
                </tr>
              </thead>
              <tbody>
                {liveTrades.map(t => (
                  <tr key={t.id}
                    className="qt-row"
                    onClick={() => go && go("trade", { id: t.id })}
                    style={{
                      borderBottom: `1px solid ${QT.border}`, transition: "background 120ms",
                      cursor: "pointer",
                    }}>
                    <td style={{ padding: "8px 14px", color: QT.textMute }}>
                      <span style={{ color: QT.green, marginRight: 6 }}>●</span>{t.time}
                    </td>
                    <td style={{ padding: "8px 14px", color: QT.text, fontFamily: QT.sans }}>{t.account || t.bot}</td>
                    <td style={{ padding: "8px 14px", color: QT.text }}>{t.pair}</td>
                    <td style={{ padding: "8px 14px", color: t.side === "long" ? QT.green : QT.red, letterSpacing: 1 }}>{t.side.toUpperCase()}</td>
                    <td style={{ padding: "8px 14px", color: QT.textDim, textAlign: "right" }}>{t.entry}</td>
                    <td style={{ padding: "8px 14px", color: QT.textDim, textAlign: "right" }}>{t.exit}</td>
                    <td style={{ padding: "8px 14px", color: QT.textDim, textAlign: "right" }}>{t.size}</td>
                    <td style={{ padding: "8px 14px", color: QT.textMute }}>{t.duration}</td>
                    <td style={{ padding: "8px 14px", color: t.pnl >= 0 ? QT.green : QT.red, textAlign: "right", fontWeight: 600 }}>
                      {qtSign(t.pnl)}{qtFmt(Math.abs(t.pnl))} CHF
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </QTPanel>
      </div>
    </div>
  );
};

window.QT = QT;
window.QTSidebar = QTSidebar;
window.QTPanel = QTPanel;
window.QTDashboard = QTDashboard;  // alias historique
window.QTHome = QTDashboard;       // nouveau nom canonique
window.QTSpark = QTSpark;
window.QTInteractiveCurve = QTInteractiveCurve;
window.qtMakeCurve = qtMakeCurve;
window.qtFmt = qtFmt;
window.qtSign = qtSign;
window.qtIconBtn = qtIconBtn;
