/* eslint-disable */
/**
 * Desktop screens — Pro Trading Dark.
 * Регистрируется в window.DesktopScreens, импортируется DesktopApp.
 *
 * ВАЖНО: весь файл обёрнут в IIFE чтобы const-переменные не попали в
 * глобальный scope. Babel-standalone компилирует все JSX в одном scope,
 * и имена вроде DashboardScreen, BotsScreen совпадают с mobile-screens.jsx
 * — без обёртки наш const перекрывает mobile, и MobileApp начинает рендерить
 * наш компонент с пустыми props (был баг "Cannot read 'weex' of undefined").
 */
(function () {
const { useState, useEffect, useCallback, useMemo, useRef } = React;
const DIcon = (window.DesktopIcons && window.DesktopIcons.DIcon) || (() => null);

// ─────────────────────────  Утилиты  ─────────────────────────
const fmt = (n, d = 2) => {
  const v = Number(n);
  if (!isFinite(v)) return "—";
  return v.toLocaleString("ru-RU", { minimumFractionDigits: d, maximumFractionDigits: d });
};
const fmtPx = (n) => {
  const v = Number(n);
  if (!isFinite(v) || v === 0) return "—";
  if (v > 100) return v.toLocaleString("ru-RU", { maximumFractionDigits: 2 });
  if (v > 1) return v.toLocaleString("ru-RU", { maximumFractionDigits: 4 });
  return v.toPrecision(5);
};
const fmtPct = (n, d = 2) => {
  const v = Number(n);
  if (!isFinite(v)) return "—";
  return (v >= 0 ? "+" : "") + v.toFixed(d) + "%";
};
const pctClass = (v) => (Number(v) >= 0 ? "pos" : "neg");
const ago = (ms) => {
  if (!ms) return "—";
  const s = Math.round((Date.now() - ms) / 1000);
  if (s < 60) return s + "с";
  if (s < 3600) return Math.round(s / 60) + "м";
  if (s < 86400) return Math.round(s / 3600) + "ч";
  return Math.round(s / 86400) + "д";
};

const Coin = ({ sym, size = "md" }) => {
  const s = (sym || "").toUpperCase().replace(/USDT$|USDC$|USD$/, "");
  return (
    <div className={"d-coin" + (size === "lg" ? " d-coin-lg" : "")}>
      {s ? s.slice(0, 3) : "?"}
    </div>
  );
};

const Skel = ({ w = "100%", h = 14, style }) => (
  <div className="d-skel" style={{ width: w, height: h, ...style }}/>
);

// ─────────────────────────  Hooks  ─────────────────────────
const useExchangeData = (exchange, exReady, refreshTick) => {
  const [account, setAccount] = useState({ equity: 0, available: 0, locked: 0, unrealized: 0, margin_ratio: 0 });
  const [positions, setPositions] = useState([]);
  const [orders, setOrders] = useState([]);
  const [loading, setLoading] = useState(false);
  const load = useCallback(async () => {
    if (!exReady[exchange]) {
      setAccount({ equity: 0, available: 0, locked: 0, unrealized: 0, margin_ratio: 0 });
      setPositions([]); setOrders([]); return;
    }
    setLoading(true);
    try {
      const [bal, pos, ord] = await Promise.all([
        window.MobileAPI.loadBalance(exchange),
        window.MobileAPI.loadPositions(exchange),
        window.MobileAPI.loadOpenOrders(exchange).catch(() => []),
      ]);
      const acc = bal && bal.account ? bal.account : (bal || {});
      setAccount({
        equity: Number(acc.equity || acc.total || 0),
        available: Number(acc.available || acc.free || 0),
        locked: Number(acc.locked || acc.frozen || 0),
        unrealized: Number(acc.unrealized || acc.unrealizedPnl || 0),
        margin_ratio: Number(acc.margin_ratio || 0),
      });
      setPositions(pos || []);
      setOrders(ord || []);
    } catch (e) {
      console.warn("loadExchangeData fail", e);
    }
    setLoading(false);
  }, [exchange, exReady]);
  useEffect(() => { load(); }, [load, refreshTick]);
  return { account, positions, orders, loading, reload: load };
};

// ═══════════════════════════════════════════════════════════════════════════════
//                            DASHBOARD SCREEN
// ═══════════════════════════════════════════════════════════════════════════════
const VerifyBanner = ({ user, pushToast }) => {
  const [sending, setSending] = useState(false);
  const [hidden, setHidden] = useState(false);
  if (!user || user.is_verified || hidden) return null;
  const send = async () => {
    setSending(true);
    try {
      const r = await fetch("/api/auth/resend-verify", { method: "POST" });
      if (!r.ok) throw new Error("HTTP " + r.status);
      const j = await r.json();
      if (j.already_verified) {
        pushToast({ msg: "Email уже подтверждён ✓", ok: true });
      } else if (j.sent === false) {
        pushToast({ msg: "Письмо не отправлено (SMTP не настроен)", err: true });
      } else {
        pushToast({ msg: "Письмо отправлено — проверь почту", ok: true });
      }
      pushToast({ msg: "Письмо отправлено — проверь почту", ok: true });
    } catch (e) {
      pushToast({ msg: "Ошибка: " + (e.message || e), err: true });
    }
    setSending(false);
  };
  return (
    <div style={{
      background: "var(--d-warn-dim)",
      border: "1px solid rgba(245,165,36,0.3)",
      borderRadius: "var(--d-radius)",
      padding: 12,
      marginBottom: 16,
      display: "flex",
      alignItems: "center",
      gap: 14,
    }}>
      <div style={{ fontSize: 22 }}>📧</div>
      <div style={{ flex: 1 }}>
        <div style={{ fontWeight: 600, color: "var(--d-warn)", fontSize: 13 }}>Email не подтверждён</div>
        <div className="muted" style={{ fontSize: 11.5, marginTop: 2 }}>
          Подтверди {user.email} — без этого нельзя сохранить API-ключи бирж и получать push.
        </div>
      </div>
      <button className="d-btn d-btn-primary" onClick={send} disabled={sending}>
        {sending ? <span className="d-spinner"/> : "Отправить письмо"}
      </button>
      <button className="d-icon-btn" onClick={() => setHidden(true)} title="Скрыть">
        <DIcon name="x" size={14}/>
      </button>
    </div>
  );
};

const DashboardScreen = ({ exchange, exReady, pushToast, refreshTick, user }) => {
  const { account, positions, orders, loading, reload } = useExchangeData(exchange, exReady, refreshTick);
  const [equityPts, setEquityPts] = useState([]);
  const [equityPeriod, setEquityPeriod] = useState(30);
  const [selectedPos, setSelectedPos] = useState(null);   // для PositionDetailModal
  const chartRef = useRef(null);

  useEffect(() => {
    (async () => {
      try {
        const r = await window.MobileAPI.loadEquityHistory(equityPeriod, exchange);
        setEquityPts(r?.points || []);
      } catch {}
    })();
  }, [exchange, equityPeriod, refreshTick]);

  // Equity chart — lightweight-charts
  useEffect(() => {
    if (!chartRef.current || !window.LightweightCharts || !equityPts.length) return;
    chartRef.current.innerHTML = "";
    const chart = window.LightweightCharts.createChart(chartRef.current, {
      width: chartRef.current.clientWidth,
      height: 180,
      layout: { background: { color: "transparent" }, textColor: "rgba(138, 145, 166, 0.8)" },
      grid: { vertLines: { visible: false }, horzLines: { color: "rgba(255,255,255,0.04)" } },
      timeScale: { borderColor: "rgba(255,255,255,0.06)", timeVisible: false },
      rightPriceScale: { borderColor: "rgba(255,255,255,0.06)" },
      crosshair: { mode: 1 },
    });
    const series = chart.addAreaSeries({
      lineColor: "#4D7CFE",
      topColor: "rgba(77, 124, 254, 0.4)",
      bottomColor: "rgba(77, 124, 254, 0)",
      lineWidth: 2,
    });
    series.setData(equityPts.map(p => ({ time: Math.floor(p.ts_ms / 1000), value: p.equity })));
    const onResize = () => chart.applyOptions({ width: chartRef.current.clientWidth });
    window.addEventListener("resize", onResize);
    return () => { window.removeEventListener("resize", onResize); chart.remove(); };
  }, [equityPts]);

  const totalUpnl = positions.reduce((s, p) => s + Number(p.unrealizePnl || 0), 0);
  const longCnt = positions.filter(p => String(p.side).toUpperCase() === "LONG").length;
  const shortCnt = positions.filter(p => String(p.side).toUpperCase() === "SHORT").length;
  const ready = exReady[exchange];

  // Risk metrics для Risk Widget
  const totalNotional = positions.reduce((s, p) => s + Number(p.size || 0) * Number(p.markPrice || 0), 0);
  const marginRatio = account.equity > 0 ? account.locked / account.equity : 0;
  const liqDistance = positions.length > 0
    ? Math.min(...positions.map(p => {
        const mark = Number(p.markPrice || 0), liq = Number(p.liquidatePrice || 0);
        if (!mark || !liq) return 100;
        return Math.abs((mark - liq) / mark * 100);
      }))
    : 100;

  return (
    <div>
      <VerifyBanner user={user} pushToast={pushToast}/>

      {!ready && (
        <div className="d-card" style={{ padding: 16, marginBottom: 16, background: "var(--d-warn-dim)", borderColor: "rgba(242,178,68,0.3)" }}>
          <div style={{ display: "flex", gap: 12, alignItems: "center" }}>
            <div style={{ width: 40, height: 40, borderRadius: 10, background: "var(--d-bg-2)", display: "grid", placeItems: "center", color: "var(--d-warn)" }}>
              <DIcon name="settings" size={20}/>
            </div>
            <div>
              <div style={{ fontWeight: 600, fontSize: 13 }}>Нет API-ключа для {exchange.toUpperCase()}</div>
              <div className="muted" style={{ fontSize: 11, marginTop: 2 }}>Добавь ключ в «Настройки» → «API ключи бирж» чтобы видеть позиции и торговать.</div>
            </div>
          </div>
        </div>
      )}

      {/* Аналитический блок наверху — Win Rate / Avg PnL / TP / Slippage / Profit Factor */}
      <AnalyticsTopBar refreshTick={refreshTick}/>

      {/* Hero — баланс биржи */}
      <div className="d-hero">
        <div className="d-hero-row">
          <div className="d-hero-balance" style={{ flex: 1 }}>
            <div className="d-kpi-label"><DIcon name="wallet" size={12}/> Капитал ({exchange.toUpperCase()})</div>
            <div className="d-kpi-value mono">{fmt(account.equity)} <span style={{ fontSize: 16, color: "var(--d-text-2)" }}>USDT</span></div>
            <div className={"d-kpi-delta mono " + pctClass(totalUpnl)}>
              {totalUpnl >= 0 ? "+" : ""}{fmt(totalUpnl)} USDT нереализ. PnL
            </div>
          </div>
          <div style={{ display: "flex", gap: 24 }}>
            <div>
              <div className="d-kpi-label">Доступно</div>
              <div className="d-kpi-value mono" style={{ fontSize: 18 }}>{fmt(account.available)}</div>
            </div>
            <div>
              <div className="d-kpi-label">Заморожено</div>
              <div className="d-kpi-value mono" style={{ fontSize: 18 }}>{fmt(account.locked)}</div>
            </div>
            <div>
              <div className="d-kpi-label">Margin Ratio</div>
              <div className="d-kpi-value mono" style={{ fontSize: 18 }}>{(account.margin_ratio * 100).toFixed(2)}%</div>
            </div>
          </div>
        </div>
      </div>

      {/* Risk widget — отдельный блок над KPI */}
      {ready && positions.length > 0 && (
        <RiskWidget
          marginRatio={marginRatio}
          liqDistance={liqDistance}
          totalNotional={totalNotional}
          equity={account.equity}
          positions={positions}
        />
      )}

      <div className="d-kpi-grid" style={{ marginBottom: 16 }}>
        <div className="d-kpi">
          <div className="d-kpi-label">Открытых позиций</div>
          <div className="d-kpi-value mono">{positions.length}</div>
          <div className="d-kpi-sub mono">
            <span className="pos">{longCnt} LONG</span> · <span className="neg">{shortCnt} SHORT</span>
          </div>
        </div>
        <div className="d-kpi">
          <div className="d-kpi-label">Активных ордеров</div>
          <div className="d-kpi-value mono">{orders.length}</div>
          <div className="d-kpi-sub">лимитки + триггеры</div>
        </div>
        <div className="d-kpi">
          <div className="d-kpi-label">Использовано маржи</div>
          <div className="d-kpi-value mono">{fmt(account.locked)}</div>
          <div className="d-kpi-sub">{(account.equity > 0 ? (account.locked / account.equity * 100).toFixed(1) : "0")}% от капитала</div>
        </div>
        <div className="d-kpi">
          <div className="d-kpi-label">Свободно</div>
          <div className="d-kpi-value mono pos">{fmt(account.available)}</div>
          <div className="d-kpi-sub">для новых сделок</div>
        </div>
      </div>

      <div className="d-card" style={{ marginBottom: 16 }}>
        <div className="d-card-head">
          <div>
            <div className="d-card-title">Equity curve</div>
            <div className="d-card-sub">Капитал во времени · {exchange.toUpperCase()}</div>
          </div>
          <div className="d-seg">
            {[7, 30, 90, 365].map(d => (
              <button key={d} className={"d-seg-btn" + (equityPeriod === d ? " active" : "")}
                      onClick={() => setEquityPeriod(d)}>
                {d === 365 ? "1Y" : d + "д"}
              </button>
            ))}
          </div>
        </div>
        <div style={{ padding: 12 }}>
          {equityPts.length === 0 ? (
            <div className="d-empty" style={{ padding: 30 }}>
              <div className="d-empty-msg">Снепшоты собираются раз в час. Подожди немного и вернись.</div>
            </div>
          ) : (
            <div ref={chartRef} className="d-chart-holder"/>
          )}
        </div>
      </div>

      <div className="d-card">
        <div className="d-card-head">
          <div className="d-card-title">Открытые позиции <span className="d-tab-count">{positions.length}</span></div>
        </div>
        {positions.length === 0 ? (
          <div className="d-empty">
            <div className="d-empty-icon"><DIcon name="chart" size={26}/></div>
            <div className="d-empty-title">Нет открытых позиций</div>
            <div className="d-empty-msg">Сделки появятся здесь как только бот или ты лично откроете позицию на {exchange.toUpperCase()}.</div>
          </div>
        ) : (
          <div style={{ overflowX: "auto" }}>
            <table className="d-table">
              <thead>
                <tr>
                  <th>Символ</th><th>Сторона</th><th style={{ textAlign: "right" }}>Размер</th>
                  <th style={{ textAlign: "right" }}>Вход</th><th style={{ textAlign: "right" }}>Mark</th>
                  <th style={{ textAlign: "right" }}>Маржа</th><th>Плечо</th>
                  <th style={{ textAlign: "right" }}>Liq.</th>
                  <th style={{ textAlign: "right" }}>PnL</th>
                </tr>
              </thead>
              <tbody>
                {positions.map((p, i) => {
                  const sym = String(p.symbol || "").toUpperCase();
                  const side = String(p.side || "").toUpperCase();
                  const upnl = Number(p.unrealizePnl || 0);
                  const entry = Number(p.entryPrice || 0);
                  const mark = Number(p.markPrice || 0);
                  const pctPnl = entry > 0 ? ((mark - entry) / entry * 100 * (side === "LONG" ? 1 : -1)) : 0;
                  return (
                    <tr key={i} onClick={() => setSelectedPos(p)} style={{ cursor: "pointer" }}>
                      <td>
                        <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
                          <Coin sym={sym}/>
                          <span style={{ fontWeight: 600 }}>{sym}</span>
                        </div>
                      </td>
                      <td><span className={"d-badge " + (side === "LONG" ? "d-badge-long" : "d-badge-short")}>{side}</span></td>
                      <td className="mono" style={{ textAlign: "right" }}>{fmt(p.size, 4)}</td>
                      <td className="mono" style={{ textAlign: "right" }}>{fmtPx(entry)}</td>
                      <td className="mono" style={{ textAlign: "right" }}>{fmtPx(mark)}</td>
                      <td className="mono" style={{ textAlign: "right" }}>{fmt(p.margin)}</td>
                      <td><span className="d-badge">{Number(p.leverage || 1).toFixed(0)}x</span></td>
                      <td className="mono" style={{ textAlign: "right", color: "var(--d-danger)" }}>{p.liquidatePrice ? fmtPx(p.liquidatePrice) : "—"}</td>
                      <td className="mono" style={{ textAlign: "right" }}>
                        <div className={pctClass(upnl)} style={{ fontWeight: 700 }}>{upnl >= 0 ? "+" : ""}{fmt(upnl)}</div>
                        <div className={"mono " + pctClass(pctPnl)} style={{ fontSize: 10 }}>{fmtPct(pctPnl)}</div>
                      </td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
        )}
      </div>

      {/* Открытые ордера (лимит/триггер) */}
      <DashboardOrdersBlock orders={orders}/>

      {/* Sidebar-row: Long/Short split + Leaderboard */}
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16, marginTop: 16 }}>
        <LongShortSplitBlock exchange={exchange} exReady={exReady} refreshTick={refreshTick}/>
        <SymbolLeaderboardBlock exchange={exchange} exReady={exReady} refreshTick={refreshTick}/>
      </div>

      {/* Daily PnL bars */}
      <DailyPnlBarsBlock exchange={exchange} exReady={exReady} refreshTick={refreshTick}/>

      {/* Закрытые сделки */}
      <ClosedTradesBlock exchange={exchange} exReady={exReady} refreshTick={refreshTick}/>

      {/* Position detail modal с калькулятором и close */}
      {selectedPos && (
        <PositionDetailModal
          pos={selectedPos}
          exchange={exchange}
          onClose={() => setSelectedPos(null)}
          onClosed={() => { setSelectedPos(null); reload(); pushToast({ msg: "Позиция закрыта", ok: true }); }}
          pushToast={pushToast}
        />
      )}
    </div>
  );
};

// ─────────────────────  Position detail с калькулятором  ─────────────────────
const PositionDetailModal = ({ pos, exchange, onClose, onClosed, pushToast }) => {
  const sym = String(pos.symbol || "").toUpperCase();
  const side = String(pos.side || "").toUpperCase();
  const isLong = side === "LONG";
  const entry = Number(pos.entryPrice || 0);
  const mark = Number(pos.markPrice || 0);
  const size = Number(pos.size || 0);
  const lev = Number(pos.leverage || 1);
  const margin = Number(pos.margin || (entry * size / lev));
  const liq = Number(pos.liquidatePrice || 0);

  const [calcPx, setCalcPx] = useState(mark);
  const [closing, setClosing] = useState(false);

  const presetDelta = (pct) => setCalcPx(mark * (1 + pct / 100));

  const calcPnlAbs = (entry > 0 ? (calcPx - entry) * size * (isLong ? 1 : -1) : 0);
  const calcPnlPct = (entry > 0 ? (calcPx - entry) / entry * 100 * (isLong ? 1 : -1) : 0);
  const calcRoi = margin > 0 ? (calcPnlAbs / margin * 100) : 0;
  const calcMove = (mark > 0 ? (calcPx - mark) / mark * 100 : 0);

  const closePos = async () => {
    if (!confirm("Закрыть позицию " + sym + " " + side + " по рынку?")) return;
    setClosing(true);
    try {
      await window.MobileAPI.closePosition(exchange, { symbol: sym, side, qty: size });
      onClosed();
    } catch (e) {
      pushToast({ msg: "Ошибка закрытия: " + (e.message || e).slice(0, 100), err: true });
      setClosing(false);
    }
  };

  return (
    <div className="d-modal-backdrop" onClick={onClose}>
      <div className="d-modal wide" onClick={e => e.stopPropagation()}>
        <div className="d-modal-head">
          <div className="d-modal-title" style={{ display: "flex", alignItems: "center", gap: 10 }}>
            <Coin sym={sym} size="lg"/>
            <div>
              <div>{sym}</div>
              <div style={{ fontSize: 11, color: "var(--d-text-2)", fontWeight: 500 }}>
                <span className={"d-badge " + (isLong ? "d-badge-long" : "d-badge-short")}>{side}</span>
                <span className="d-badge" style={{ marginLeft: 6 }}>{lev}x</span>
              </div>
            </div>
          </div>
          <button className="d-icon-btn" onClick={onClose}><DIcon name="x" size={14}/></button>
        </div>
        <div className="d-modal-body d-pos-detail">
          {/* Left — current state */}
          <div>
            <div className="d-label">Текущая позиция</div>
            <div className="d-card" style={{ padding: 14 }}>
              <div className="d-stat"><span className="d-stat-label">Цена входа</span><span className="d-stat-val mono">{fmtPx(entry)}</span></div>
              <div className="d-stat"><span className="d-stat-label">Mark цена</span><span className="d-stat-val mono">{fmtPx(mark)}</span></div>
              <div className="d-stat"><span className="d-stat-label">Размер</span><span className="d-stat-val mono">{fmt(size, 4)}</span></div>
              <div className="d-stat"><span className="d-stat-label">Нотионал</span><span className="d-stat-val mono">{fmt(entry * size)} USDT</span></div>
              <div className="d-stat"><span className="d-stat-label">Маржа</span><span className="d-stat-val mono">{fmt(margin)} USDT</span></div>
              {liq > 0 && (
                <div className="d-stat">
                  <span className="d-stat-label">Цена ликвидации</span>
                  <span className="d-stat-val mono neg">{fmtPx(liq)}</span>
                </div>
              )}
              <div className="d-stat">
                <span className="d-stat-label">Текущий PnL</span>
                <span className={"d-stat-val mono " + pctClass(pos.unrealizePnl || 0)}>
                  {(pos.unrealizePnl || 0) >= 0 ? "+" : ""}{fmt(pos.unrealizePnl || 0)} USDT
                </span>
              </div>
            </div>

            <button className="d-btn d-btn-danger" onClick={closePos} disabled={closing}
                    style={{ width: "100%", marginTop: 14, padding: "10px 14px" }}>
              {closing ? <span className="d-spinner"/> : "🛑 Закрыть позицию по рынку"}
            </button>
          </div>

          {/* Right — calculator */}
          <div>
            <div className="d-label">Калькулятор «если цена будет X»</div>
            <div className="d-card" style={{ padding: 14 }}>
              <input className="d-input mono" type="number" step="any"
                     value={calcPx} onChange={e => setCalcPx(parseFloat(e.target.value) || 0)}
                     style={{ fontSize: 18, textAlign: "center", padding: "10px 14px" }}/>

              <div style={{ marginTop: 12 }}>
                <input type="range" className="d-slider"
                       min={mark * 0.7} max={mark * 1.3} step={mark * 0.001}
                       value={calcPx}
                       onChange={e => setCalcPx(parseFloat(e.target.value))}/>
                <div style={{ display: "flex", justifyContent: "space-between", fontSize: 10, color: "var(--d-text-3)", marginTop: 4 }}>
                  <span className="mono">-30%</span>
                  <span className="mono">mark</span>
                  <span className="mono">+30%</span>
                </div>
              </div>

              <div style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 5, marginTop: 12 }}>
                {[-10, -5, +5, +10].map(p => (
                  <button key={p} className="d-btn d-btn-sm" onClick={() => presetDelta(p)}>
                    {p > 0 ? "+" : ""}{p}%
                  </button>
                ))}
              </div>

              <div style={{ marginTop: 14, paddingTop: 12, borderTop: "1px solid var(--d-line)" }}>
                <div className="d-stat">
                  <span className="d-stat-label">Движение цены</span>
                  <span className={"d-stat-val mono " + pctClass(calcMove)}>{fmtPct(calcMove)}</span>
                </div>
                <div className="d-stat">
                  <span className="d-stat-label">PnL при этой цене</span>
                  <span className={"d-stat-val mono " + pctClass(calcPnlAbs)} style={{ fontSize: 16 }}>
                    {calcPnlAbs >= 0 ? "+" : ""}{fmt(calcPnlAbs)} USDT
                  </span>
                </div>
                <div className="d-stat">
                  <span className="d-stat-label">ROI на маржу</span>
                  <span className={"d-stat-val mono " + pctClass(calcRoi)} style={{ fontSize: 14 }}>
                    {fmtPct(calcRoi)}
                  </span>
                </div>
                <div className="d-stat">
                  <span className="d-stat-label">PnL % к входу</span>
                  <span className={"d-stat-val mono " + pctClass(calcPnlPct)}>{fmtPct(calcPnlPct)}</span>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
};

// ─────────────────────  Analytics Top Bar (Win Rate / TP / Slip / RR)  ─────────────────────
const AnalyticsTopBar = ({ refreshTick }) => {
  const [period, setPeriod] = useState(30);
  const [tps, setTps] = useState(null);
  const [slip, setSlip] = useState(null);
  const [channels, setChannels] = useState(null);

  useEffect(() => {
    (async () => {
      try {
        const [t, s, c] = await Promise.all([
          window.MobileAPI.loadTpStats(period),
          window.MobileAPI.loadSlippageStats(period),
          window.MobileAPI.loadChannelStats(period),
        ]);
        setTps(t); setSlip(s); setChannels(c || []);
      } catch {}
    })();
  }, [period, refreshTick]);

  const t = tps || {};
  const total = Number(t.total || 0);
  const tpHits = Array.isArray(t.tp_hits) ? t.tp_hits : [0, 0, 0];
  const tp1 = total > 0 ? (tpHits[0] / total * 100) : 0;
  const tp2 = total > 0 ? (tpHits[1] / total * 100) : 0;
  const tp3 = total > 0 ? (tpHits[2] / total * 100) : 0;
  const slBeforeTp1 = Number(t.sl_before_tp1_rate || 0);

  // Win Rate / RR aggregate из channelStats
  const chStats = Array.isArray(channels) ? channels : [];
  const totalTrades = chStats.reduce((a, c) => a + Number(c.count || 0), 0);
  const totalPnl = chStats.reduce((a, c) => a + Number(c.total_pnl || 0), 0);
  const avgRr = chStats.length > 0
    ? chStats.reduce((a, c) => a + Number(c.rr || 0), 0) / chStats.length : 0;
  const avgWinRate = chStats.length > 0
    ? chStats.reduce((a, c) => a + Number(c.winrate || 0), 0) / chStats.length : 0;

  // Slippage
  const slipTotal = Number(slip?.total_slip_usdt || 0);
  const slipAvg = Number(slip?.avg_slip_per_trade || 0);

  return (
    <div className="d-card" style={{ marginBottom: 16, overflow: "visible" }}>
      <div className="d-card-head">
        <div className="d-card-title"><DIcon name="chart" size={14}/> Аналитика (сигналы)</div>
        <div className="d-seg">
          {[7, 30, 90, 365].map(d => (
            <button key={d} className={"d-seg-btn" + (period === d ? " active" : "")} onClick={() => setPeriod(d)}>
              {d === 365 ? "1Y" : d + "д"}
            </button>
          ))}
        </div>
      </div>
      <div style={{ padding: 14, display: "grid", gridTemplateColumns: "repeat(6, 1fr)", gap: 14 }}>
        <AnalyticsBlock label="Сделок" val={totalTrades || total} sub={`${period}д период`}/>
        <AnalyticsBlock label="Win Rate" val={(avgWinRate || 0).toFixed(1) + "%"}
                        color={avgWinRate >= 50 ? "var(--d-success)" : "var(--d-danger)"}
                        sub={`avg по каналам`}/>
        <AnalyticsBlock label="Net PnL" val={(totalPnl >= 0 ? "+" : "") + fmt(totalPnl)}
                        color={totalPnl >= 0 ? "var(--d-success)" : "var(--d-danger)"}
                        sub="за период"/>
        <AnalyticsBlock label="Avg R:R" val={avgRr.toFixed(2)}
                        color={avgRr >= 1.5 ? "var(--d-success)" : avgRr >= 1 ? "var(--d-warn)" : "var(--d-danger)"}
                        sub="reward/risk"/>
        <AnalyticsBlock label="TP1 / TP2 / TP3"
                        val={<span style={{ fontSize: 16 }}>
                          <span className="pos">{tp1.toFixed(0)}</span>
                          <span className="muted"> / </span>
                          <span className="pos">{tp2.toFixed(0)}</span>
                          <span className="muted"> / </span>
                          <span className="pos">{tp3.toFixed(0)}%</span>
                        </span>}
                        sub={`SL до TP1: ${slBeforeTp1}%`}/>
        <AnalyticsBlock label="Slippage" val={"−" + fmt(slipTotal, 2)}
                        color="var(--d-danger)"
                        sub={`avg ${fmt(slipAvg, 2)}/сделку`}/>
      </div>
    </div>
  );
};

const AnalyticsBlock = ({ label, val, sub, color }) => (
  <div>
    <div className="d-kpi-label" style={{ fontSize: 10 }}>{label}</div>
    <div className="mono" style={{ fontSize: 20, fontWeight: 700, marginTop: 4, color: color || "var(--d-text-0)" }}>{val}</div>
    {sub && <div className="muted" style={{ fontSize: 10, marginTop: 2 }}>{sub}</div>}
  </div>
);

// ─────────────────────  Dashboard под-блоки  ─────────────────────

const RiskWidget = ({ marginRatio, liqDistance, totalNotional, equity, positions }) => {
  const ratio = Math.min(1, marginRatio);
  const riskLevel = ratio < 0.3 ? "safe" : ratio < 0.6 ? "warn" : "danger";
  const liqLevel = liqDistance > 20 ? "safe" : liqDistance > 10 ? "warn" : "danger";
  const color = (lvl) => lvl === "safe" ? "var(--d-success)" : lvl === "warn" ? "var(--d-warn)" : "var(--d-danger)";

  return (
    <div className="d-card" style={{ padding: 14, marginBottom: 16,
      background: "linear-gradient(135deg, var(--d-bg-1), var(--d-bg-2))" }}>
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 14, alignItems: "center" }}>
        <div>
          <div className="d-kpi-label"><DIcon name="shield" size={12} style={{ verticalAlign: "-2px" }}/> Margin Ratio</div>
          <div className="d-kpi-value mono" style={{ fontSize: 22, color: color(riskLevel) }}>
            {(ratio * 100).toFixed(1)}%
          </div>
          <div style={{ height: 4, background: "var(--d-bg-3)", borderRadius: 2, marginTop: 8, overflow: "hidden" }}>
            <div style={{ height: "100%", width: (ratio * 100) + "%", background: color(riskLevel), transition: "width 0.3s" }}/>
          </div>
          <div className="muted" style={{ fontSize: 10, marginTop: 4 }}>≥80% = warning</div>
        </div>
        <div>
          <div className="d-kpi-label"><DIcon name="warning" size={12} style={{ verticalAlign: "-2px" }}/> Min liq distance</div>
          <div className="d-kpi-value mono" style={{ fontSize: 22, color: color(liqLevel) }}>
            {liqDistance > 50 ? "50%+" : liqDistance.toFixed(1) + "%"}
          </div>
          <div className="muted" style={{ fontSize: 10, marginTop: 8 }}>до ликвидации ближайшей позиции</div>
        </div>
        <div>
          <div className="d-kpi-label"><DIcon name="chart" size={12} style={{ verticalAlign: "-2px" }}/> Total exposure</div>
          <div className="d-kpi-value mono" style={{ fontSize: 22 }}>
            {fmt(totalNotional, 0)} USDT
          </div>
          <div className="muted" style={{ fontSize: 10, marginTop: 8 }}>
            эфф. плечо {equity > 0 ? (totalNotional / equity).toFixed(2) : "0"}x
          </div>
        </div>
      </div>
    </div>
  );
};

const DashSpotBlock = ({ exchange, exReady, refreshTick }) => {
  const [balances, setBalances] = useState([]);
  const [loading, setLoading] = useState(true);
  useEffect(() => {
    if (!exReady[exchange]) { setBalances([]); setLoading(false); return; }
    setLoading(true);
    (async () => {
      try {
        const r = await window.MobileAPI.loadSpotBalance(exchange);
        // Гарантируем массив: API может вернуть {balances: [...]} или сразу []
        const arr = Array.isArray(r) ? r
          : Array.isArray(r?.balances) ? r.balances
          : Array.isArray(r?.data) ? r.data
          : [];
        setBalances(arr);
      } catch { setBalances([]); }
      setLoading(false);
    })();
  }, [exchange, exReady, refreshTick]);
  const total = Array.isArray(balances)
    ? balances.reduce((s, b) => s + Number(b.valueUSDT || 0), 0)
    : 0;
  if (!exReady[exchange]) {
    return <div className="d-card d-empty"><div className="d-empty-msg">Нет API-ключа для {exchange.toUpperCase()}</div></div>;
  }
  return (
    <div className="d-card" style={{ marginBottom: 16 }}>
      <div className="d-card-head">
        <div className="d-card-title"><DIcon name="spot" size={14}/> Спот-балансы <span className="d-card-sub">{exchange.toUpperCase()}</span></div>
        <div className="mono" style={{ fontSize: 16, fontWeight: 700 }}>≈ {fmt(total)} USDT</div>
      </div>
      {loading ? <div style={{ padding: 16 }}><Skel h={120}/></div>
        : balances.length === 0 ? <div className="d-empty"><div className="d-empty-msg">Спот баланс пуст</div></div>
        : (
          <table className="d-table">
            <thead><tr><th>Актив</th><th style={{textAlign:"right"}}>Free</th><th style={{textAlign:"right"}}>Locked</th><th style={{textAlign:"right"}}>USDT</th></tr></thead>
            <tbody>
              {balances.filter(b => Number(b.total) > 0).slice(0, 10).map((b, i) => (
                <tr key={i}>
                  <td><div style={{ display: "flex", alignItems: "center", gap: 8 }}><Coin sym={b.asset}/><span style={{ fontWeight: 600 }}>{b.asset}</span></div></td>
                  <td className="mono" style={{ textAlign: "right" }}>{fmt(b.free, 6)}</td>
                  <td className="mono muted" style={{ textAlign: "right" }}>{fmt(b.locked, 6)}</td>
                  <td className="mono" style={{ textAlign: "right", fontWeight: 600 }}>${fmt(b.valueUSDT)}</td>
                </tr>
              ))}
            </tbody>
          </table>
        )}
    </div>
  );
};

const DashBotsBlock = ({ exchange, refreshTick, pushToast }) => {
  const [bots, setBots] = useState([]);
  const [loading, setLoading] = useState(true);
  useEffect(() => {
    setLoading(true);
    (async () => {
      try {
        const r = await window.MobileAPI.listLocalBots();
        setBots(Array.isArray(r) ? r : []);
      } catch {}
      setLoading(false);
    })();
  }, [refreshTick]);
  const ACTIVE = ["running", "paused", "starting"];
  const active = bots.filter(b => ACTIVE.includes(b.state));
  if (loading) return <div className="d-card d-card-pad"><Skel h={200}/></div>;
  if (active.length === 0) {
    return (
      <div className="d-card">
        <div className="d-empty">
          <div className="d-empty-icon"><DIcon name="bot" size={26}/></div>
          <div className="d-empty-title">Нет активных ботов</div>
          <div className="d-empty-msg">Создай Grid / Smart-DCA / Basket — открой раздел Боты в сайдбаре.</div>
        </div>
      </div>
    );
  }
  return (
    <div className="d-card" style={{ marginBottom: 16 }}>
      <div className="d-card-head">
        <div className="d-card-title"><DIcon name="robot" size={14}/> Активные боты <span className="d-tab-count">{active.length}</span></div>
      </div>
      <table className="d-table">
        <thead><tr><th>Бот</th><th>Тип</th><th>Биржа</th><th style={{textAlign:"right"}}>PnL</th><th style={{textAlign:"right"}}>uPnL</th><th style={{textAlign:"right"}}>Liq</th></tr></thead>
        <tbody>
          {active.map(b => {
            const upnl = b.live?.unrealized_pnl;
            const realized = Number(b.total_pnl) || 0;
            const total = (upnl != null ? realized + upnl : realized);
            return (
              <tr key={b.id}>
                <td><div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                  <Coin sym={b.symbol}/><span style={{ fontWeight: 600 }}>{b.symbol}</span>
                </div></td>
                <td><span className="d-badge">{(b.bot_type || "").toUpperCase()}</span></td>
                <td><span className="d-badge">{(b.exchange || "").toUpperCase()}</span></td>
                <td className={"mono " + pctClass(total)} style={{ textAlign: "right", fontWeight: 700 }}>
                  {total >= 0 ? "+" : ""}{fmt(total)}
                </td>
                <td className={"mono " + pctClass(upnl || 0)} style={{ textAlign: "right" }}>
                  {upnl != null ? ((upnl >= 0 ? "+" : "") + fmt(upnl)) : "—"}
                </td>
                <td className="mono" style={{ textAlign: "right", color: "var(--d-danger)" }}>
                  {b.live?.liquidation_price > 0 ? fmtPx(b.live.liquidation_price) : "—"}
                </td>
              </tr>
            );
          })}
        </tbody>
      </table>
    </div>
  );
};

const DashSignalsLent = ({ refreshTick }) => {
  const [trades, setTrades] = useState([]);
  const [loading, setLoading] = useState(true);
  useEffect(() => {
    setLoading(true);
    (async () => {
      try {
        const r = await window.MobileAPI.loadTrades?.({ limit: 30 }) || [];
        setTrades(Array.isArray(r) ? r.filter(t => t.source === "signal" || t.source === "telegram" || t.signal_source) : []);
      } catch {}
      setLoading(false);
    })();
  }, [refreshTick]);
  if (loading) return <div className="d-card d-card-pad"><Skel h={200}/></div>;
  if (trades.length === 0) {
    return (
      <div className="d-card">
        <div className="d-empty">
          <div className="d-empty-icon"><DIcon name="signal" size={26}/></div>
          <div className="d-empty-title">Нет распарсенных сигналов</div>
          <div className="d-empty-msg">Listener сигналов либо не активен либо в подписанных каналах не было сообщений за период.</div>
        </div>
      </div>
    );
  }
  return (
    <div className="d-card" style={{ marginBottom: 16 }}>
      <div className="d-card-head">
        <div className="d-card-title"><DIcon name="signal" size={14}/> Live signal pipeline <span className="d-tab-count">{trades.length}</span></div>
        <div className="d-card-sub">последние 30 событий</div>
      </div>
      <table className="d-table">
        <thead><tr><th>Время</th><th>Символ</th><th>Сторона</th><th>Источник</th><th style={{textAlign:"right"}}>Entry</th><th>Статус</th><th style={{textAlign:"right"}}>PnL</th></tr></thead>
        <tbody>
          {trades.map((t, i) => {
            const pnl = Number(t.pnl || 0);
            const side = String(t.side || t.direction || "").toUpperCase();
            return (
              <tr key={i}>
                <td className="muted mono" style={{ fontSize: 11 }}>{ago(t.opened_at || t.created_at)}</td>
                <td><div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                  <Coin sym={t.symbol}/><span style={{ fontWeight: 600 }}>{t.symbol}</span>
                </div></td>
                <td><span className={"d-badge " + (side === "LONG" ? "d-badge-long" : "d-badge-short")}>{side}</span></td>
                <td className="muted" style={{ fontSize: 11 }}>{t.signal_source || t.source || "—"}</td>
                <td className="mono" style={{ textAlign: "right" }}>{fmtPx(t.entry_price)}</td>
                <td>
                  {t.status === "closed" && <span className="d-badge d-badge-long">closed</span>}
                  {t.status === "open" && <span className="d-badge d-badge-running">open</span>}
                  {t.status === "failed" && <span className="d-badge d-badge-short">failed</span>}
                  {!["closed","open","failed"].includes(t.status) && <span className="d-badge">{t.status}</span>}
                </td>
                <td className={"mono " + pctClass(pnl)} style={{ textAlign: "right", fontWeight: 700 }}>
                  {t.exit_price ? ((pnl >= 0 ? "+" : "") + fmt(pnl)) : "—"}
                </td>
              </tr>
            );
          })}
        </tbody>
      </table>
    </div>
  );
};

const DashboardOrdersBlock = ({ orders }) => {
  if (!orders || orders.length === 0) return null;
  return (
    <div className="d-card" style={{ marginTop: 16 }}>
      <div className="d-card-head">
        <div className="d-card-title">Активные ордера <span className="d-tab-count">{orders.length}</span></div>
      </div>
      <div style={{ overflowX: "auto" }}>
        <table className="d-table">
          <thead>
            <tr>
              <th>Символ</th><th>Тип</th><th>Сторона</th>
              <th style={{ textAlign: "right" }}>Цена</th>
              <th style={{ textAlign: "right" }}>Количество</th>
              <th style={{ textAlign: "right" }}>Filled</th>
              <th>Статус</th>
            </tr>
          </thead>
          <tbody>
            {orders.map((o, i) => {
              const side = String(o.side || "").toUpperCase();
              const type = String(o.type || "LIMIT").toUpperCase();
              return (
                <tr key={i}>
                  <td>
                    <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                      <Coin sym={o.symbol}/>
                      <span style={{ fontWeight: 600 }}>{String(o.symbol || "").toUpperCase()}</span>
                    </div>
                  </td>
                  <td><span className="d-badge">{type}</span></td>
                  <td><span className={"d-badge " + (side === "BUY" || side === "LONG" ? "d-badge-long" : "d-badge-short")}>{side}</span></td>
                  <td className="mono" style={{ textAlign: "right" }}>{fmtPx(o.price)}</td>
                  <td className="mono" style={{ textAlign: "right" }}>{fmt(o.qty, 4)}</td>
                  <td className="mono" style={{ textAlign: "right", color: "var(--d-text-2)" }}>{fmt(o.filled, 4)}</td>
                  <td><span className="d-badge">{o.status || "NEW"}</span></td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>
    </div>
  );
};

const LongShortSplitBlock = ({ exchange, exReady, refreshTick }) => {
  const [stats, setStats] = useState(null);
  useEffect(() => {
    if (!exReady[exchange]) { setStats(null); return; }
    (async () => {
      try {
        const r = await window.MobileAPI.loadAnalytics(exchange, 30);
        setStats(r);
      } catch {}
    })();
  }, [exchange, exReady, refreshTick]);
  if (!stats) return null;
  const longPnl = Number(stats.long_pnl || 0);
  const shortPnl = Number(stats.short_pnl || 0);
  const longCnt = stats.long_count || 0;
  const shortCnt = stats.short_count || 0;
  return (
    <div className="d-card">
      <div className="d-card-head">
        <div className="d-card-title">Long / Short анализ <span className="d-card-sub">30д</span></div>
      </div>
      <div className="d-card-pad" style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 14 }}>
        <div style={{ padding: 14, borderRadius: 10, background: "var(--d-success-dim)", border: "1px solid rgba(0,193,118,0.3)" }}>
          <div className="d-kpi-label" style={{ color: "var(--d-success)" }}>↑ LONG</div>
          <div className={"d-kpi-value mono " + pctClass(longPnl)} style={{ fontSize: 22 }}>
            {longPnl >= 0 ? "+" : ""}{fmt(longPnl)}
          </div>
          <div className="muted" style={{ fontSize: 11 }}>{longCnt} сделок</div>
        </div>
        <div style={{ padding: 14, borderRadius: 10, background: "var(--d-danger-dim)", border: "1px solid rgba(255,71,89,0.3)" }}>
          <div className="d-kpi-label" style={{ color: "var(--d-danger)" }}>↓ SHORT</div>
          <div className={"d-kpi-value mono " + pctClass(shortPnl)} style={{ fontSize: 22 }}>
            {shortPnl >= 0 ? "+" : ""}{fmt(shortPnl)}
          </div>
          <div className="muted" style={{ fontSize: 11 }}>{shortCnt} сделок</div>
        </div>
      </div>
    </div>
  );
};

const SymbolLeaderboardBlock = ({ exchange, exReady, refreshTick }) => {
  const [top, setTop] = useState([]);
  useEffect(() => {
    if (!exReady[exchange]) { setTop([]); return; }
    (async () => {
      try {
        const r = await window.MobileAPI.loadLeaderboard(exchange, 30);
        const arr = Array.isArray(r) ? r : (r?.items || []);
        setTop(arr.slice(0, 7));
      } catch {}
    })();
  }, [exchange, exReady, refreshTick]);
  return (
    <div className="d-card">
      <div className="d-card-head">
        <div className="d-card-title"><DIcon name="crown" size={14}/> Топ символы <span className="d-card-sub">30д</span></div>
      </div>
      <div className="d-card-pad">
        {top.length === 0 ? (
          <div className="muted" style={{ fontSize: 12 }}>Нет данных за период</div>
        ) : top.map((t, i) => {
          const pnl = Number(t.pnl || t.total_pnl || 0);
          return (
            <div key={i} style={{ display: "flex", alignItems: "center", gap: 10, padding: "8px 0",
              borderBottom: i < top.length - 1 ? "1px solid var(--d-line-soft)" : "none" }}>
              <div className="mono muted" style={{ width: 16, fontSize: 11 }}>{i + 1}</div>
              <Coin sym={t.symbol}/>
              <div style={{ flex: 1, fontWeight: 600, fontSize: 12 }}>{(t.symbol || "").toUpperCase()}</div>
              <div className="muted mono" style={{ fontSize: 11 }}>{t.count || 0}</div>
              <div className={"mono " + pctClass(pnl)} style={{ fontWeight: 700, fontSize: 13, minWidth: 70, textAlign: "right" }}>
                {pnl >= 0 ? "+" : ""}{fmt(pnl)}
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
};

const DailyPnlBarsBlock = ({ exchange, exReady, refreshTick }) => {
  const [days, setDays] = useState([]);
  useEffect(() => {
    if (!exReady[exchange]) { setDays([]); return; }
    (async () => {
      try {
        const r = await window.MobileAPI.loadDailyPnl(exchange, 30);
        setDays(Array.isArray(r) ? r : (r?.days || []));
      } catch {}
    })();
  }, [exchange, exReady, refreshTick]);
  if (!days || days.length === 0) return null;
  const max = Math.max(...days.map(d => Math.abs(Number(d.pnl || 0))), 1);
  return (
    <div className="d-card" style={{ marginTop: 16 }}>
      <div className="d-card-head">
        <div className="d-card-title"><DIcon name="chart" size={14}/> PnL по дням <span className="d-card-sub">последние 30</span></div>
      </div>
      <div className="d-card-pad">
        <div style={{ display: "flex", alignItems: "flex-end", gap: 4, height: 100 }}>
          {days.slice(-30).map((d, i) => {
            const v = Number(d.pnl || 0);
            const h = Math.max(2, Math.abs(v) / max * 90);
            return (
              <div key={i}
                title={`${d.date}: ${v >= 0 ? "+" : ""}${fmt(v)} USDT`}
                style={{ flex: 1, height: h,
                  background: v >= 0
                    ? "linear-gradient(180deg, var(--d-success-2), var(--d-success))"
                    : "linear-gradient(180deg, var(--d-danger-2), var(--d-danger))",
                  borderRadius: "4px 4px 0 0",
                  boxShadow: "0 0 8px " + (v >= 0 ? "rgba(0,193,118,0.4)" : "rgba(255,71,89,0.4)"),
                  cursor: "pointer",
                  transition: "filter 0.15s",
                }}
                onMouseEnter={e => e.currentTarget.style.filter = "brightness(1.3)"}
                onMouseLeave={e => e.currentTarget.style.filter = "none"}
              />
            );
          })}
        </div>
      </div>
    </div>
  );
};

const ClosedTradesBlock = ({ exchange, exReady, refreshTick }) => {
  const [trades, setTrades] = useState([]);
  const [showAll, setShowAll] = useState(false);
  useEffect(() => {
    if (!exReady[exchange]) { setTrades([]); return; }
    (async () => {
      try {
        const r = await window.MobileAPI.loadClosedTrades(exchange, 30);
        setTrades(Array.isArray(r) ? r : (r?.trades || []));
      } catch {}
    })();
  }, [exchange, exReady, refreshTick]);
  if (!trades || trades.length === 0) return null;
  const list = showAll ? trades : trades.slice(0, 8);
  return (
    <div className="d-card" style={{ marginTop: 16 }}>
      <div className="d-card-head">
        <div className="d-card-title">Закрытые сделки биржи <span className="d-tab-count">{trades.length}</span></div>
      </div>
      <div style={{ overflowX: "auto" }}>
        <table className="d-table">
          <thead>
            <tr>
              <th>Закрыта</th><th>Символ</th><th>Сторона</th>
              <th style={{ textAlign: "right" }}>Вход</th>
              <th style={{ textAlign: "right" }}>Выход</th>
              <th style={{ textAlign: "right" }}>Размер</th>
              <th style={{ textAlign: "right" }}>PnL</th>
            </tr>
          </thead>
          <tbody>
            {list.map((t, i) => {
              const pnl = Number(t.pnl || t.realizedPnl || 0);
              const side = String(t.side || "").toUpperCase();
              return (
                <tr key={i}>
                  <td className="muted mono" style={{ fontSize: 11 }}>{ago(t.closed_at || t.time)}</td>
                  <td>
                    <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                      <Coin sym={t.symbol}/>
                      <span style={{ fontWeight: 600 }}>{String(t.symbol || "").toUpperCase()}</span>
                    </div>
                  </td>
                  <td><span className={"d-badge " + (side === "LONG" ? "d-badge-long" : "d-badge-short")}>{side}</span></td>
                  <td className="mono" style={{ textAlign: "right" }}>{fmtPx(t.entry_price || t.entryPrice)}</td>
                  <td className="mono" style={{ textAlign: "right" }}>{fmtPx(t.exit_price || t.closePrice)}</td>
                  <td className="mono" style={{ textAlign: "right" }}>{fmt(t.qty || t.size, 4)}</td>
                  <td className={"mono " + pctClass(pnl)} style={{ textAlign: "right", fontWeight: 700 }}>
                    {pnl >= 0 ? "+" : ""}{fmt(pnl)}
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>
      {trades.length > 8 && (
        <div style={{ padding: 10, textAlign: "center", borderTop: "1px solid var(--d-line)" }}>
          <button className="d-btn d-btn-ghost" onClick={() => setShowAll(v => !v)}>
            {showAll ? "Свернуть" : `Показать все ${trades.length}`}
          </button>
        </div>
      )}
    </div>
  );
};

// ═══════════════════════════════════════════════════════════════════════════════
//                                BOTS SCREEN
// ═══════════════════════════════════════════════════════════════════════════════
const BotsScreen = ({ exchange, exReady, pushToast, refreshTick }) => {
  const [topTab, setTopTab] = useState("local");   // local | exchange
  const [bots, setBots] = useState([]);
  const [tab, setTab] = useState("active");
  const [loading, setLoading] = useState(true);
  const [createOpen, setCreateOpen] = useState(false);
  const [detailsBotId, setDetailsBotId] = useState(null);
  // Exchange bots (OKX/etc)
  const [exBots, setExBots] = useState({ active: [], history: [] });
  const [exLoading, setExLoading] = useState(false);
  useEffect(() => {
    if (topTab !== "exchange") return;
    setExLoading(true);
    (async () => {
      try {
        const [a, h] = await Promise.all([
          window.MobileAPI.loadBotsActive(exchange).catch(() => []),
          window.MobileAPI.loadBotsHistory(exchange, 30).catch(() => []),
        ]);
        setExBots({ active: Array.isArray(a) ? a : [], history: Array.isArray(h) ? h : [] });
      } catch {}
      setExLoading(false);
    })();
  }, [topTab, exchange, refreshTick]);

  const load = useCallback(async () => {
    setLoading(true);
    try {
      const r = await window.MobileAPI.listLocalBots();
      setBots(r || []);
    } catch (e) { console.warn(e); }
    setLoading(false);
  }, []);
  useEffect(() => { load(); }, [load, refreshTick]);

  const ACTIVE = ["running", "paused", "starting"];
  const active = bots.filter(b => ACTIVE.includes(b.state));
  const history = bots.filter(b => !ACTIVE.includes(b.state));
  const list = tab === "active" ? active : history;

  const totalLivePnl = active.reduce((s, b) => {
    const upnl = b.live?.unrealized_pnl || 0;
    return s + (Number(b.total_pnl) || 0) + upnl;
  }, 0);

  const toggleBot = async (b) => {
    try {
      if (b.state === "running") await window.MobileAPI.stopLocalBot(b.id);
      else await window.MobileAPI.startLocalBot(b.id);
      await load();
      pushToast({ msg: b.state === "running" ? "Бот остановлен" : "Бот запущен", ok: true });
    } catch (e) { pushToast({ msg: "Ошибка: " + (e.message || e).slice(0, 80), err: true }); }
  };
  const removeBot = async (b) => {
    if (!confirm("Удалить бота " + b.symbol + "?")) return;
    try { await window.MobileAPI.deleteLocalBot(b.id); await load(); pushToast({ msg: "Удалён", ok: true }); }
    catch { pushToast({ msg: "Ошибка удаления", err: true }); }
  };

  return (
    <div>
      {/* Top-level: API4ATKA vs Exchange Bots */}
      <div className="d-tabs">
        <button className={"d-tab" + (topTab === "local" ? " active" : "")} onClick={() => setTopTab("local")}>
          <DIcon name="robot" size={14} style={{ verticalAlign: "-2px" }}/> Боты API4ATKA <span className="d-tab-count">{active.length + history.length}</span>
        </button>
        <button className={"d-tab" + (topTab === "exchange" ? " active" : "")} onClick={() => setTopTab("exchange")}>
          <DIcon name="wallet" size={14} style={{ verticalAlign: "-2px" }}/> Биржевые боты ({exchange.toUpperCase()}) <span className="d-tab-count">{exBots.active.length + exBots.history.length}</span>
        </button>
      </div>

      {topTab === "exchange" && (
        <ExchangeBotsBlock exchange={exchange} bots={exBots} loading={exLoading}/>
      )}

      {topTab === "local" && <>
      <div className="d-kpi-grid" style={{ marginBottom: 16 }}>
        <div className="d-kpi">
          <div className="d-kpi-label">Активных ботов</div>
          <div className="d-kpi-value mono">{active.length}</div>
          <div className="d-kpi-sub">{active.filter(b => b.bot_type === "grid").length} Grid · {active.filter(b => b.bot_type === "dca").length} DCA · {active.filter(b => b.bot_type === "recurring").length} Basket</div>
        </div>
        <div className="d-kpi">
          <div className="d-kpi-label">Общий PnL</div>
          <div className={"d-kpi-value mono " + pctClass(totalLivePnl)}>{totalLivePnl >= 0 ? "+" : ""}{fmt(totalLivePnl)}</div>
          <div className="d-kpi-sub">realized + unrealized</div>
        </div>
        <div className="d-kpi">
          <div className="d-kpi-label">История</div>
          <div className="d-kpi-value mono">{history.length}</div>
          <div className="d-kpi-sub">остановленных ботов</div>
        </div>
        <div className="d-kpi" style={{ display: "flex", flexDirection: "column", justifyContent: "center" }}>
          <button className="d-btn d-btn-primary" style={{ width: "100%", padding: "10px 14px", fontSize: 13 }}
                  onClick={() => setCreateOpen(true)}>
            <DIcon name="plus" size={14}/> Создать бота
          </button>
          <div className="muted" style={{ fontSize: 10, marginTop: 8, textAlign: "center" }}>Grid · DCA · Basket</div>
        </div>
      </div>

      <div className="d-tabs">
        <button className={"d-tab" + (tab === "active" ? " active" : "")} onClick={() => setTab("active")}>
          Активные <span className="d-tab-count">{active.length}</span>
        </button>
        <button className={"d-tab" + (tab === "history" ? " active" : "")} onClick={() => setTab("history")}>
          История <span className="d-tab-count">{history.length}</span>
        </button>
      </div>

      {loading ? (
        <div className="d-card d-card-pad">
          {Array.from({ length: 3 }).map((_, i) => (
            <div key={i} style={{ padding: 12, borderBottom: "1px solid var(--d-line-soft)", display: "flex", gap: 16 }}>
              <Skel w={32} h={32} style={{ borderRadius: 8 }}/>
              <div style={{ flex: 1 }}>
                <Skel w={140} h={14}/>
                <div style={{ marginTop: 8 }}><Skel w={220} h={10}/></div>
              </div>
              <Skel w={80} h={28} style={{ borderRadius: 6 }}/>
            </div>
          ))}
        </div>
      ) : list.length === 0 ? (
        <div className="d-card">
          <div className="d-empty">
            <div className="d-empty-icon"><DIcon name="bot" size={26}/></div>
            <div className="d-empty-title">{tab === "active" ? "Нет активных ботов" : "История пуста"}</div>
            <div className="d-empty-msg">{tab === "active" ? "Создай Grid или DCA-бота — работает на любой бирже." : "Остановленные боты появятся здесь."}</div>
          </div>
        </div>
      ) : (
        <div className="d-card">
          <table className="d-table">
            <thead>
              <tr>
                <th>Бот</th><th>Тип</th><th>Биржа</th><th>Сторона</th>
                <th style={{ textAlign: "right" }}>Инвестиция</th>
                <th style={{ textAlign: "right" }}>PnL</th>
                <th style={{ textAlign: "right" }}>uPnL</th>
                <th style={{ textAlign: "right" }}>Liq.</th>
                <th>Статус</th>
                <th style={{ textAlign: "right" }}>Действия</th>
              </tr>
            </thead>
            <tbody>
              {list.map(b => {
                const upnl = b.live?.unrealized_pnl;
                const realized = Number(b.total_pnl) || 0;
                const total = (upnl != null ? realized + upnl : realized);
                return (
                  <tr key={b.id} onClick={() => setDetailsBotId(b.id)} style={{ cursor: "pointer" }}>
                    <td>
                      <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
                        <Coin sym={b.symbol}/>
                        <div>
                          <div style={{ fontWeight: 600 }}>{b.symbol}</div>
                          <div className="muted" style={{ fontSize: 10 }}>#{b.id}</div>
                        </div>
                      </div>
                    </td>
                    <td><span className="d-badge">{b.bot_type.toUpperCase()}</span></td>
                    <td><span className="d-badge">{(b.exchange || "").toUpperCase()}</span></td>
                    <td><span className={"d-badge " + (b.direction === "long" ? "d-badge-long" : b.direction === "short" ? "d-badge-short" : "")}>{(b.direction || "").toUpperCase()}</span></td>
                    <td className="mono" style={{ textAlign: "right" }}>{fmt(b.investment_usdt, 0)}</td>
                    <td className={"mono " + pctClass(total)} style={{ textAlign: "right", fontWeight: 700 }}>
                      {total >= 0 ? "+" : ""}{fmt(total)}
                    </td>
                    <td className={"mono " + pctClass(upnl || 0)} style={{ textAlign: "right" }}>
                      {upnl != null ? ((upnl >= 0 ? "+" : "") + fmt(upnl)) : "—"}
                    </td>
                    <td className="mono" style={{ textAlign: "right", color: "var(--d-danger)" }}>
                      {b.live?.liquidation_price > 0 ? fmtPx(b.live.liquidation_price) : "—"}
                    </td>
                    <td>
                      {b.state === "running" ? <span className="d-badge d-badge-running">running</span>
                        : <span className="d-badge">{b.state}</span>}
                    </td>
                    <td style={{ textAlign: "right" }} onClick={e => e.stopPropagation()}>
                      {ACTIVE.includes(b.state) && (
                        <button className="d-btn d-btn-sm" onClick={() => toggleBot(b)}>
                          {b.state === "running" ? <DIcon name="pause" size={12}/> : <DIcon name="play" size={12}/>}
                        </button>
                      )}
                      <button className="d-btn d-btn-sm d-btn-danger" style={{ marginLeft: 6 }} onClick={() => removeBot(b)}>
                        <DIcon name="x" size={12}/>
                      </button>
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      )}

      </>}

      {createOpen && (
        <BotCreateModal
          exchange={exchange}
          onClose={() => setCreateOpen(false)}
          onCreated={() => { setCreateOpen(false); load(); pushToast({ msg: "Бот создан", ok: true }); }}
          pushToast={pushToast}
        />
      )}

      {detailsBotId && window.DesktopExtras?.LocalBotDetailsModal && (
        <window.DesktopExtras.LocalBotDetailsModal
          botId={detailsBotId}
          onClose={() => setDetailsBotId(null)}
          onReload={load}
          pushToast={pushToast}
        />
      )}
    </div>
  );
};

// ─────────────────────  Биржевые боты (OKX и т.д.)  ─────────────────────
const ExchangeBotsBlock = ({ exchange, bots, loading }) => {
  const [tab, setTab] = useState("active");
  const [selected, setSelected] = useState(null);
  const list = tab === "active" ? bots.active : bots.history;
  if (loading) {
    return <div className="d-card d-card-pad"><Skel h={200}/></div>;
  }
  if ((bots.active.length + bots.history.length) === 0) {
    return (
      <div className="d-card">
        <div className="d-empty">
          <div className="d-empty-icon"><DIcon name="bot" size={26}/></div>
          <div className="d-empty-title">Нет биржевых ботов</div>
          <div className="d-empty-msg">На бирже {exchange.toUpperCase()} нет встроенных Grid/Recurring/Signal ботов. Создавай ботов прямо в API4ATKA — они работают на любой бирже через API.</div>
        </div>
      </div>
    );
  }
  return (
    <div>
      <div className="d-seg" style={{ marginBottom: 14 }}>
        <button className={"d-seg-btn" + (tab === "active" ? " active" : "")} onClick={() => setTab("active")}>
          Активные ({bots.active.length})
        </button>
        <button className={"d-seg-btn" + (tab === "history" ? " active" : "")} onClick={() => setTab("history")}>
          История ({bots.history.length})
        </button>
      </div>
      <div className="d-card">
        {list.length === 0 ? (
          <div className="d-empty" style={{ padding: 30 }}>
            <div className="d-empty-msg">{tab === "active" ? "Нет активных биржевых ботов" : "История пуста"}</div>
          </div>
        ) : (
          <table className="d-table">
            <thead>
              <tr>
                <th>Символ</th><th>Тип</th><th>Направление</th>
                <th style={{ textAlign: "right" }}>Инвест.</th>
                <th style={{ textAlign: "right" }}>PnL</th>
                <th style={{ textAlign: "right" }}>APR</th>
                <th>Создан</th>
              </tr>
            </thead>
            <tbody>
              {list.map((b, i) => {
                const pnl = Number(b.totalPnl || b.pnl || 0);
                const apr = Number(b.totalPnlRatio || b.apr || 0);
                return (
                  <tr key={i} onClick={() => setSelected(b)} style={{ cursor: "pointer" }}>
                    <td><div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                      <Coin sym={b.instId || b.symbol}/>
                      <span style={{ fontWeight: 600 }}>{String(b.instId || b.symbol || "").toUpperCase()}</span>
                    </div></td>
                    <td><span className="d-badge">{(b.botType || b.algoOrdType || "GRID").toUpperCase()}</span></td>
                    <td>
                      {b.direction === "long" ? <span className="d-badge d-badge-long">LONG</span>
                        : b.direction === "short" ? <span className="d-badge d-badge-short">SHORT</span>
                          : <span className="d-badge">{(b.direction || "neutral").toUpperCase()}</span>}
                    </td>
                    <td className="mono" style={{ textAlign: "right" }}>{fmt(b.investment || 0, 0)} {b.currency || "USDT"}</td>
                    <td className={"mono " + pctClass(pnl)} style={{ textAlign: "right", fontWeight: 700 }}>
                      {pnl >= 0 ? "+" : ""}{fmt(pnl)}
                    </td>
                    <td className={"mono " + pctClass(apr)} style={{ textAlign: "right" }}>
                      {fmtPct(apr * 100)}
                    </td>
                    <td className="muted" style={{ fontSize: 11 }}>{ago(b.createTime || b.cTime)}</td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        )}
      </div>

      {selected && window.DesktopExtras?.ExchangeBotDetailsModal && (
        <window.DesktopExtras.ExchangeBotDetailsModal
          bot={selected}
          exchange={exchange}
          onClose={() => setSelected(null)}
        />
      )}
    </div>
  );
};

// ─────────────────────────  Bot Create Modal  ─────────────────────────
const BotCreateModal = ({ exchange, onClose, onCreated, pushToast }) => {
  const [type, setType] = useState("grid");
  const [symbol, setSymbol] = useState("");
  const [direction, setDirection] = useState("long");
  const [invest, setInvest] = useState("100");
  const [lev, setLev] = useState("3");
  // Recurring Basket
  const [basket, setBasket] = useState([
    { symbol: "BTCUSDT", percent: 50 },
    { symbol: "ETHUSDT", percent: 30 },
    { symbol: "SOLUSDT", percent: 20 },
  ]);
  const [basketAmount, setBasketAmount] = useState("100");
  const [basketIntervalH, setBasketIntervalH] = useState("168");
  const [minPx, setMinPx] = useState("");
  const [maxPx, setMaxPx] = useState("");
  const [gridNum, setGridNum] = useState("20");
  // DCA fields
  const [dcaInit, setDcaInit] = useState("10");
  const [dcaSafety, setDcaSafety] = useState("10");
  const [dcaStep, setDcaStep] = useState("0.5");
  const [dcaMargMult, setDcaMargMult] = useState("1.5");
  const [dcaMaxSafety, setDcaMaxSafety] = useState("8");
  const [dcaTp, setDcaTp] = useState("1.0");

  const [markPx, setMarkPx] = useState(0);
  const [busy, setBusy] = useState(false);

  useEffect(() => {
    if (!symbol) { setMarkPx(0); return; }
    (async () => {
      try {
        const p = await window.MobileAPI.loadMarkPrice(exchange, symbol);
        setMarkPx(Number(p) || 0);
        if (type === "grid" && (!minPx || !maxPx) && p > 0) {
          setMinPx((p * 0.95).toPrecision(6));
          setMaxPx((p * 1.05).toPrecision(6));
        }
      } catch {}
    })();
  }, [symbol, exchange, type]);

  const investNum = parseFloat(invest) || 0;
  const levNum = parseInt(lev) || 1;
  const n = parseInt(gridNum) || 0;
  const perLevelMargin = n > 0 ? investNum / n : 0;
  const perLevelNotional = perLevelMargin * levNum;
  const totalNotional = investNum * levNum;
  const isLong = direction === "long";
  let bulkLevels = 0;
  if (type === "grid" && minPx && maxPx && n > 1 && markPx > 0) {
    const lo = parseFloat(minPx), hi = parseFloat(maxPx), step = (hi - lo) / n;
    for (let i = 0; i < n; i++) {
      const p = lo + i * step;
      if (isLong ? p > markPx : p < markPx) bulkLevels++;
    }
  }
  const bulkNotional = bulkLevels * perLevelNotional;
  let worstLoss = 0, liq = 0;
  if (type === "grid" && investNum > 0 && markPx > 0 && minPx && maxPx) {
    const lo = parseFloat(minPx), hi = parseFloat(maxPx);
    const avgAccum = isLong ? (lo + markPx) / 2 : (hi + markPx) / 2;
    const maxPos = totalNotional / avgAccum;
    worstLoss = isLong ? maxPos * Math.max(0, avgAccum - lo) : maxPos * Math.max(0, hi - avgAccum);
    const maint = 0.005;
    liq = isLong ? avgAccum * (1 - (1 / levNum - maint)) : avgAccum * (1 + (1 / levNum - maint));
  }

  const submit = async () => {
    if (busy) return;
    setBusy(true);
    try {
      const config = { market: "futures" };
      if (type === "grid") {
        const lo = parseFloat(minPx), hi = parseFloat(maxPx);
        if (!(lo > 0 && hi > lo && n >= 2)) throw new Error("Неверный диапазон");
        config.min_price = lo; config.max_price = hi; config.grid_num = n;
      } else if (type === "dca") {
        config.initial_margin = parseFloat(dcaInit);
        config.safety_margin = parseFloat(dcaSafety);
        config.price_step_pct = parseFloat(dcaStep);
        config.margin_multiplier = parseFloat(dcaMargMult) || 1.5;
        config.max_safety_orders = parseInt(dcaMaxSafety);
        config.tp_per_cycle_pct = parseFloat(dcaTp);
        config.direction = direction;
      } else if (type === "recurring") {
        const total = basket.reduce((s, b) => s + (parseFloat(b.percent) || 0), 0);
        if (Math.abs(total - 100) > 0.5) throw new Error("Сумма % в корзине должна быть 100, сейчас " + total.toFixed(1));
        if (basket.length === 0) throw new Error("Добавь хотя бы один символ в корзину");
        config.basket = basket;
        config.amount_per_purchase = parseFloat(basketAmount);
        config.interval_hours = parseInt(basketIntervalH);
        config.market = "spot";
      }
      await window.MobileAPI.createLocalBot({
        exchange,
        symbol: (type === "recurring" ? "BASKET" : symbol.toUpperCase()),
        bot_type: type,
        direction: type === "recurring" ? "long" : direction,
        investment_usdt: investNum,
        leverage: type === "recurring" ? 1 : levNum,
        config,
      });
      onCreated();
    } catch (e) {
      pushToast({ msg: "Ошибка: " + (e.message || e).slice(0, 100), err: true });
      setBusy(false);
    }
  };

  return (
    <div className="d-modal-backdrop" onClick={onClose}>
      <div className="d-modal wide" onClick={e => e.stopPropagation()}>
        <div className="d-modal-head">
          <div className="d-modal-title">Создать локального бота</div>
          <button className="d-icon-btn" onClick={onClose}><DIcon name="x" size={14}/></button>
        </div>
        <div className="d-modal-body" style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 20 }}>
          <div>
            <div className="d-label">Тип бота</div>
            <div className="d-seg" style={{ marginBottom: 14 }}>
              {[["grid","Grid"],["dca","Smart-DCA"],["recurring","Basket"]].map(([k,l]) => (
                <button key={k} className={"d-seg-btn" + (type === k ? " active" : "")} onClick={() => setType(k)}>{l}</button>
              ))}
            </div>

            <div className="d-field">
              <div className="d-label">Символ</div>
              <input className="d-input" value={symbol} onChange={e => setSymbol(e.target.value.toUpperCase())} placeholder="TONUSDT"/>
              {markPx > 0 && (
                <div className="muted" style={{ fontSize: 11, marginTop: 4 }}>
                  Текущая цена: <span className="mono">{fmtPx(markPx)}</span>
                </div>
              )}
            </div>
            <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
              <div className="d-field">
                <div className="d-label">Направление</div>
                <div className="d-seg">
                  {["long","short","neutral"].map(d => (
                    <button key={d} className={"d-seg-btn" + (direction === d ? " active" : "")} onClick={() => setDirection(d)}>{d}</button>
                  ))}
                </div>
              </div>
              <div className="d-field">
                <div className="d-label">Инвестиция (маржа USDT)</div>
                <input className="d-input" value={invest} onChange={e => setInvest(e.target.value)} inputMode="decimal"/>
              </div>
            </div>
            <div className="d-field">
              <div className="d-label">Плечо</div>
              <div style={{ display: "flex", gap: 6 }}>
                {[1,3,5,10,20,50].map(L => (
                  <button key={L} className={"d-btn d-btn-sm" + (parseInt(lev) === L ? " d-btn-primary" : "")}
                          onClick={() => setLev(String(L))} style={{ flex: 1 }}>{L}x</button>
                ))}
              </div>
            </div>

            {type === "grid" && (
              <>
                <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
                  <div className="d-field">
                    <div className="d-label">Мин цена</div>
                    <input className="d-input" value={minPx} onChange={e => setMinPx(e.target.value)} inputMode="decimal"/>
                  </div>
                  <div className="d-field">
                    <div className="d-label">Макс цена</div>
                    <input className="d-input" value={maxPx} onChange={e => setMaxPx(e.target.value)} inputMode="decimal"/>
                  </div>
                </div>
                <div className="d-field">
                  <div className="d-label">Число ордеров (сетка)</div>
                  <input className="d-input" value={gridNum} onChange={e => setGridNum(e.target.value.replace(/\D/g, ""))} inputMode="numeric"/>
                </div>
              </>
            )}

            {type === "dca" && (
              <>
                <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
                  <div className="d-field">
                    <div className="d-label">Маржа начального</div>
                    <input className="d-input" value={dcaInit} onChange={e => setDcaInit(e.target.value)} inputMode="decimal"/>
                  </div>
                  <div className="d-field">
                    <div className="d-label">Маржа страховочного</div>
                    <input className="d-input" value={dcaSafety} onChange={e => setDcaSafety(e.target.value)} inputMode="decimal"/>
                  </div>
                </div>
                <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
                  <div className="d-field">
                    <div className="d-label">Шаг цены, %</div>
                    <input className="d-input" value={dcaStep} onChange={e => setDcaStep(e.target.value)} inputMode="decimal"/>
                  </div>
                  <div className="d-field">
                    <div className="d-label">Маржа ×</div>
                    <input className="d-input" value={dcaMargMult} onChange={e => setDcaMargMult(e.target.value)} inputMode="decimal"/>
                  </div>
                </div>
                <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
                  <div className="d-field">
                    <div className="d-label">Макс. страховочных</div>
                    <input className="d-input" value={dcaMaxSafety} onChange={e => setDcaMaxSafety(e.target.value.replace(/\D/g, ""))} inputMode="numeric"/>
                  </div>
                  <div className="d-field">
                    <div className="d-label">TP за цикл, %</div>
                    <input className="d-input" value={dcaTp} onChange={e => setDcaTp(e.target.value)} inputMode="decimal"/>
                  </div>
                </div>
              </>
            )}

            {type === "recurring" && window.DesktopExtras?.BasketCreateForm && (
              <window.DesktopExtras.BasketCreateForm
                basket={basket}
                setBasket={setBasket}
                amount={basketAmount}
                setAmount={setBasketAmount}
                intervalH={basketIntervalH}
                setIntervalH={setBasketIntervalH}
              />
            )}
          </div>

          {/* Right column — live summary */}
          <div>
            <div className="d-label">Расчёт</div>
            <div className="d-card" style={{ padding: 16 }}>
              <div className="d-stat">
                <span className="d-stat-label">Маржа/уровень</span>
                <span className="d-stat-val">{fmt(perLevelMargin)} USDT</span>
              </div>
              <div className="d-stat">
                <span className="d-stat-label">Нотионал/уровень</span>
                <span className={"d-stat-val " + (perLevelNotional < 7.5 ? "neg" : "")}>
                  {fmt(perLevelNotional)} USDT {perLevelNotional < 7.5 ? "⚠" : ""}
                </span>
              </div>
              <div className="d-stat">
                <span className="d-stat-label">Всего нотионал</span>
                <span className="d-stat-val">{fmt(totalNotional)} USDT</span>
              </div>
              {bulkNotional > 0 && (
                <div className="d-stat">
                  <span className="d-stat-label" style={{ color: "var(--d-danger)" }}>Bulk-buy при старте</span>
                  <span className="d-stat-val neg">{fmt(bulkNotional, 0)} USDT ({bulkLevels} ур.)</span>
                </div>
              )}
              {worstLoss > 0 && (
                <div className="d-stat">
                  <span className="d-stat-label">Макс. убыток (out-of-range)</span>
                  <span className="d-stat-val neg">−{fmt(worstLoss, 0)} USDT</span>
                </div>
              )}
              {liq > 0 && (
                <div className="d-stat">
                  <span className="d-stat-label">Цена ликвидации (~)</span>
                  <span className="d-stat-val neg">{fmtPx(liq)}</span>
                </div>
              )}
            </div>

            {(levNum > 5 || worstLoss > investNum) && (
              <div style={{ marginTop: 10, padding: 12, background: "var(--d-danger-dim)",
                            border: "1px solid rgba(255,71,89,0.3)", borderRadius: 8, fontSize: 11,
                            color: "var(--d-danger)", lineHeight: 1.5 }}>
                ⚠ {levNum > 5
                  ? "Плечо " + levNum + "x высокое для Grid — на OKX рекомендуют 1-3x."
                  : "Макс. убыток " + fmt(worstLoss, 0) + " > инвестиций " + fmt(investNum, 0) + ". Снизьте плечо."}
              </div>
            )}
          </div>
        </div>
        <div className="d-modal-foot">
          <button className="d-btn d-btn-ghost" onClick={onClose}>Отмена</button>
          <button className="d-btn d-btn-primary" onClick={submit} disabled={busy || !symbol || !investNum}>
            {busy ? <span className="d-spinner"/> : "Создать бота"}
          </button>
        </div>
      </div>
    </div>
  );
};

// ═══════════════════════════════════════════════════════════════════════════════
//                                SPOT SCREEN
// ═══════════════════════════════════════════════════════════════════════════════
const SpotScreen = ({ exchange, exReady, pushToast, refreshTick }) => {
  const [balances, setBalances] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    (async () => {
      setLoading(true);
      if (!exReady[exchange]) { setBalances([]); setLoading(false); return; }
      try {
        const r = await window.MobileAPI.loadSpotBalance(exchange);
        // API может вернуть массив, или { balances: [...] }, или { error }, или null.
        // Нормализуем строго в массив, чтобы reduce/map не падали.
        let arr = [];
        if (Array.isArray(r)) arr = r;
        else if (r && Array.isArray(r.balances)) arr = r.balances;
        else if (r && Array.isArray(r.data)) arr = r.data;
        setBalances(arr);
      } catch (e) {
        console.warn("loadSpotBalance fail", e);
        setBalances([]);
      }
      setLoading(false);
    })();
  }, [exchange, exReady, refreshTick]);

  const total = (Array.isArray(balances) ? balances : []).reduce(
    (s, b) => s + Number(b.valueUSDT || 0), 0,
  );

  if (!exReady[exchange]) {
    return (
      <div className="d-empty">
        <div className="d-empty-icon"><DIcon name="wallet" size={26}/></div>
        <div className="d-empty-title">Нет API-ключа для {exchange.toUpperCase()}</div>
        <div className="d-empty-msg">Добавь ключ в «Настройки» чтобы видеть спотовый баланс.</div>
      </div>
    );
  }

  return (
    <div>
      <div className="d-hero" style={{ marginBottom: 16 }}>
        <div className="d-kpi-label">Спот-баланс ({exchange.toUpperCase()})</div>
        <div className="d-kpi-value mono">{fmt(total)} <span style={{ fontSize: 16, color: "var(--d-text-2)" }}>USDT</span></div>
        <div className="muted" style={{ fontSize: 12, marginTop: 4 }}>{balances.filter(b => Number(b.total) > 0).length} монет с ненулевым балансом</div>
      </div>
      <div className="d-card">
        {loading ? (
          <div style={{ padding: 16 }}>
            {Array.from({ length: 5 }).map((_, i) => <div key={i} style={{ marginBottom: 10 }}><Skel/></div>)}
          </div>
        ) : balances.length === 0 ? (
          <div className="d-empty"><div className="d-empty-msg">Спот баланс пуст</div></div>
        ) : (
          <table className="d-table">
            <thead>
              <tr>
                <th>Актив</th>
                <th style={{ textAlign: "right" }}>Свободно</th>
                <th style={{ textAlign: "right" }}>Locked</th>
                <th style={{ textAlign: "right" }}>Total</th>
                <th style={{ textAlign: "right" }}>~USDT</th>
              </tr>
            </thead>
            <tbody>
              {balances.filter(b => Number(b.total) > 0).map((b, i) => (
                <tr key={i}>
                  <td>
                    <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
                      <Coin sym={b.asset}/>
                      <span style={{ fontWeight: 600 }}>{b.asset}</span>
                    </div>
                  </td>
                  <td className="mono" style={{ textAlign: "right" }}>{fmt(b.free, 6)}</td>
                  <td className="mono" style={{ textAlign: "right", color: "var(--d-text-2)" }}>{fmt(b.locked, 6)}</td>
                  <td className="mono" style={{ textAlign: "right", fontWeight: 600 }}>{fmt(b.total, 6)}</td>
                  <td className="mono" style={{ textAlign: "right" }}>${fmt(b.valueUSDT)}</td>
                </tr>
              ))}
            </tbody>
          </table>
        )}
      </div>
    </div>
  );
};

// ═══════════════════════════════════════════════════════════════════════════════
//                              SIGNALS SCREEN
// ═══════════════════════════════════════════════════════════════════════════════
const SignalsScreen = ({ pushToast, refreshTick }) => {
  const [tab, setTab] = useState("channels");   // channels | forwards | lent
  const [channels, setChannels] = useState([]);
  const [stats, setStats] = useState(null);
  const [period, setPeriod] = useState(30);
  const [loading, setLoading] = useState(true);
  const [editing, setEditing] = useState(null);
  const [addOpen, setAddOpen] = useState(false);

  const reload = useCallback(async () => {
    setLoading(true);
    try {
      const [c, s] = await Promise.all([
        window.MobileAPI.listChannels(),
        window.MobileAPI.loadChannelStats(period),
      ]);
      setChannels(c || []);
      setStats(s || []);
    } catch {}
    setLoading(false);
  }, [period]);
  useEffect(() => { reload(); }, [reload, refreshTick]);

  const statsMap = useMemo(() => {
    const m = {};
    (stats || []).forEach(s => { m[s.channel_ref || s.label || ""] = s; });
    return m;
  }, [stats]);

  return (
    <div>
      <div className="d-tabs" style={{ marginBottom: 14 }}>
        <button className={"d-tab" + (tab === "channels" ? " active" : "")} onClick={() => setTab("channels")}>
          <DIcon name="signal" size={14} style={{ verticalAlign: "-2px", marginRight: 6 }}/> Каналы <span className="d-tab-count">{channels.length}</span>
        </button>
        <button className={"d-tab" + (tab === "lent" ? " active" : "")} onClick={() => setTab("lent")}>
          <DIcon name="fire" size={14} style={{ verticalAlign: "-2px", marginRight: 6 }}/> Live лента
        </button>
        <button className={"d-tab" + (tab === "forwards" ? " active" : "")} onClick={() => setTab("forwards")}>
          <DIcon name="forward" size={14} style={{ verticalAlign: "-2px", marginRight: 6 }}/> Перенаправления
        </button>
      </div>

      {tab === "lent" && <DashSignalsLent refreshTick={refreshTick}/>}
      {tab === "forwards" && <ForwardsManager pushToast={pushToast} channels={channels}/>}

      {tab === "channels" && <>
      <div className="d-section-head">
        <div className="d-section-title">Каналы сигналов <span className="d-tab-count">{channels.length}</span></div>
        <div style={{ display: "flex", gap: 10 }}>
          <div className="d-seg">
            {[7, 30, 90].map(d => (
              <button key={d} className={"d-seg-btn" + (period === d ? " active" : "")} onClick={() => setPeriod(d)}>
                {d}д
              </button>
            ))}
          </div>
          <button className="d-btn d-btn-primary" onClick={() => setAddOpen(true)}>
            <DIcon name="plus" size={12}/> Добавить канал
          </button>
        </div>
      </div>

      {loading ? (
        <Skel h={200}/>
      ) : channels.length === 0 ? (
        <div className="d-empty">
          <div className="d-empty-icon"><DIcon name="signal" size={26}/></div>
          <div className="d-empty-title">Нет подключённых каналов</div>
          <div className="d-empty-msg">Открой «Настройки» → «Telegram» → «Подключить аккаунт», затем добавь каналы для парсинга сигналов.</div>
        </div>
      ) : (
        <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(320px, 1fr))", gap: 12 }}>
          {channels.map(c => {
            const st = statsMap[c.channel_ref] || {};
            const winrate = Number(st.winrate || 0);
            const pnl = Number(st.total_pnl || 0);
            return (
              <div key={c.id} className="d-card d-card-pad">
                <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 10 }}>
                  <div style={{ width: 36, height: 36, borderRadius: 10, background: "var(--d-bg-3)",
                                display: "grid", placeItems: "center", color: "var(--d-accent)" }}>
                    <DIcon name="signal" size={16}/>
                  </div>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontWeight: 600, fontSize: 13 }}>{c.label || c.channel_ref}</div>
                    <div className="muted mono" style={{ fontSize: 10 }}>{c.channel_ref}</div>
                  </div>
                </div>
                <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8 }}>
                  <div>
                    <div className="d-kpi-label">Сделок</div>
                    <div className="mono" style={{ fontSize: 16, fontWeight: 700 }}>{st.count || 0}</div>
                  </div>
                  <div>
                    <div className="d-kpi-label">Winrate</div>
                    <div className={"mono " + (winrate >= 50 ? "pos" : "neg")} style={{ fontSize: 16, fontWeight: 700 }}>
                      {winrate ? winrate.toFixed(1) + "%" : "—"}
                    </div>
                  </div>
                  <div>
                    <div className="d-kpi-label">Avg PnL</div>
                    <div className={"mono " + pctClass(st.avg_pnl || 0)} style={{ fontSize: 14 }}>
                      {st.avg_pnl != null ? (st.avg_pnl >= 0 ? "+" : "") + fmt(st.avg_pnl, 2) : "—"}
                    </div>
                  </div>
                  <div>
                    <div className="d-kpi-label">Total PnL</div>
                    <div className={"mono " + pctClass(pnl)} style={{ fontSize: 14, fontWeight: 700 }}>
                      {pnl >= 0 ? "+" : ""}{fmt(pnl, 0)}
                    </div>
                  </div>
                </div>
                <div style={{ marginTop: 10, paddingTop: 10, borderTop: "1px solid var(--d-line-soft)", display: "flex", gap: 6 }}>
                  <button className="d-btn d-btn-sm" style={{ flex: 1 }} onClick={() => setEditing(c)}>
                    <DIcon name="settings" size={12}/> Настройка
                  </button>
                </div>
              </div>
            );
          })}
        </div>
      )}

      </>}

      {editing && (
        <ChannelEditModal
          channel={editing}
          onClose={() => setEditing(null)}
          onSaved={() => { setEditing(null); reload(); pushToast({ msg: "Сохранено", ok: true }); }}
          onDeleted={() => { setEditing(null); reload(); pushToast({ msg: "Канал удалён", ok: true }); }}
          pushToast={pushToast}
        />
      )}
      {addOpen && (
        <ChannelAddModal
          onClose={() => setAddOpen(false)}
          onAdded={() => { setAddOpen(false); reload(); pushToast({ msg: "Канал добавлен", ok: true }); }}
          pushToast={pushToast}
        />
      )}
    </div>
  );
};

// ─────────────────────  Channel Add Modal  ─────────────────────
const ChannelAddModal = ({ onClose, onAdded, pushToast }) => {
  const [ref, setRef] = useState("");
  const [label, setLabel] = useState("");
  const [busy, setBusy] = useState(false);
  const save = async () => {
    if (!ref.trim()) { pushToast({ msg: "Укажи @username или ID канала", err: true }); return; }
    setBusy(true);
    try {
      await window.MobileAPI.addChannel(ref.trim(), label.trim());
      onAdded();
    } catch (e) {
      pushToast({ msg: "Ошибка: " + (e.message || e).slice(0, 100), err: true });
      setBusy(false);
    }
  };
  return (
    <div className="d-modal-backdrop" onClick={onClose}>
      <div className="d-modal" onClick={e => e.stopPropagation()}>
        <div className="d-modal-head">
          <div className="d-modal-title">Добавить канал сигналов</div>
          <button className="d-icon-btn" onClick={onClose}><DIcon name="x" size={14}/></button>
        </div>
        <div className="d-modal-body">
          <div className="d-field">
            <div className="d-label">Канал (@username или -100…)</div>
            <input className="d-input" value={ref} onChange={e => setRef(e.target.value)}
                   placeholder="@signals_premium" autoFocus/>
            <div className="muted" style={{ fontSize: 11, marginTop: 4, lineHeight: 1.5 }}>
              Можно использовать @username приватного/публичного канала или числовой ID вида -1001234567890. Listener бота должен быть подписан на этот канал.
            </div>
          </div>
          <div className="d-field">
            <div className="d-label">Метка (опционально)</div>
            <input className="d-input" value={label} onChange={e => setLabel(e.target.value)}
                   placeholder="Премиум сигналы"/>
          </div>
        </div>
        <div className="d-modal-foot">
          <button className="d-btn d-btn-ghost" onClick={onClose}>Отмена</button>
          <button className="d-btn d-btn-primary" onClick={save} disabled={busy || !ref.trim()}>
            {busy ? <span className="d-spinner"/> : "Добавить"}
          </button>
        </div>
      </div>
    </div>
  );
};

// ─────────────────────  Channel Edit (per-channel settings)  ─────────────────────
const ChannelEditModal = ({ channel, onClose, onSaved, onDeleted, pushToast }) => {
  const [label, setLabel] = useState(channel.label || "");
  const [active, setActive] = useState(channel.is_active !== false);
  const [entry, setEntry] = useState(channel.default_entry_type || "signal");
  const [sl, setSl] = useState(String(channel.default_sl_pct || ""));
  const [busy, setBusy] = useState(false);

  const save = async () => {
    setBusy(true);
    try {
      const slNum = parseFloat(sl);
      await window.MobileAPI.updateChannel(channel.id, {
        label: label.trim(),
        is_active: active,
        default_entry_type: entry,
        default_sl_pct: isFinite(slNum) && slNum > 0 ? slNum : null,
      });
      onSaved();
    } catch (e) {
      pushToast({ msg: "Ошибка: " + (e.message || e).slice(0, 100), err: true });
      setBusy(false);
    }
  };
  const remove = async () => {
    if (!confirm(`Удалить канал ${channel.label || channel.channel_ref}? Сигналы из него больше не будут парситься.`)) return;
    setBusy(true);
    try {
      await window.MobileAPI.removeChannel(channel.id);
      onDeleted();
    } catch (e) {
      pushToast({ msg: "Ошибка удаления: " + (e.message || e).slice(0, 80), err: true });
      setBusy(false);
    }
  };

  return (
    <div className="d-modal-backdrop" onClick={onClose}>
      <div className="d-modal" onClick={e => e.stopPropagation()}>
        <div className="d-modal-head">
          <div className="d-modal-title" style={{ display: "flex", alignItems: "center", gap: 10 }}>
            <div style={{ width: 32, height: 32, borderRadius: 8, background: "var(--d-bg-3)",
                          display: "grid", placeItems: "center", color: "var(--d-accent)" }}>
              <DIcon name="signal" size={14}/>
            </div>
            <div>
              <div>{channel.label || channel.channel_ref}</div>
              <div className="muted mono" style={{ fontSize: 10, fontWeight: 400 }}>{channel.channel_ref}</div>
            </div>
          </div>
          <button className="d-icon-btn" onClick={onClose}><DIcon name="x" size={14}/></button>
        </div>
        <div className="d-modal-body">
          <div className="d-field">
            <div className="d-label">Метка</div>
            <input className="d-input" value={label} onChange={e => setLabel(e.target.value)}/>
          </div>

          <div className="d-field">
            <div className="d-label">Статус</div>
            <div className="d-seg">
              <button className={"d-seg-btn" + (active ? " active" : "")} onClick={() => setActive(true)}>● Активен</button>
              <button className={"d-seg-btn" + (!active ? " active" : "")} onClick={() => setActive(false)}>○ На паузе</button>
            </div>
          </div>

          <div className="d-field">
            <div className="d-label">Тип входа в сделки</div>
            <div className="d-seg" style={{ width: "100%" }}>
              <button className={"d-seg-btn" + (entry === "signal" ? " active" : "")}
                      style={{ flex: 1 }} onClick={() => setEntry("signal")}>
                Как в сигнале
              </button>
              <button className={"d-seg-btn" + (entry === "market" ? " active" : "")}
                      style={{ flex: 1 }} onClick={() => setEntry("market")}>
                Всегда market
              </button>
              <button className={"d-seg-btn" + (entry === "limit" ? " active" : "")}
                      style={{ flex: 1 }} onClick={() => setEntry("limit")}>
                Всегда лимит
              </button>
            </div>
            <div className="muted" style={{ fontSize: 10.5, marginTop: 4, lineHeight: 1.5 }}>
              «Как в сигнале» — если канал указал entry price, ставим лимит; иначе market.
              «Всегда market» — игнорируем entry, входим по рынку.
              «Всегда лимит» — если нет entry в сигнале, используем mark price.
            </div>
          </div>

          <div className="d-field">
            <div className="d-label">Дефолтный SL для сигналов без стопа, %</div>
            <input className="d-input mono" value={sl} onChange={e => setSl(e.target.value)}
                   placeholder="например 8" inputMode="decimal"/>
            <div className="muted" style={{ fontSize: 10.5, marginTop: 4 }}>
              Применяется только когда в сигнале нет SL. Пусто = брать дефолт из общих настроек.
            </div>
          </div>
        </div>
        <div className="d-modal-foot" style={{ justifyContent: "space-between" }}>
          <button className="d-btn d-btn-danger" onClick={remove} disabled={busy}>
            <DIcon name="trash" size={13}/> Удалить
          </button>
          <div style={{ display: "flex", gap: 8 }}>
            <button className="d-btn d-btn-ghost" onClick={onClose}>Отмена</button>
            <button className="d-btn d-btn-primary" onClick={save} disabled={busy}>
              {busy ? <span className="d-spinner"/> : "Сохранить"}
            </button>
          </div>
        </div>
      </div>
    </div>
  );
};

// ─────────────────────  Forwards manager  ─────────────────────
const ForwardsManager = ({ pushToast, channels }) => {
  const [items, setItems] = useState([]);
  const [loading, setLoading] = useState(true);
  const [dest, setDest] = useState("");
  const [sourceId, setSourceId] = useState("");
  const [busy, setBusy] = useState(false);

  const reload = useCallback(async () => {
    setLoading(true);
    try {
      const r = await window.MobileAPI.listForwards();
      setItems(Array.isArray(r) ? r : []);
    } catch {}
    setLoading(false);
  }, []);
  useEffect(() => { reload(); }, [reload]);

  const add = async () => {
    if (!dest.trim()) { pushToast({ msg: "Укажи канал назначения", err: true }); return; }
    setBusy(true);
    try {
      await window.MobileAPI.addForward(dest.trim(), sourceId || null);
      setDest(""); setSourceId("");
      await reload();
      pushToast({ msg: "Перенаправление добавлено", ok: true });
    } catch (e) {
      pushToast({ msg: "Ошибка: " + (e.message || e).slice(0, 100), err: true });
    }
    setBusy(false);
  };
  const toggle = async (f) => {
    try {
      await window.MobileAPI.updateForward(f.id, !f.enabled);
      await reload();
    } catch (e) { pushToast({ msg: "Ошибка: " + (e.message || e), err: true }); }
  };
  const remove = async (f) => {
    if (!confirm("Удалить перенаправление в " + (f.dest_channel_ref || "канал") + "?")) return;
    try {
      await window.MobileAPI.deleteForward(f.id);
      await reload();
      pushToast({ msg: "Удалено", ok: true });
    } catch (e) { pushToast({ msg: "Ошибка: " + (e.message || e), err: true }); }
  };

  return (
    <div>
      <div className="d-card d-card-pad-lg" style={{ marginBottom: 14 }}>
        <div className="d-card-title" style={{ marginBottom: 6 }}>Добавить перенаправление</div>
        <div className="muted" style={{ fontSize: 11.5, marginBottom: 12, lineHeight: 1.5 }}>
          Каждое распарсенное сообщение из выбранного источника будет автоматически переслано в канал/чат назначения с человеко-читаемой картой сигнала.
        </div>
        <div style={{ display: "grid", gridTemplateColumns: "1.5fr 1fr auto", gap: 10, alignItems: "end" }}>
          <div>
            <div className="d-label">Канал назначения (@username или -100…)</div>
            <input className="d-input mono" value={dest} onChange={e => setDest(e.target.value)} placeholder="@my_trades_log"/>
          </div>
          <div>
            <div className="d-label">Источник</div>
            <select className="d-select" value={sourceId} onChange={e => setSourceId(e.target.value)}>
              <option value="">— любой канал —</option>
              {(channels || []).map(c => (
                <option key={c.id} value={c.id}>{c.label || c.channel_ref}</option>
              ))}
            </select>
          </div>
          <button className="d-btn d-btn-primary" onClick={add} disabled={busy || !dest.trim()}>
            {busy ? <span className="d-spinner"/> : "+ Добавить"}
          </button>
        </div>
      </div>

      <div className="d-card">
        {loading ? <div style={{ padding: 16 }}><Skel h={120}/></div>
          : items.length === 0 ? (
            <div className="d-empty">
              <div className="d-empty-icon"><DIcon name="signal" size={22}/></div>
              <div className="d-empty-title">Нет перенаправлений</div>
              <div className="d-empty-msg">Добавь канал-назначение чтобы отслеживать все распарсенные сигналы в отдельном чате.</div>
            </div>
          ) : (
            <table className="d-table">
              <thead>
                <tr>
                  <th>Назначение</th>
                  <th>Источник</th>
                  <th>Статус</th>
                  <th>Создан</th>
                  <th style={{ textAlign: "right" }}>Действия</th>
                </tr>
              </thead>
              <tbody>
                {items.map(f => {
                  const src = (channels || []).find(c => c.id === f.source_channel_id);
                  return (
                    <tr key={f.id}>
                      <td className="mono" style={{ fontWeight: 600 }}>{f.dest_channel_ref}</td>
                      <td>{src ? (src.label || src.channel_ref) : <span className="muted">любой</span>}</td>
                      <td>
                        {f.enabled
                          ? <span className="d-badge d-badge-long">● включён</span>
                          : <span className="d-badge">○ выключен</span>}
                      </td>
                      <td className="muted" style={{ fontSize: 11 }}>{ago(f.created_at)}</td>
                      <td style={{ textAlign: "right" }}>
                        <button className="d-btn d-btn-sm" onClick={() => toggle(f)}>
                          {f.enabled ? "Выкл" : "Вкл"}
                        </button>
                        <button className="d-btn d-btn-sm d-btn-danger" style={{ marginLeft: 6 }} onClick={() => remove(f)}>
                          🗑
                        </button>
                      </td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          )}
      </div>
    </div>
  );
};

// ═══════════════════════════════════════════════════════════════════════════════
//                             JOURNAL SCREEN
// ═══════════════════════════════════════════════════════════════════════════════
const JournalScreen = ({ exchange, exReady, refreshTick, pushToast }) => {
  const [trades, setTrades] = useState([]);
  const [filter, setFilter] = useState("all");
  const [period, setPeriod] = useState("week");
  const [search, setSearch] = useState("");
  const [sourceFilter, setSourceFilter] = useState("");
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    setLoading(true);
    (async () => {
      try {
        const r = await window.MobileAPI.loadTrades({ status: filter === "all" ? null : filter, limit: 500 });
        setTrades(r || []);
      } catch {}
      setLoading(false);
    })();
  }, [filter, period, refreshTick]);

  // Фильтрация на клиенте по symbol + source
  const filtered = useMemo(() => {
    let r = trades;
    if (search) r = r.filter(t => (t.symbol || "").toLowerCase().includes(search.toLowerCase()));
    if (sourceFilter) r = r.filter(t => (t.source || t.signal_source || "") === sourceFilter);
    return r;
  }, [trades, search, sourceFilter]);

  const sources = useMemo(() => {
    const set = new Set();
    trades.forEach(t => { const s = t.source || t.signal_source; if (s) set.add(s); });
    return Array.from(set);
  }, [trades]);

  const exportCSV = () => {
    const rows = [["id", "symbol", "side", "source", "entry", "exit", "qty", "pnl", "status", "opened", "closed"]];
    filtered.forEach(t => {
      rows.push([
        t.id || "", t.symbol || "", t.side || "", t.source || t.signal_source || "",
        t.entry_price || "", t.exit_price || "", t.qty || t.size || "",
        t.pnl || "", t.status || "",
        t.opened_at ? new Date(t.opened_at).toISOString() : "",
        t.closed_at ? new Date(t.closed_at).toISOString() : "",
      ]);
    });
    const csv = rows.map(r => r.map(c => `"${String(c).replace(/"/g, '""')}"`).join(",")).join("\n");
    const blob = new Blob(["﻿" + csv], { type: "text/csv;charset=utf-8" });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url; a.download = `trades_${new Date().toISOString().slice(0,10)}.csv`;
    a.click();
    setTimeout(() => URL.revokeObjectURL(url), 1000);
    pushToast && pushToast({ msg: `Экспортировано ${filtered.length} сделок`, ok: true });
  };

  // Stats по отфильтрованным
  const stats = useMemo(() => {
    const closed = filtered.filter(t => t.status === "closed");
    const wins = closed.filter(t => Number(t.pnl) > 0);
    const totalPnl = closed.reduce((s, t) => s + Number(t.pnl || 0), 0);
    return { total: filtered.length, closed: closed.length, wins: wins.length, winrate: closed.length > 0 ? wins.length / closed.length * 100 : 0, totalPnl };
  }, [filtered]);

  return (
    <div>
      <div className="d-kpi-grid" style={{ marginBottom: 14 }}>
        <div className="d-kpi">
          <div className="d-kpi-label">Всего сделок</div>
          <div className="d-kpi-value mono">{stats.total}</div>
          <div className="d-kpi-sub">{stats.closed} закрытых</div>
        </div>
        <div className="d-kpi">
          <div className="d-kpi-label">Win Rate</div>
          <div className={"d-kpi-value mono " + (stats.winrate >= 50 ? "pos" : "neg")}>
            {stats.winrate.toFixed(1)}%
          </div>
          <div className="d-kpi-sub">{stats.wins} побед</div>
        </div>
        <div className="d-kpi">
          <div className="d-kpi-label">Net PnL</div>
          <div className={"d-kpi-value mono " + pctClass(stats.totalPnl)}>
            {stats.totalPnl >= 0 ? "+" : ""}{fmt(stats.totalPnl)}
          </div>
          <div className="d-kpi-sub">после комиссий</div>
        </div>
        <div className="d-kpi" style={{ display: "flex", flexDirection: "column", justifyContent: "center" }}>
          <button className="d-btn d-btn-primary" onClick={exportCSV} style={{ width: "100%", justifyContent: "center", gap: 8 }}>
            <DIcon name="download" size={14}/> Экспорт CSV
          </button>
          <div className="muted" style={{ fontSize: 10, marginTop: 6, textAlign: "center" }}>{filtered.length} строк</div>
        </div>
      </div>

      <div className="d-section-head">
        <div className="d-section-title">Журнал сделок <span className="d-tab-count">{filtered.length}</span></div>
        <div style={{ display: "flex", gap: 8 }}>
          <input className="d-input" value={search} onChange={e => setSearch(e.target.value)}
                 placeholder="🔍 Символ (BTCUSDT)" style={{ width: 180 }}/>
          <select className="d-select" value={sourceFilter} onChange={e => setSourceFilter(e.target.value)} style={{ width: 160 }}>
            <option value="">Все источники</option>
            {sources.map(s => <option key={s} value={s}>{s}</option>)}
          </select>
          <div className="d-seg">
            {[["all","Все"],["closed","Закрытые"],["open","Открытые"],["failed","Ошибки"]].map(([k,l]) => (
              <button key={k} className={"d-seg-btn" + (filter === k ? " active" : "")} onClick={() => setFilter(k)}>{l}</button>
            ))}
          </div>
        </div>
      </div>
      <div className="d-card">
        {loading ? <div style={{ padding: 16 }}><Skel h={200}/></div>
          : filtered.length === 0 ? (
            <div className="d-empty">
              <div className="d-empty-icon"><DIcon name="journal" size={26}/></div>
              <div className="d-empty-title">Нет сделок по фильтру</div>
              <div className="d-empty-msg">Попробуй убрать фильтр или сменить период.</div>
            </div>
          ) : (
            <table className="d-table">
              <thead>
                <tr>
                  <th>Символ</th><th>Источник</th><th>Сторона</th>
                  <th style={{ textAlign: "right" }}>Вход</th>
                  <th style={{ textAlign: "right" }}>Выход</th>
                  <th style={{ textAlign: "right" }}>Размер</th>
                  <th style={{ textAlign: "right" }}>PnL</th>
                  <th>Открыта</th><th>Статус</th>
                </tr>
              </thead>
              <tbody>
                {filtered.map((t, i) => {
                  const pnl = Number(t.pnl || 0);
                  return (
                    <tr key={i}>
                      <td>
                        <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                          <Coin sym={t.symbol}/>
                          <span style={{ fontWeight: 600 }}>{t.symbol}</span>
                        </div>
                      </td>
                      <td><span className="d-badge">{t.source || "-"}</span></td>
                      <td><span className={"d-badge " + (t.side === "LONG" ? "d-badge-long" : "d-badge-short")}>{t.side}</span></td>
                      <td className="mono" style={{ textAlign: "right" }}>{fmtPx(t.entry_price)}</td>
                      <td className="mono" style={{ textAlign: "right" }}>{t.exit_price ? fmtPx(t.exit_price) : "—"}</td>
                      <td className="mono" style={{ textAlign: "right" }}>{fmt(t.qty, 4)}</td>
                      <td className={"mono " + pctClass(pnl)} style={{ textAlign: "right", fontWeight: 700 }}>
                        {t.exit_price ? ((pnl >= 0 ? "+" : "") + fmt(pnl)) : "—"}
                      </td>
                      <td className="muted" style={{ fontSize: 11 }}>{ago(t.opened_at)}</td>
                      <td>
                        {t.status === "closed" && <span className="d-badge d-badge-long">closed</span>}
                        {t.status === "open" && <span className="d-badge d-badge-running">open</span>}
                        {t.status === "failed" && <span className="d-badge d-badge-short">failed</span>}
                        {!["closed","open","failed"].includes(t.status) && <span className="d-badge">{t.status}</span>}
                      </td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          )}
      </div>
    </div>
  );
};

// ═══════════════════════════════════════════════════════════════════════════════
//                              STATS SCREEN
// ═══════════════════════════════════════════════════════════════════════════════
const StatsScreen = ({ refreshTick }) => {
  const [period, setPeriod] = useState(30);
  const [tps, setTps] = useState(null);
  const [slip, setSlip] = useState(null);
  const [channels, setChannels] = useState(null);
  const [busy, setBusy] = useState(true);
  useEffect(() => {
    setBusy(true);
    (async () => {
      try {
        const [t, s, c] = await Promise.all([
          window.MobileAPI.loadTpStats(period),
          window.MobileAPI.loadSlippageStats(period),
          window.MobileAPI.loadChannelStats(period),
        ]);
        setTps(t); setSlip(s); setChannels(c || []);
      } catch {}
      setBusy(false);
    })();
  }, [period, refreshTick]);

  const t = tps || {};
  const total = Number(t.total || 0);
  const tpHits = Array.isArray(t.tp_hits) ? t.tp_hits : [0,0,0];
  const tpRates = Array.isArray(t.tp_rates) ? t.tp_rates : tpHits.map(h => total > 0 ? Math.round(h/total*1000)/10 : 0);

  return (
    <div>
      <div className="d-section-head">
        <div className="d-section-title">Статистика</div>
        <div className="d-seg">
          {[7, 30, 90, 365].map(d => (
            <button key={d} className={"d-seg-btn" + (period === d ? " active" : "")} onClick={() => setPeriod(d)}>
              {d === 365 ? "1Y" : d + "д"}
            </button>
          ))}
        </div>
      </div>

      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16 }}>
        <div className="d-card">
          <div className="d-card-head">
            <div className="d-card-title">Достижение TP / SL</div>
          </div>
          <div className="d-card-pad">
            <div className="muted" style={{ fontSize: 11, marginBottom: 12 }}>
              {total > 0 ? `Из ${total} сделок:` : "Нет закрытых сделок с TP/SL — серое для справки"}
            </div>
            <div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 8 }}>
              {[0,1,2].map(i => (
                <div key={i} style={{ padding: 14, textAlign: "center",
                  background: total > 0 ? "var(--d-success-dim)" : "var(--d-bg-3)",
                  borderRadius: 8, border: total > 0 ? "1px solid rgba(0,193,118,0.3)" : "1px solid var(--d-line)" }}>
                  <div className="muted" style={{ fontSize: 10 }}>TP{i+1}</div>
                  <div className={"mono " + (total > 0 ? "pos" : "")} style={{ fontSize: 22, fontWeight: 700,
                    color: total > 0 ? undefined : "var(--d-text-2)" }}>
                    {tpRates[i] || 0}%
                  </div>
                  <div className="muted" style={{ fontSize: 10 }}>{tpHits[i] || 0} из {total}</div>
                </div>
              ))}
            </div>
            <div style={{ marginTop: 12, padding: 12,
              background: total > 0 ? "var(--d-danger-dim)" : "var(--d-bg-3)",
              borderRadius: 8, border: total > 0 ? "1px solid rgba(255,71,89,0.3)" : "1px solid var(--d-line)",
              display: "flex", justifyContent: "space-between", alignItems: "center" }}>
              <div>
                <div className="muted" style={{ fontSize: 10 }}>SL до TP1</div>
                <div style={{ fontSize: 11, color: "var(--d-text-2)" }}>стоп раньше первого тейка</div>
              </div>
              <div className={"mono " + (total > 0 ? "neg" : "")} style={{ fontSize: 22, fontWeight: 700,
                color: total > 0 ? undefined : "var(--d-text-2)" }}>
                {Number(t.sl_before_tp1_rate || 0)}%
              </div>
            </div>
          </div>
        </div>

        <div className="d-card">
          <div className="d-card-head">
            <div className="d-card-title">Slippage анализ</div>
            <div className="muted" style={{ fontSize: 11 }}>{slip?.samples || 0} сделок</div>
          </div>
          <div className="d-card-pad">
            {slip && slip.samples > 0 ? (
              <>
                <div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 10 }}>
                  <div>
                    <div className="d-kpi-label">Потеряно</div>
                    <div className="mono neg" style={{ fontWeight: 700, fontSize: 18 }}>−{fmt(slip.total_slip_usdt, 2)}</div>
                  </div>
                  <div>
                    <div className="d-kpi-label">Среднее</div>
                    <div className="mono neg" style={{ fontSize: 14 }}>−{fmt(slip.avg_slip_per_trade, 2)}</div>
                  </div>
                  <div>
                    <div className="d-kpi-label">% от объёма</div>
                    <div className="mono" style={{ fontSize: 14 }}>{Number(slip.pct_of_volume || 0).toFixed(3)}%</div>
                  </div>
                </div>
                {Array.isArray(slip.worst) && slip.worst.length > 0 && (
                  <div style={{ marginTop: 14, paddingTop: 12, borderTop: "1px solid var(--d-line-soft)" }}>
                    <div className="muted" style={{ fontSize: 11, marginBottom: 6 }}>Худшие сделки:</div>
                    {slip.worst.slice(0, 3).map((w, i) => (
                      <div key={i} className="d-stat" style={{ padding: "5px 0" }}>
                        <span style={{ fontWeight: 600 }}>{w.symbol}</span>
                        <span className="mono neg">−{fmt(w.slip)} ({Number(w.pct || 0).toFixed(3)}%)</span>
                      </div>
                    ))}
                  </div>
                )}
              </>
            ) : (
              <div className="muted" style={{ fontSize: 12 }}>Нет данных по slippage за период.</div>
            )}
          </div>
        </div>

        <div className="d-card" style={{ gridColumn: "1 / -1" }}>
          <div className="d-card-head">
            <div className="d-card-title">Каналы-источники</div>
          </div>
          {(!channels || channels.length === 0) ? (
            <div className="d-empty"><div className="d-empty-msg">Нет данных по каналам за период.</div></div>
          ) : (
            <table className="d-table">
              <thead><tr><th>Канал</th><th style={{textAlign:"right"}}>Сделок</th><th style={{textAlign:"right"}}>Winrate</th><th style={{textAlign:"right"}}>Avg PnL</th><th style={{textAlign:"right"}}>R/R</th><th style={{textAlign:"right"}}>Total PnL</th></tr></thead>
              <tbody>
                {channels.map((c, i) => (
                  <tr key={i}>
                    <td style={{ fontWeight: 600 }}>{c.label || c.channel_ref || "—"}</td>
                    <td className="mono" style={{ textAlign: "right" }}>{c.count || 0}</td>
                    <td className={"mono " + (c.winrate >= 50 ? "pos" : "neg")} style={{ textAlign: "right" }}>{Number(c.winrate || 0).toFixed(1)}%</td>
                    <td className={"mono " + pctClass(c.avg_pnl || 0)} style={{ textAlign: "right" }}>{Number(c.avg_pnl || 0) >= 0 ? "+" : ""}{fmt(c.avg_pnl, 2)}</td>
                    <td className="mono" style={{ textAlign: "right" }}>{Number(c.rr || 0).toFixed(2)}</td>
                    <td className={"mono " + pctClass(c.total_pnl || 0)} style={{ textAlign: "right", fontWeight: 700 }}>{Number(c.total_pnl || 0) >= 0 ? "+" : ""}{fmt(c.total_pnl, 0)}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          )}
        </div>
      </div>
    </div>
  );
};

// ═══════════════════════════════════════════════════════════════════════════════
//                              SETTINGS SCREEN
// ═══════════════════════════════════════════════════════════════════════════════
const SettingsScreen = ({ user, pushToast }) => {
  const [tab, setTab] = useState("api");
  return (
    <div>
      <div className="d-tabs">
        {[["api","API ключи бирж"],["telegram","Telegram"],["push","Push уведомления"],["account","Аккаунт"]].map(([k,l]) => (
          <button key={k} className={"d-tab" + (tab === k ? " active" : "")} onClick={() => setTab(k)}>{l}</button>
        ))}
      </div>

      {tab === "api" && <SettingsApiKeys pushToast={pushToast}/>}
      {tab === "telegram" && <SettingsTelegram pushToast={pushToast}/>}
      {tab === "push" && <SettingsPush pushToast={pushToast}/>}
      {tab === "account" && <SettingsAccount user={user} pushToast={pushToast}/>}
    </div>
  );
};

const SettingsApiKeys = ({ pushToast }) => {
  const [exch, setExch] = useState("weex");
  const [key, setKey] = useState("");
  const [secret, setSecret] = useState("");
  const [pass, setPass] = useState("");
  const [ready, setReady] = useState(window.creds.readyMap());
  useEffect(() => {
    const upd = () => setReady(window.creds.readyMap());
    window.addEventListener("creds-updated", upd);
    return () => window.removeEventListener("creds-updated", upd);
  }, []);
  const save = async () => {
    try {
      await window.creds.save(exch, "default", { key, secret, pass });
      pushToast({ msg: "Ключ сохранён", ok: true });
      setKey(""); setSecret(""); setPass("");
    } catch (e) { pushToast({ msg: "Ошибка: " + (e.message || e), err: true }); }
  };
  return (
    <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16 }}>
      <div className="d-card d-card-pad-lg">
        <div className="d-card-title" style={{ marginBottom: 14 }}>Добавить API-ключ</div>
        <div className="d-field">
          <div className="d-label">Биржа</div>
          <div className="d-seg">
            {["weex","okx","binance","bybit"].map(e => (
              <button key={e} className={"d-seg-btn" + (exch === e ? " active" : "")} onClick={() => setExch(e)}>
                {e.toUpperCase()} {ready[e] && "✓"}
              </button>
            ))}
          </div>
        </div>
        <div className="d-field"><div className="d-label">API Key</div><input className="d-input" value={key} onChange={e => setKey(e.target.value)}/></div>
        <div className="d-field"><div className="d-label">API Secret</div><input className="d-input" type="password" value={secret} onChange={e => setSecret(e.target.value)}/></div>
        {(exch === "okx" || exch === "weex") && (
          <div className="d-field"><div className="d-label">Passphrase</div><input className="d-input" type="password" value={pass} onChange={e => setPass(e.target.value)}/></div>
        )}
        <button className="d-btn d-btn-primary" onClick={save} disabled={!key || !secret} style={{ width: "100%", marginTop: 6 }}>
          Сохранить ключ
        </button>
      </div>
      <div className="d-card d-card-pad-lg">
        <div className="d-card-title" style={{ marginBottom: 14 }}>Подключённые биржи</div>
        {["weex","okx","binance","bybit"].map(e => (
          <div key={e} className="d-stat">
            <span className="d-stat-label" style={{ display: "flex", alignItems: "center", gap: 8 }}>
              <span style={{ width: 8, height: 8, borderRadius: 4, background: ready[e] ? "var(--d-success)" : "var(--d-text-3)" }}/>
              {e.toUpperCase()}
            </span>
            <span className="d-stat-val">{ready[e] ? "подключена" : "—"}</span>
          </div>
        ))}
      </div>

      <div style={{ gridColumn: "1 / -1" }}>
        {window.DesktopExtras?.MultiAccountManager && <window.DesktopExtras.MultiAccountManager pushToast={pushToast}/>}
      </div>
    </div>
  );
};

const SettingsTelegram = ({ pushToast }) => {
  const [tg, setTg] = useState(null);
  const [loading, setLoading] = useState(true);
  const [wizardOpen, setWizardOpen] = useState(false);
  const [disconnecting, setDisconnecting] = useState(false);

  const refresh = useCallback(async () => {
    setLoading(true);
    try {
      const r = await fetch("/api/me/telegram/status").then(x => x.json()).catch(() => ({}));
      setTg(r || {});
    } catch {}
    setLoading(false);
  }, []);
  useEffect(() => { refresh(); }, [refresh]);

  const disconnect = async () => {
    if (!confirm("Отключить Telegram-аккаунт? Listener остановится и сигналы перестанут приходить.")) return;
    setDisconnecting(true);
    try {
      await window.MobileAPI.tgDisconnect();
      pushToast({ msg: "Telegram отключён", ok: true });
      await refresh();
    } catch (e) {
      pushToast({ msg: "Ошибка: " + (e.message || e), err: true });
    }
    setDisconnecting(false);
  };

  if (loading) return <div className="d-card d-card-pad-lg"><Skel h={180}/></div>;

  const connected = !!(tg?.tg_username || tg?.phone_masked);
  return (
    <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16 }}>
      <div className="d-card d-card-pad-lg">
        <div className="d-card-title" style={{ marginBottom: 14 }}>Telegram аккаунт</div>
        {connected ? (
          <>
            <div className="d-stat">
              <span className="d-stat-label">Статус</span>
              <span className="d-stat-val pos">✓ подключён</span>
            </div>
            <div className="d-stat">
              <span className="d-stat-label">Username</span>
              <span className="d-stat-val mono">{tg.tg_username ? "@" + tg.tg_username : "—"}</span>
            </div>
            <div className="d-stat">
              <span className="d-stat-label">Телефон</span>
              <span className="d-stat-val mono">{tg.phone_masked || "—"}</span>
            </div>
            {tg.is_listener_running != null && (
              <div className="d-stat">
                <span className="d-stat-label">Listener</span>
                <span className={"d-stat-val " + (tg.is_listener_running ? "pos" : "neg")}>
                  {tg.is_listener_running ? "● работает" : "○ не запущен"}
                </span>
              </div>
            )}
            <div style={{ display: "flex", gap: 8, marginTop: 14 }}>
              <button className="d-btn d-btn-danger" onClick={disconnect} disabled={disconnecting}>
                {disconnecting ? <span className="d-spinner"/> : "Отключить"}
              </button>
              <button className="d-btn" onClick={() => setWizardOpen(true)}>
                Переподключить
              </button>
            </div>
          </>
        ) : (
          <>
            <div style={{ padding: 12, marginBottom: 14, background: "var(--d-warn-dim)",
                          border: "1px solid rgba(245,165,36,0.3)", borderRadius: 8,
                          fontSize: 12, color: "var(--d-warn)" }}>
              ⚠ Telegram не подключён — сигналы из каналов не парсятся.
            </div>
            <button className="d-btn d-btn-primary" onClick={() => setWizardOpen(true)} style={{ width: "100%", justifyContent: "center", gap: 8 }}>
              <DIcon name="mail" size={14}/> Подключить Telegram
            </button>
          </>
        )}
        {wizardOpen && window.DesktopExtras?.TelegramConnectWizard && (
          <window.DesktopExtras.TelegramConnectWizard
            onClose={() => setWizardOpen(false)}
            onDone={() => refresh()}
            pushToast={pushToast}
          />
        )}
      </div>

      <div className="d-card d-card-pad-lg">
        <div className="d-card-title" style={{ marginBottom: 14 }}>Confirm-бот (опционально)</div>
        <div className="muted" style={{ fontSize: 12, lineHeight: 1.7 }}>
          Confirm-бот пишет тебе в личку каждый распарсенный сигнал с inline-кнопками «Войти / Пропустить».
          Полезно если хочешь подтверждать сделки руками вместо авто-режима.
          <br/><br/>
          Создаётся через <a href="https://t.me/BotFather" target="_blank">@BotFather</a>, токен вводится в мобильной версии.
        </div>
      </div>
    </div>
  );
};

const SettingsPush = ({ pushToast }) => {
  const [perm, setPerm] = useState("default");
  const [status, setStatus] = useState({ subscriptions: 0, enabled: false });
  const [busy, setBusy] = useState(false);
  const [supported, setSupported] = useState(true);

  const refresh = useCallback(async () => {
    if (typeof Notification !== "undefined") setPerm(Notification.permission);
    try {
      const sup = window.MobileAPI.pushIsSupported ? await window.MobileAPI.pushIsSupported() : true;
      setSupported(!!sup);
    } catch {}
    try {
      const s = await window.MobileAPI.pushStatus();
      setStatus(s || { subscriptions: 0, enabled: false });
    } catch {}
  }, []);
  useEffect(() => { refresh(); }, [refresh]);

  const enable = async () => {
    setBusy(true);
    try {
      await window.MobileAPI.pushEnable();
      await refresh();
      pushToast({ msg: "Push включён — ты получишь уведомления о сигналах и сделках", ok: true });
    } catch (e) {
      pushToast({ msg: "Ошибка: " + (e.message || e).slice(0, 120), err: true });
    }
    setBusy(false);
  };
  const disable = async () => {
    setBusy(true);
    try {
      await window.MobileAPI.pushDisable();
      await refresh();
      pushToast({ msg: "Push отключён", ok: true });
    } catch (e) {
      pushToast({ msg: "Ошибка: " + (e.message || e), err: true });
    }
    setBusy(false);
  };
  const test = async () => {
    setBusy(true);
    try {
      await window.MobileAPI.pushTest();
      pushToast({ msg: "Тест-уведомление отправлено — проверь центр уведомлений", ok: true });
    } catch (e) {
      pushToast({ msg: "Ошибка: " + (e.message || e), err: true });
    }
    setBusy(false);
  };

  const permPill = perm === "granted"
    ? { text: "✓ разрешено", color: "var(--d-success)" }
    : perm === "denied"
    ? { text: "✕ запрещено", color: "var(--d-danger)" }
    : { text: "не запрошено", color: "var(--d-text-2)" };

  return (
    <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16 }}>
      <div className="d-card d-card-pad-lg">
        <div className="d-card-title" style={{ marginBottom: 14 }}>Push уведомления</div>
        {!supported && (
          <div style={{ padding: 10, marginBottom: 14, background: "var(--d-warn-dim)",
            border: "1px solid rgba(245,165,36,0.3)", borderRadius: 8,
            fontSize: 11.5, color: "var(--d-warn)", lineHeight: 1.5 }}>
            ⚠ Браузер не поддерживает Web Push. Установи как PWA или используй Chrome/Edge/Firefox.
          </div>
        )}
        <div className="d-stat">
          <span className="d-stat-label">Permission</span>
          <span className="d-stat-val" style={{ color: permPill.color }}>{permPill.text}</span>
        </div>
        <div className="d-stat">
          <span className="d-stat-label">Подписок устройств</span>
          <span className="d-stat-val mono">{status.subscriptions || 0}</span>
        </div>
        <div className="d-stat">
          <span className="d-stat-label">Статус</span>
          <span className={"d-stat-val " + (status.enabled ? "pos" : "muted")}>
            {status.enabled ? "✓ активны" : "выключены"}
          </span>
        </div>
        <div style={{ display: "flex", gap: 8, marginTop: 16 }}>
          {!status.enabled ? (
            <button className="d-btn d-btn-primary" onClick={enable} disabled={busy || !supported || perm === "denied"}>
              {busy ? <span className="d-spinner"/> : <><DIcon name="bell" size={13}/> Включить push</>}
            </button>
          ) : (
            <button className="d-btn d-btn-danger" onClick={disable} disabled={busy}>
              Отключить
            </button>
          )}
          <button className="d-btn" onClick={test} disabled={busy || !status.enabled}>
            Отправить тест
          </button>
        </div>
      </div>

      <div className="d-card d-card-pad-lg">
        <div className="d-card-title" style={{ marginBottom: 14 }}>Когда приходят пуши</div>
        <ul style={{ paddingLeft: 18, margin: 0, lineHeight: 2, fontSize: 12.5, color: "var(--d-text-1)" }}>
          <li>🟢 Новый сигнал распарсен из канала</li>
          <li>📊 Сделка открыта (entry price, размер)</li>
          <li>🎯 TP / SL сработал — с PnL</li>
          <li>🤖 Локальный бот: цикл завершён / ошибка / out-of-range</li>
          <li>⚡ Storm mode — кто-то включил аварийную остановку</li>
        </ul>
        <div className="muted" style={{ fontSize: 11, marginTop: 14, lineHeight: 1.6 }}>
          Подписка действует на это устройство/браузер. Чтобы получать на телефон — открой сайт с PWA на нём и нажми «Включить push» там.
        </div>
      </div>
    </div>
  );
};

const SettingsAccount = ({ user, pushToast }) => {
  const [sendingVerify, setSendingVerify] = useState(false);
  const sendVerify = async () => {
    setSendingVerify(true);
    try {
      const r = await fetch("/api/auth/resend-verify", { method: "POST" });
      if (!r.ok) throw new Error("HTTP " + r.status);
      const j = await r.json();
      if (j.already_verified) {
        pushToast({ msg: "Email уже подтверждён ✓", ok: true });
      } else if (j.sent === false) {
        pushToast({ msg: "Письмо не отправлено (SMTP не настроен)", err: true });
      } else {
        pushToast({ msg: "Письмо отправлено — проверь почту", ok: true });
      }
      pushToast({ msg: "Письмо отправлено — проверь почту", ok: true });
    } catch (e) {
      pushToast({ msg: "Ошибка: " + (e.message || e), err: true });
    }
    setSendingVerify(false);
  };
  const logoutAll = async () => {
    if (!confirm("Завершить все сессии (выйти везде)?")) return;
    try {
      await fetch("/api/auth/logout-all", { method: "POST" });
      window.location.reload();
    } catch (e) {
      pushToast({ msg: "Ошибка: " + (e.message || e), err: true });
    }
  };
  return (
    <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16 }}>
      <div className="d-card d-card-pad-lg">
        <div className="d-card-title" style={{ marginBottom: 14 }}>Аккаунт</div>
        <div className="d-stat">
          <span className="d-stat-label">Email</span>
          <span className="d-stat-val">{user?.email || "—"}</span>
        </div>
        <div className="d-stat">
          <span className="d-stat-label">Тариф</span>
          <span className="d-stat-val" style={{ textTransform: "uppercase" }}>
            <span className="d-badge" style={{
              background: user?.plan_tier === "premium" ? "rgba(20,184,166,0.18)"
                : user?.plan_tier === "pro" ? "var(--d-accent-dim)"
                : "var(--d-bg-3)",
              color: user?.plan_tier === "premium" ? "var(--d-accent-3)"
                : user?.plan_tier === "pro" ? "var(--d-accent-2)"
                : "var(--d-text-1)",
            }}>
              {user?.plan_tier || "free"}
            </span>
          </span>
        </div>
        <div className="d-stat">
          <span className="d-stat-label">Email подтверждён</span>
          <span className={"d-stat-val " + (user?.is_verified ? "pos" : "neg")}>
            {user?.is_verified ? "✓ да" : "✕ нет"}
          </span>
        </div>
        <div className="d-stat">
          <span className="d-stat-label">Роль</span>
          <span className="d-stat-val">{user?.role || "user"}</span>
        </div>

        {!user?.is_verified && (
          <button className="d-btn d-btn-primary" onClick={sendVerify}
                  disabled={sendingVerify} style={{ width: "100%", marginTop: 14 }}>
            {sendingVerify ? <span className="d-spinner"/> : "📧 Отправить письмо для верификации"}
          </button>
        )}
      </div>

      <div className="d-card d-card-pad-lg">
        <div className="d-card-title" style={{ marginBottom: 14 }}>Безопасность</div>
        <div className="muted" style={{ fontSize: 12, lineHeight: 1.7, marginBottom: 14 }}>
          API-ключи бирж хранятся в БД с шифрованием Fernet. Telegram-сессия и push-подписки — изолированы per user.
          Если подозреваешь компрометацию аккаунта — завершите все сессии:
        </div>
        <button className="d-btn d-btn-danger" onClick={logoutAll} style={{ width: "100%" }}>
          <DIcon name="logout" size={14}/> Завершить все сессии
        </button>

        <div className="muted" style={{ fontSize: 11, marginTop: 18, lineHeight: 1.6 }}>
          ⚠ После выхода придётся заново залогиниться на всех устройствах. Telegram-listener останется работать — он не привязан к веб-сессии.
        </div>
      </div>
    </div>
  );
};

// ═══════════════════════════════════════════════════════════════════════════════
//                              ADMIN SCREEN
// ═══════════════════════════════════════════════════════════════════════════════
const AdminScreen = ({ user, pushToast, refreshTick }) => {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(true);
  const [filter, setFilter] = useState("");
  const [editUser, setEditUser] = useState(null);

  const reload = useCallback(async () => {
    setLoading(true);
    try {
      const r = await fetch("/api/admin/users").then(x => x.ok ? x.json() : []);
      setUsers(Array.isArray(r) ? r : []);
    } catch (e) {
      pushToast({ msg: "Ошибка загрузки: " + (e.message || e), err: true });
    }
    setLoading(false);
  }, [pushToast]);
  useEffect(() => { reload(); }, [reload, refreshTick]);

  if (user?.role !== "admin") {
    return (
      <div className="d-empty">
        <div className="d-empty-icon"><DIcon name="settings" size={28}/></div>
        <div className="d-empty-title">Доступ запрещён</div>
        <div className="d-empty-msg">Этот раздел доступен только администраторам.</div>
      </div>
    );
  }

  const filtered = filter
    ? users.filter(u => (u.email || "").toLowerCase().includes(filter.toLowerCase()))
    : users;

  const stats = {
    total: users.length,
    verified: users.filter(u => u.is_verified).length,
    admin: users.filter(u => u.role === "admin").length,
    pro: users.filter(u => u.plan_tier === "pro" || u.plan_tier === "premium").length,
  };

  return (
    <div>
      <div className="d-kpi-grid" style={{ marginBottom: 16 }}>
        <div className="d-kpi">
          <div className="d-kpi-label">Всего юзеров</div>
          <div className="d-kpi-value mono">{stats.total}</div>
        </div>
        <div className="d-kpi">
          <div className="d-kpi-label">Верифицированных</div>
          <div className="d-kpi-value mono pos">{stats.verified}</div>
          <div className="d-kpi-sub">{stats.total > 0 ? (stats.verified / stats.total * 100).toFixed(0) : 0}%</div>
        </div>
        <div className="d-kpi">
          <div className="d-kpi-label">Pro / Premium</div>
          <div className="d-kpi-value mono">{stats.pro}</div>
        </div>
        <div className="d-kpi">
          <div className="d-kpi-label">Админов</div>
          <div className="d-kpi-value mono">{stats.admin}</div>
        </div>
      </div>

      <div className="d-section-head">
        <div className="d-section-title">Пользователи <span className="d-tab-count">{filtered.length}</span></div>
        <input className="d-input" value={filter} onChange={e => setFilter(e.target.value)}
               placeholder="🔍 Поиск по email" style={{ maxWidth: 260 }}/>
      </div>

      <div className="d-card">
        {loading ? (
          <div style={{ padding: 16 }}><Skel h={200}/></div>
        ) : filtered.length === 0 ? (
          <div className="d-empty"><div className="d-empty-msg">Юзеров не найдено</div></div>
        ) : (
          <table className="d-table">
            <thead>
              <tr>
                <th>ID</th>
                <th>Email</th>
                <th>Роль</th>
                <th>Тариф</th>
                <th>Verified</th>
                <th>Создан</th>
                <th>Последний вход</th>
                <th style={{ textAlign: "right" }}>Действия</th>
              </tr>
            </thead>
            <tbody>
              {filtered.map(u => (
                <tr key={u.id}>
                  <td className="mono muted">{u.id}</td>
                  <td>
                    <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                      <div className="d-avatar" style={{ width: 24, height: 24, fontSize: 10 }}>
                        {(u.email || "?")[0].toUpperCase()}
                      </div>
                      <span style={{ fontWeight: 500 }}>{u.email}</span>
                    </div>
                  </td>
                  <td>
                    {u.role === "admin"
                      ? <span className="d-badge" style={{ background: "var(--d-accent-dim)", color: "var(--d-accent-3)" }}><DIcon name="crown" size={10} style={{ verticalAlign: "-1px", marginRight: 3 }}/> ADMIN</span>
                      : <span className="d-badge">user</span>}
                  </td>
                  <td>
                    <span className="d-badge" style={{
                      background: u.plan_tier === "premium" ? "rgba(20,184,166,0.18)"
                        : u.plan_tier === "pro" ? "var(--d-accent-dim)"
                        : "var(--d-bg-3)",
                      color: u.plan_tier === "premium" ? "var(--d-accent-3)"
                        : u.plan_tier === "pro" ? "var(--d-accent-2)"
                        : "var(--d-text-1)",
                    }}>{u.plan_tier || "free"}</span>
                  </td>
                  <td>
                    {u.is_verified
                      ? <span className="pos">✓</span>
                      : <span className="muted">—</span>}
                  </td>
                  <td className="muted mono" style={{ fontSize: 11 }}>{ago(u.created_at)}</td>
                  <td className="muted mono" style={{ fontSize: 11 }}>{u.last_login_at ? ago(u.last_login_at) : "—"}</td>
                  <td style={{ textAlign: "right" }}>
                    <button className="d-btn d-btn-sm" onClick={() => setEditUser(u)}>
                      ⚙ Изменить
                    </button>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        )}
      </div>

      {editUser && (
        <AdminEditUserModal
          user={editUser}
          adminUser={user}
          onClose={() => setEditUser(null)}
          onSaved={() => { setEditUser(null); reload(); pushToast({ msg: "Сохранено", ok: true }); }}
          onDeleted={() => { setEditUser(null); reload(); pushToast({ msg: "Юзер удалён", ok: true }); }}
          pushToast={pushToast}
        />
      )}
    </div>
  );
};

const AdminEditUserModal = ({ user, adminUser, onClose, onSaved, onDeleted, pushToast }) => {
  const [role, setRole] = useState(user.role || "user");
  const [planTier, setPlanTier] = useState(user.plan_tier || "free");
  const [busy, setBusy] = useState(false);
  const isSelf = user.id === adminUser.id;

  const save = async () => {
    setBusy(true);
    try {
      const r = await fetch(`/api/admin/users/${user.id}`, {
        method: "PATCH",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ role, plan_tier: planTier }),
      });
      if (!r.ok) throw new Error(await r.text() || "HTTP " + r.status);
      onSaved();
    } catch (e) {
      pushToast({ msg: "Ошибка: " + (e.message || e).slice(0, 120), err: true });
      setBusy(false);
    }
  };

  const remove = async () => {
    if (isSelf) { pushToast({ msg: "Нельзя удалить себя", err: true }); return; }
    if (!confirm(`Удалить юзера ${user.email}? Все его боты и подписки тоже удалятся.`)) return;
    setBusy(true);
    try {
      const r = await fetch(`/api/admin/users/${user.id}`, { method: "DELETE" });
      if (!r.ok) throw new Error(await r.text() || "HTTP " + r.status);
      onDeleted();
    } catch (e) {
      pushToast({ msg: "Ошибка: " + (e.message || e).slice(0, 120), err: true });
      setBusy(false);
    }
  };

  return (
    <div className="d-modal-backdrop" onClick={onClose}>
      <div className="d-modal" onClick={e => e.stopPropagation()}>
        <div className="d-modal-head">
          <div className="d-modal-title">Изменить пользователя</div>
          <button className="d-icon-btn" onClick={onClose}><DIcon name="x" size={14}/></button>
        </div>
        <div className="d-modal-body">
          <div className="d-stat">
            <span className="d-stat-label">ID</span>
            <span className="d-stat-val mono">{user.id}</span>
          </div>
          <div className="d-stat">
            <span className="d-stat-label">Email</span>
            <span className="d-stat-val">{user.email}</span>
          </div>
          <div className="d-stat">
            <span className="d-stat-label">Verified</span>
            <span className="d-stat-val">{user.is_verified ? "✓ да" : "нет"}</span>
          </div>

          <div className="d-field" style={{ marginTop: 14 }}>
            <div className="d-label">Роль</div>
            <div className="d-seg">
              <button className={"d-seg-btn" + (role === "user" ? " active" : "")} onClick={() => setRole("user")}>user</button>
              <button className={"d-seg-btn" + (role === "admin" ? " active" : "")} onClick={() => setRole("admin")}>admin</button>
            </div>
            {isSelf && role !== "admin" && (
              <div className="muted" style={{ fontSize: 11, marginTop: 4, color: "var(--d-warn)" }}>
                ⚠ Нельзя снять у себя роль admin — бэкенд отклонит.
              </div>
            )}
          </div>

          <div className="d-field">
            <div className="d-label">Тариф</div>
            <div className="d-seg" style={{ width: "100%" }}>
              {["free", "pro", "premium"].map(t => (
                <button key={t} className={"d-seg-btn" + (planTier === t ? " active" : "")}
                        style={{ flex: 1 }} onClick={() => setPlanTier(t)}>
                  {t}
                </button>
              ))}
            </div>
          </div>
        </div>
        <div className="d-modal-foot" style={{ justifyContent: "space-between" }}>
          <button className="d-btn d-btn-danger" onClick={remove} disabled={busy || isSelf}
                  title={isSelf ? "Нельзя удалить себя" : ""}>
            <DIcon name="trash" size={13}/> Удалить юзера
          </button>
          <div style={{ display: "flex", gap: 8 }}>
            <button className="d-btn d-btn-ghost" onClick={onClose}>Отмена</button>
            <button className="d-btn d-btn-primary" onClick={save} disabled={busy}>
              {busy ? <span className="d-spinner"/> : "Сохранить"}
            </button>
          </div>
        </div>
      </div>
    </div>
  );
};

// ─────────────────────────  Регистрация  ─────────────────────────
window.DesktopScreens = {
  dashboard: DashboardScreen,
  bots: BotsScreen,
  spot: SpotScreen,
  signals: SignalsScreen,
  journal: JournalScreen,
  stats: StatsScreen,
  settings: SettingsScreen,
  admin: AdminScreen,
};

})();  // конец IIFE-обёртки
