// ============================================================
// API4ATKA mobile — screens + cards + sheets
// ============================================================

// ------------------------------------------------------------
// PositionCard — tap to expand, swipe right = close, swipe left = edit
// ------------------------------------------------------------
// Мини-график OHLC для PositionCard expand. Линии: entry/SL/TP/mark.
const PositionMiniChart = ({ symbol, exchange, entry, sl, tps = [], mark, side }) => {
  const ref = useRef(null);
  const chartRef = useRef(null);
  const [error, setError] = useState(false);

  useEffect(() => {
    if (!ref.current || !window.LightweightCharts) {
      setError(true);
      return;
    }
    let cancelled = false;
    (async () => {
      const candles = await window.MobileAPI.loadKlines(exchange, symbol, "1H", 80);
      if (cancelled || !ref.current || !candles || candles.length === 0) {
        setError(true);
        return;
      }
      try {
        if (chartRef.current) {
          try { chartRef.current.remove(); } catch (e) {}
          chartRef.current = null;
        }
        const chart = window.LightweightCharts.createChart(ref.current, {
          width: ref.current.clientWidth, height: 140,
          layout: { background: { color: "transparent" }, textColor: "#7a7a7a" },
          grid: { vertLines: { visible: false }, horzLines: { color: "rgba(255,255,255,0.05)" } },
          timeScale: { borderVisible: false, timeVisible: true, secondsVisible: false },
          rightPriceScale: { borderVisible: false },
          handleScroll: false, handleScale: false,
        });
        const series = chart.addCandlestickSeries({
          upColor: "#22c55e", downColor: "#ef4444",
          borderVisible: false,
          wickUpColor: "#22c55e", wickDownColor: "#ef4444",
        });
        series.setData(candles.map(c => ({
          time: Math.floor(c.time_ms / 1000),
          open: c.open, high: c.high, low: c.low, close: c.close,
        })));
        // Линии: entry/SL/TPs/mark
        const lines = [];
        if (entry > 0) lines.push({ price: entry, color: "#ffffff", title: "Entry" });
        if (mark > 0) lines.push({ price: mark, color: "#4f9eff", title: "Mark" });
        if (sl > 0) lines.push({ price: sl, color: "#ef4444", title: "SL" });
        (tps || []).slice(0, 3).forEach((tp, i) => {
          if (tp > 0) lines.push({ price: tp, color: "#22c55e", title: `TP${i + 1}` });
        });
        lines.forEach(l => series.createPriceLine({
          price: l.price, color: l.color, lineWidth: 1,
          lineStyle: 2, axisLabelVisible: true, title: l.title,
        }));
        chart.timeScale().fitContent();
        chartRef.current = chart;
        const ro = new ResizeObserver(() => {
          if (ref.current && chartRef.current) {
            chartRef.current.applyOptions({ width: ref.current.clientWidth });
          }
        });
        ro.observe(ref.current);
        return () => { ro.disconnect(); try { chart.remove(); } catch (e) {} };
      } catch (e) { setError(true); }
    })();
    return () => {
      cancelled = true;
      if (chartRef.current) { try { chartRef.current.remove(); } catch (e) {} chartRef.current = null; }
    };
  }, [symbol, exchange, entry, sl, mark, JSON.stringify(tps || [])]);

  if (error) {
    return (
      <div style={{ padding: 10, color: "var(--text-3)", fontSize: 11, textAlign: "center" }}>
        {T("chart.unavailable", "График недоступен")}
      </div>
    );
  }
  return <div ref={ref} style={{ width: "100%", height: 140, marginTop: 8 }}/>;
};

// Считает Risk Score (0-100) для открытой позиции:
//   0   = безопасно
//   100 = почти ликвидирован
// Учитывает: дистанцию до liq, утилизацию маржи, относительный leverage.
const _riskScore = (p) => {
  const mark = parseFloat(p.mark || 0);
  const liq = parseFloat(p.liq || 0);
  const margin = parseFloat(p.margin || 0);
  const lev = parseInt(p.lev || 1);
  if (mark <= 0 || liq <= 0 || margin <= 0) return 0;
  // 1) Distance to liq (0=at liq, 1=full safety)
  const distPct = Math.abs(mark - liq) / mark * 100;
  // Маппим: 0% дистанции → 100 риска, 20% → 0 риска
  const liqScore = Math.max(0, Math.min(100, 100 - distPct * 5));
  // 2) Leverage utilization (1x → 0, 25x → 60, 100x+ → 100)
  const levScore = Math.min(100, lev * 2.5);
  // 3) PnL deep negative (худший pnl → больше риск)
  const pnlPct = parseFloat(p.pnlPct || 0);
  const pnlScore = pnlPct < 0 ? Math.min(100, Math.abs(pnlPct) * 1.5) : 0;
  // Weighted: liq важнее всего
  return Math.round(liqScore * 0.6 + levScore * 0.2 + pnlScore * 0.2);
};

const _riskColor = (score) => {
  if (score < 30) return "var(--long)";
  if (score < 60) return "var(--warn)";
  return "var(--short)";
};
const _riskLabel = (score) => {
  if (score < 30) return T("risk.low", "низкий");
  if (score < 60) return T("risk.medium", "средний");
  return T("risk.high", "ВЫСОКИЙ");
};

const PositionCard = ({ p, onClose, onEdit, exchange }) => {
  const [open, setOpen] = useState(false);
  const pulseCls = usePulse(p.pnl);
  const { dx, active, handlers } = useSwipe({
    onRight: () => onClose(p),
    onLeft: () => onEdit(p),
    threshold: 0.4,
  });
  const reveal = dx > 8 ? "left" : dx < -8 ? "right" : null;
  const up = p.pnl >= 0;
  const risk = _riskScore(p);
  const riskColor = _riskColor(risk);

  return (
    <div className="swipe-wrap">
      <div className="swipe-actions close-edit">
        <span className="left" style={{ opacity: reveal === "left" ? 1 : 0.3 }}><MIcon name="x" size={18}/> Закрыть</span>
        <span className="right" style={{ opacity: reveal === "right" ? 1 : 0.3 }}>Изменить <MIcon name="sliders" size={18}/></span>
      </div>
      <div className="swipe-fg pos-card" {...handlers}
        onClick={() => { if (Math.abs(dx) < 4) { buzz(); setOpen(o => !o); } }}
        style={{ transform: `translateX(${dx}px)`, transition: active ? "none" : "transform 180ms cubic-bezier(0.32,0.72,0,1)" }}>
        <div className="row1">
          <Coin sym={p.coin}/>
          <span className="sym">{p.sym}</span>
          <span className="meta">
            <Side side={p.side}/><span className="m-pill lev">{p.lev}x</span>
            <span title={T("risk.score", "Risk Score")} style={{
              display: "inline-flex", alignItems: "center", gap: 3,
              padding: "2px 6px", borderRadius: 4, marginLeft: 4,
              background: riskColor + "22", color: riskColor,
              fontSize: 10, fontWeight: 700, fontFamily: "var(--font-mono)",
            }}>
              <span style={{ width: 6, height: 6, borderRadius: 3, background: riskColor }}/>
              R{risk}
            </span>
          </span>
        </div>
        <div className="grid2">
          <div className="kv"><div className="k">{T("portfolio.size", "Размер")}</div><div className="v">{p.qty} {p.qtyUnit}</div></div>
          <div className="kv"><div className="k">{T("portfolio.margin", "Маржа")}</div><div className="v">{money(p.margin)} <span className="muted">USDT</span></div></div>
          <div className="kv"><div className="k">{T("home.entryToPrice", "Вход → Цена")}</div><div className="v">{price(p.entry)} <span className="arrow"><MIcon name="arrowR" size={11}/></span> {price(p.mark)}</div></div>
          <div className="kv"><div className="k">{T("home.liquidation", "Ликвидация")}</div><div className="v neg">{price(p.liq)}</div></div>
        </div>
        <div className="pnl-row">
          <span className={"pnl-big mono " + (up ? "pos " : "neg ") + pulseCls}>{signed(p.pnl)} <span style={{ fontSize: 12, color: "var(--text-3)" }}>USDT</span></span>
          <span className={"pnl-pct mono " + (up ? "pos" : "neg")}>{pct(p.pnlPct)}</span>
        </div>
        {open && (
          <>
            <PositionMiniChart
              symbol={p.sym} exchange={exchange}
              entry={p.entry} sl={p.sl}
              tps={p.tps || (p.tp ? [p.tp] : [])}
              mark={p.mark} side={p.side}/>
            <div className="pos-expand">
              <button className="m-button short sm" onClick={(e) => { e.stopPropagation(); onClose(p); }}>{T("home.closeAll", "Закрыть 100%")}</button>
              <button className="m-button sm" onClick={(e) => { e.stopPropagation(); onClose(p, true); }}>{T("home.part", "Часть")}</button>
              <button className="m-button ghost sm" onClick={(e) => { e.stopPropagation(); onEdit(p); }}>TP/SL</button>
            </div>
          </>
        )}
      </div>
    </div>
  );
};

// ------------------------------------------------------------
// Dashboard screen
// ------------------------------------------------------------
const DashboardScreen = ({ data, positions, onClosePos, onEditPos, exchange, onRefresh }) => {
  const b = data.balance;
  const eqPulse = usePulse(b.dayPnl);
  const winFrac = b.dayTrades ? b.dayWins / b.dayTrades : 0;
  const ptr = usePullToRefresh(onRefresh);
  return (
    <div className="m-scroll" ref={ptr.scrollRef} {...ptr.handlers}>
      <PullIndicator ptr={ptr.ptr} refreshing={ptr.refreshing}/>
      {/* Balance hero */}
      <div className="m-hero">
        <div className="h-label"><span>{T("home.balance", "Баланс")}</span></div>
        <div className="h-value mono">{money(b.equity)} <span className="ccy">USDT</span>
          <span className={"delta-chip " + (b.deltaPct >= 0 ? "up" : "down")} style={{ marginLeft: "auto", alignSelf: "center" }}>
            <MIcon name={b.deltaPct >= 0 ? "trendUp" : "down"} size={12}/>{pct(b.deltaPct, 0)}
          </span>
        </div>
        <div className="h-sub">{T("home.available", "Доступно")}: <span className="mono" style={{ color: "var(--text)" }}>{money(b.available)} USDT</span></div>
        <div className="h-spark"><Spark data={b.spark} color="var(--accent)"/></div>
      </div>

      {/* Day PnL hero */}
      <div className="m-hero">
        <div className="h-label"><span>{T("home.dailyPnl", "Прибыль за сутки")}</span><span className="mono" style={{ textTransform: "none", letterSpacing: 0 }}>{b.dayTrades} {T("home.tradesCount", "сделок")}</span></div>
        <div className={"h-value mono " + (b.dayPnl >= 0 ? "pos " : "neg ") + eqPulse}>
          {signed(b.dayPnl)} <span className="ccy">USDT</span>
          <span className={(b.dayPnl >= 0 ? "pos" : "neg")} style={{ fontSize: 14, marginLeft: 2 }}>({pct(b.dayPnlPct)})</span>
        </div>
        <div className="m-progress">
          <i className="win" style={{ width: (winFrac * 100) + "%" }}></i>
          <i className="loss" style={{ width: ((1 - winFrac) * 100) + "%" }}></i>
        </div>
        <div className="h-sub mono" style={{ display: "flex", gap: 12, fontSize: 12 }}>
          <span className="pos">{b.dayWins}W</span><span className="neg">{b.dayLosses}L</span>
          <span className="muted">{T("home.winRate", "win rate")} {(winFrac * 100).toFixed(0)}%</span>
        </div>
      </div>

      {/* Open positions */}
      <div className="m-section-h">
        <span className="t">{T("home.openPositions", "Открытые позиции")} <span className="count">{positions.length}</span></span>
        <span className="a">{T("home.swipe", "свайп →")}</span>
      </div>
      {positions.length === 0 ? (
        <div className="m-card" style={{ textAlign: "center", color: "var(--text-3)", padding: 28 }}>
          <MIcon name="layers" size={32} sw={1.2} style={{ marginBottom: 8 }}/>
          <div style={{ fontSize: 14, color: "var(--text-2)" }}>{T("portfolio.noOpen", "Нет открытых позиций")}</div>
          <div style={{ fontSize: 12, marginTop: 2 }}>{T("home.botWillOpen", "Бот откроет сделку при следующем сигнале")}</div>
        </div>
      ) : positions.map(p => (
        <PositionCard key={p.id} p={p} onClose={onClosePos} onEdit={onEditPos} exchange={exchange}/>
      ))}
    </div>
  );
};

// ------------------------------------------------------------
// Journal screen — segmented filter, time pills, date dividers, pull-to-refresh
// ------------------------------------------------------------
const TradeCard = ({ t, onToast }) => {
  const [open, setOpen] = useState(false);
  const up = t.pnl >= 0;
  const ri = t.reason === "tp" ? "target" : t.reason === "sl" ? "stop" : "hand";

  const { dx, active, handlers } = useSwipe({
    onRight: () => setOpen(true),
    onLeft: async () => {
      try {
        await navigator.clipboard.writeText(t.rawSignal || `${t.sym} ${t.side} ${signed(t.pnl)} USDT`);
        onToast && onToast({ msg: T("toast.copied", "Скопировано") });
      } catch (e) {
        onToast && onToast({ msg: T("error.copy", "Не удалось скопировать"), err: true });
      }
    },
    threshold: 0.3,
  });
  const reveal = dx > 8 ? "left" : dx < -8 ? "right" : null;

  return (
    <div className="swipe-wrap" style={{ marginBottom: 8 }}>
      <div className="swipe-actions" style={{
        background: "linear-gradient(90deg, var(--long-bg), transparent 40%, transparent 60%, var(--accent-dim))",
      }}>
        <span className="left" style={{ opacity: reveal === "left" ? 1 : 0.3 }}>
          <MIcon name="layers" size={16}/> Детали
        </span>
        <span className="right" style={{ opacity: reveal === "right" ? 1 : 0.3 }}>
          Копировать <MIcon name="journal" size={16}/>
        </span>
      </div>
      <div className="swipe-fg trade-card" {...handlers}
        onClick={() => { if (Math.abs(dx) < 4) { buzz(); setOpen(o => !o); } }}
        style={{
          cursor: "pointer",
          transform: `translateX(${dx}px)`,
          transition: active ? "none" : "transform 180ms cubic-bezier(0.32,0.72,0,1)",
        }}>
        <div className="tr1">
          <Coin sym={t.coin} size="sm"/>
          <span className="tr-sym">{t.sym}</span>
          <Side side={t.side}/>
          {t.lev > 0 && <span className="m-pill lev">{t.lev}x</span>}
          <span className={"close-reason " + t.reason} title={t.reason}>
            <MIcon name={ri} size={14}/>
          </span>
          <MIcon name={open ? "up" : "down"} size={14} color="var(--text-4)" style={{ marginLeft: 4 }}/>
        </div>
        <div style={{ display: "flex", alignItems: "center", gap: 6, marginTop: 4, fontSize: 11 }}>
          {t.botManaged ? (
            <span style={{ display: "inline-flex", alignItems: "center", gap: 4, color: "var(--accent)", background: "var(--accent-dim)", padding: "1px 6px", borderRadius: 4 }}>
              <MIcon name="plane" size={10}/>
              {t.channel ? `из «${t.channel}»` : T("journal.fromBot", "из бота")}
            </span>
          ) : (
            <span style={{ display: "inline-flex", alignItems: "center", gap: 4, color: "var(--text-3)", background: "var(--bg-2)", padding: "1px 6px", borderRadius: 4 }}>
              <MIcon name="key" size={10}/>
              биржа
            </span>
          )}
        </div>
        <div className="tr-prices">{price(t.entry)} <span className="arrow"><MIcon name="arrowR" size={11}/></span> {price(t.exit)}</div>
        <div className="tr-bottom">
          <span className={"tr-pnl mono " + (up ? "pos" : "neg")}>
            {signed(t.pnl)} <span className="mono" style={{ fontSize: 12, color: up ? "var(--long)" : "var(--short)", opacity: 0.7 }}>({pct(t.pnlPct)})</span>
          </span>
          <span className="tr-time">{t.time}</span>
        </div>

        {open && (
          <div style={{ marginTop: 10, paddingTop: 10, borderTop: "1px solid var(--border)" }}>
            {t.channel && (
              <div style={{ display: "flex", alignItems: "center", gap: 6, fontSize: 12, color: "var(--text-2)", marginBottom: 8 }}>
                <MIcon name="plane" size={12} color="var(--accent)"/>
                <span>из «{t.channel}»</span>
                {t.sizeUsdt > 0 && <span style={{ marginLeft: "auto", color: "var(--text-3)" }}>{money(t.sizeUsdt)} USDT</span>}
              </div>
            )}
            {(t.origSl > 0 || (t.origTps && t.origTps.length > 0)) && (
              <div style={{ background: "var(--bg-2)", borderRadius: "var(--r-1)", padding: "8px 10px", fontSize: 12, lineHeight: 1.7 }}>
                {t.origSl > 0 && (
                  <div style={{ display: "flex", justifyContent: "space-between" }}>
                    <span className="muted">{T("journal.signalSl", "SL сигнала")}</span>
                    <span className="mono neg">{price(t.origSl)}</span>
                  </div>
                )}
                {t.origTps && t.origTps.length > 0 && (
                  <div style={{ display: "flex", justifyContent: "space-between" }}>
                    <span className="muted">{T("journal.signalTp", "TP сигнала")}</span>
                    <span className="mono pos" style={{ display: "flex", gap: 6, flexWrap: "wrap", justifyContent: "flex-end" }}>
                      {t.origTps.map((tp, i) => <span key={i}>{price(tp)}</span>)}
                    </span>
                  </div>
                )}
              </div>
            )}
            {t.rawSignal && (
              <details style={{ marginTop: 8 }} onClick={e => e.stopPropagation()}>
                <summary style={{ fontSize: 11, color: "var(--text-3)", cursor: "pointer" }}>{T("journal.sourceText", "исходный текст")}</summary>
                <pre style={{ fontSize: 11, color: "var(--text-2)", whiteSpace: "pre-wrap", wordBreak: "break-word", marginTop: 6, padding: 8, background: "var(--bg-1)", borderRadius: "var(--r-1)", maxHeight: 200, overflow: "auto" }}>
                  {t.rawSignal}
                </pre>
              </details>
            )}
            {t.orderId && (
              <div style={{ fontSize: 10, color: "var(--text-4)", marginTop: 6, fontFamily: "var(--font-mono)" }}>
                order: {String(t.orderId).slice(0, 40)}
              </div>
            )}
            {t.botManaged && t.tradeId && (
              <button className="m-button sm block" style={{ marginTop: 10 }}
                onClick={async (e) => {
                  e.stopPropagation();
                  buzz();
                  try {
                    const url = window.MobileAPI.pnlCardUrl(t.tradeId);
                    const r = await fetch(url, { credentials: "include" });
                    if (!r.ok) throw new Error("HTTP " + r.status);
                    const blob = await r.blob();
                    const file = new File([blob], `pnl_${t.sym}_${t.tradeId}.png`, { type: "image/png" });
                    if (navigator.canShare && navigator.canShare({ files: [file] })) {
                      await navigator.share({ files: [file], title: `${t.sym} ${t.side}` });
                    } else {
                      const objUrl = URL.createObjectURL(blob);
                      const a = document.createElement("a");
                      a.href = objUrl; a.download = file.name; a.click();
                      setTimeout(() => URL.revokeObjectURL(objUrl), 1000);
                    }
                  } catch (err) {
                    onToast && onToast({ msg: "Не получилось: " + (err.message || err).slice(0, 80), err: true });
                  }
                }}>
                <MIcon name="arrowR" size={14}/> Поделиться карточкой PnL
              </button>
            )}
          </div>
        )}
      </div>
    </div>
  );
};

// ============================================================
// Mode switcher (Futures / Spot / Bots) — segmented control под TopBar
// ============================================================
const ModeSwitcher = ({ mode, onChange, exchange }) => {
  const supports = (window.MobileAPI && window.MobileAPI.getTradingModes(exchange)) || { futures: true, spot: false, bots: false };
  // Боты всегда доступны: даже если биржа не имеет встроенных Grid-ботов
  // (WEEX/Binance/Bybit), наши ЛОКАЛЬНЫЕ боты работают на любой бирже
  // через universal_place_market.
  const tabs = [
    { k: "futures", label: T("mode.futures", "Фьючерсы"), enabled: supports.futures },
    { k: "spot", label: T("mode.spot", "Спот"), enabled: supports.spot },
    { k: "bots", label: T("mode.bots", "Боты"), enabled: true },
  ];
  return (
    <div className="m-mode-tabs" style={{
      display: "flex", gap: 4, padding: "8px 14px 0",
      background: "var(--bg-1)", borderBottom: "1px solid var(--border)",
    }}>
      {tabs.map(t => (
        <button key={t.k}
          className={"m-mode-tab" + (mode === t.k ? " active" : "") + (!t.enabled ? " disabled" : "")}
          disabled={!t.enabled}
          onClick={() => { if (t.enabled) { buzz(); onChange(t.k); } }}
          style={{
            flex: 1, padding: "10px 10px",
            background: mode === t.k ? "var(--accent-dim)" : "transparent",
            color: mode === t.k ? "var(--accent)" : (t.enabled ? "var(--text-2)" : "var(--text-4)"),
            border: "none", borderRadius: "var(--r-1) var(--r-1) 0 0",
            fontSize: 13, fontWeight: mode === t.k ? 700 : 500,
            cursor: t.enabled ? "pointer" : "not-allowed",
            opacity: t.enabled ? 1 : 0.4,
            transition: "all 140ms ease",
          }}>
          {t.label}
        </button>
      ))}
    </div>
  );
};

// ============================================================
// SpotScreen — балансы по монетам + объём/комиссии
// ============================================================
const SpotScreen = ({ exchange, onToast }) => {
  const [bal, setBal] = useState(null);
  const [ana, setAna] = useState(null);
  const [period, setPeriod] = useState(30);
  const [loading, setLoading] = useState(true);

  const load = useCallback(async () => {
    setLoading(true);
    const [b, a] = await Promise.all([
      window.MobileAPI.loadSpotBalance(exchange),
      window.MobileAPI.loadSpotAnalytics(exchange, period),
    ]);
    setBal(b); setAna(a); setLoading(false);
  }, [exchange, period]);
  useEffect(() => { load(); }, [load]);

  const ptr = usePullToRefresh(load);

  if (loading) return <div className="m-scroll"><SkelCard/><SkelCard/></div>;

  return (
    <div className="m-scroll" ref={ptr.scrollRef} {...ptr.handlers}>
      <PullIndicator ptr={ptr.ptr} refreshing={ptr.refreshing}/>
      <div className="m-hero">
        <div className="muted" style={{ fontSize: 11 }}>{T("spot.portfolioValue", "Стоимость спот-портфеля")}</div>
        <div className="mono" style={{ fontSize: 30, fontWeight: 700, marginTop: 6 }}>
          {money(bal?.totalUsdt || 0)} <span className="muted" style={{ fontSize: 14 }}>USDT</span>
        </div>
        <div className="muted" style={{ fontSize: 11, marginTop: 4 }}>{bal?.assets?.length || 0} активов</div>
      </div>

      <div className="m-segmented" style={{ margin: "12px 14px" }}>
        {[[7, T("stats.range.7d", "7 дн")], [30, T("stats.range.30d", "30 дн")], [90, T("stats.range.90d", "90 дн")]].map(([d, l]) => (
          <button key={d} className={period === d ? "active" : ""}
            onClick={() => { buzz(); setPeriod(d); }}>{l}</button>
        ))}
      </div>

      {ana && ana.trades > 0 && (
        <div className="m-card" style={{ margin: "0 14px 12px" }}>
          <div className="muted" style={{ fontSize: 11, marginBottom: 8 }}>{T("stats.activity", "Активность за период")}</div>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 12, fontSize: 13 }}>
            <div><div className="muted" style={{ fontSize: 10 }}>{T("stats.volume", "Объём")}</div><div className="mono">{money(ana.volume)}</div></div>
            <div><div className="muted" style={{ fontSize: 10 }}>{T("bots.fees", "Комиссии")}</div><div className="mono neg">-{money(ana.fees)}</div></div>
            <div><div className="muted" style={{ fontSize: 10 }}>{T("stats.trades", "Сделок")}</div><div className="mono">{ana.trades}</div></div>
          </div>
          {(ana.buys > 0 || ana.sells > 0) && (
            <div style={{ marginTop: 10, display: "flex", gap: 8, fontSize: 12 }}>
              <span className="pos">↑ {ana.buys} buy</span>
              <span style={{ color: "var(--text-3)" }}>·</span>
              <span className="neg">↓ {ana.sells} sell</span>
            </div>
          )}
        </div>
      )}

      <div className="sheet-label" style={{ padding: "0 14px", marginTop: 6 }}>
        Активы {bal && bal.main.length > 0 && <span className="muted" style={{ fontWeight: 400 }}>({bal.main.length})</span>}
      </div>
      {(!bal || bal.assets.length === 0) && (
        <EmptyState icon="wallet" title={T("spot.emptyBalance", "Спот-баланс пуст")}
          msg={T("spot.emptyHint", "Купи активы на споте — появятся здесь.")}/>
      )}
      {bal && bal.main.map(a => <SpotAssetRow key={a.asset} a={a}/>)}

      {bal && bal.main.length === 0 && bal.dust.length > 0 && (
        <div style={{ padding: "0 14px", color: "var(--text-3)", fontSize: 13, lineHeight: 1.5 }}>
          На споте только мелкие остатки (пыль) — показаны ниже.
        </div>
      )}

      {bal && bal.dust.length > 0 && (
        <DustBlock dust={bal.dust}/>
      )}
    </div>
  );
};

const SpotAssetRow = ({ a }) => (
  <div className="m-card" style={{ margin: "0 14px 8px", display: "flex", alignItems: "center", gap: 12 }}>
    <Coin sym={a.asset} size="sm"/>
    <div style={{ flex: 1, minWidth: 0 }}>
      <div style={{ fontWeight: 600, fontSize: 14 }}>{a.asset}</div>
      <div className="mono" style={{ fontSize: 11, color: "var(--text-3)" }}>
        {a.total.toFixed(a.total >= 1 ? 4 : 8)}
        {(a.earnAmt > 0 || a.fundingAmt > 0) && (
          <span style={{ marginLeft: 6 }}>
            {a.earnAmt > 0 && <span style={{ color: "var(--accent)" }}>{a.earnAmt.toFixed(4)} Earn </span>}
            {a.fundingAmt > 0 && <span style={{ color: "var(--text-3)" }}>{a.fundingAmt.toFixed(4)} Fund</span>}
          </span>
        )}
      </div>
    </div>
    <div style={{ textAlign: "right" }}>
      <div className="mono" style={{ fontWeight: 700, fontSize: 14 }}>
        {a.valueUsdt > 0 ? money(a.valueUsdt) : "—"} <span className="muted" style={{ fontSize: 11 }}>$</span>
      </div>
    </div>
  </div>
);

const DustBlock = ({ dust }) => {
  const [open, setOpen] = useState(false);
  const total = dust.reduce((s, a) => s + a.valueUsdt, 0);
  return (
    <div style={{ margin: "12px 14px 0" }}>
      <button
        onClick={() => { buzz(); setOpen(o => !o); }}
        className="m-card"
        style={{ width: "100%", display: "flex", alignItems: "center", gap: 12, cursor: "pointer", background: "var(--bg-2)" }}>
        <MIcon name="layers" size={16} color="var(--text-3)"/>
        <div style={{ flex: 1, textAlign: "left" }}>
          <div style={{ fontSize: 13, fontWeight: 600 }}>Пыль · {dust.length} активов</div>
          <div className="muted mono" style={{ fontSize: 11 }}>суммарно ~{money(total)} $</div>
        </div>
        <MIcon name={open ? "up" : "down"} size={14} color="var(--text-3)"/>
      </button>
      {open && (
        <div style={{ marginTop: 8 }}>
          {dust.map(a => <SpotAssetRow key={a.asset} a={a}/>)}
        </div>
      )}
    </div>
  );
};

// ============================================================
// BotsScreen
// ============================================================
const BOT_TYPE_LABELS = {
  grid: T("bots.spotGrid", "Спот-грид"),
  contract_grid: T("bots.futGrid", "Фьюч-грид"),
  moon_grid: T("bots.moonGrid", "Moon-грид"),
  recurring: "DCA",
  dca: "DCA",
  signal: "Signal",
};
const STATE_LABELS = {
  running: T("bots.status.running", "работает"), paused: T("bots.status.paused", "пауза"), stopped: T("bots.status.stopped", "остановлен"),
  cancelled: T("bots.status.canceled", "отменён"), canceled: T("bots.status.canceled", "отменён"), manualstop: T("bots.status.stopped", "остановлен"),
};
const BotCard = ({ b, history, onSelect }) => {
  const up = (b.pnl || 0) >= 0;
  const typeLabel = BOT_TYPE_LABELS[b.type] || b.type || T("bots.title", "Бот");
  const stateLabel = STATE_LABELS[b.state] || b.state || "";
  return (
    <div className="m-card" style={{ marginBottom: 8, cursor: onSelect ? "pointer" : "default" }}
      onClick={() => { if (onSelect) { buzz(); onSelect(b); } }}>
      <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
        <span className="si" style={{
          width: 32, height: 32, borderRadius: 8,
          background: b.type === "contract_grid" ? "rgba(244, 67, 54, 0.15)" : "var(--accent-dim)",
          display: "grid", placeItems: "center",
        }}>
          <MIcon name="bot" size={16}
            color={b.type === "contract_grid" ? "var(--short)" : "var(--accent)"}/>
        </span>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontWeight: 600, fontSize: 14, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>
            {b.symbol}
          </div>
          <div className="muted" style={{ fontSize: 11 }}>
            <span style={{ color: "var(--text-2)" }}>{typeLabel}</span>
            {!history && stateLabel && <> · <span style={{
              color: b.state === "running" ? "var(--long)" : "var(--text-3)",
            }}>{stateLabel}</span></>}
          </div>
        </div>
        <div style={{ textAlign: "right" }}>
          <div className={"mono " + (up ? "pos" : "neg")} style={{ fontWeight: 700 }}>
            {up ? "+" : ""}{money(b.pnl)} {b.currency || "USDT"}
          </div>
          <div className="muted mono" style={{ fontSize: 10, marginTop: 2 }}>
            {b.invested > 0 && <>invest {money(b.invested)}</>}
            {b.pnlPct !== 0 && <> · {b.pnlPct > 0 ? "+" : ""}{b.pnlPct.toFixed(2)}%</>}
          </div>
        </div>
      </div>
    </div>
  );
};

const BotsScreen = ({ exchange, onToast, onSelectBot, onCreateLocal, onSelectLocalBot, refreshTick }) => {
  const [active, setActive] = useState(null);
  const [history, setHistory] = useState(null);
  const [localBots, setLocalBots] = useState([]);
  const [tab, setTab] = useState("active");          // локальные боты: active/history
  const [exTab, setExTab] = useState("active");      // биржевые боты: active/history (только для OKX)
  const [period, setPeriod] = useState(30);
  const [loading, setLoading] = useState(true);
  const [supported, setSupported] = useState(true);

  const load = useCallback(async () => {
    setLoading(true);
    // Локальные боты грузим всегда (они кросс-биржевые)
    const lb = await window.MobileAPI.listLocalBots();
    setLocalBots(lb || []);
    const supports = window.MobileAPI.getTradingModes(exchange);
    if (!supports.bots) { setSupported(false); setLoading(false); return; }
    const [a, h] = await Promise.all([
      window.MobileAPI.loadBotsActive(exchange),
      window.MobileAPI.loadBotsHistory(exchange, period),
    ]);
    setActive(a); setHistory(h); setLoading(false);
  }, [exchange, period]);
  useEffect(() => { load(); }, [load]);
  // Реагирует на refreshTick (инкрементится из MobileApp при create/delete/start/stop)
  useEffect(() => { if (refreshTick > 0) load(); }, [refreshTick, load]);

  const toggleLocalBot = async (b) => {
    try {
      if (b.state === "running") await window.MobileAPI.stopLocalBot(b.id);
      else await window.MobileAPI.startLocalBot(b.id);
      await load();
      onToast({ msg: b.state === "running" ? T("toast.botStopped", "Остановлен") : T("toast.botStarted", "Запущен") });
    } catch (e) { onToast({ msg: "Ошибка: " + (e.message || e).slice(0, 80), err: true }); }
  };
  const deleteLocalBot = async (b) => {
    if (!confirm(`Удалить бота ${b.symbol}?`)) return;
    try {
      await window.MobileAPI.deleteLocalBot(b.id);
      await load();
      onToast({ msg: T("creds.toast.deleted", "Удалён") });
    } catch (e) { onToast({ msg: T("common.error", "Ошибка"), err: true }); }
  };

  const ptr = usePullToRefresh(load);

  if (loading) return <div className="m-scroll"><SkelCard/><SkelCard/></div>;

  const totalActivePnl = (active || []).reduce((s, b) => s + (b.pnl || 0), 0);
  const runningLocal = localBots.filter(b => b.state === "running").length;
  const ACTIVE_STATES = ["running", "paused", "starting"];
  const localActive = localBots.filter(b => ACTIVE_STATES.includes(b.state));
  const localHistory = localBots.filter(b => !ACTIVE_STATES.includes(b.state));
  const localTotalLivePnl = localActive.reduce((s, b) => {
    const upnl = b.live && typeof b.live.unrealized_pnl === "number" ? b.live.unrealized_pnl : 0;
    return s + (Number(b.total_pnl) || 0) + upnl;
  }, 0);

  return (
    <div className="m-scroll" ref={ptr.scrollRef} {...ptr.handlers}>
      <PullIndicator ptr={ptr.ptr} refreshing={ptr.refreshing}/>
      {/* Компактный summary — только если есть боты */}
      {(runningLocal > 0 || (active?.length || 0) > 0) && (
        <div className="m-hero" style={{ padding: 12 }}>
          <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
            <div>
              <div className="muted" style={{ fontSize: 10 }}>{T("bots.running", "Активные")}</div>
              <div className="mono" style={{ fontSize: 22, fontWeight: 700 }}>
                {runningLocal + (active?.length || 0)}
              </div>
            </div>
            {(localTotalLivePnl !== 0 || totalActivePnl !== 0) && (
              <div style={{ textAlign: "right" }}>
                <div className="muted" style={{ fontSize: 10 }}>PnL</div>
                <div className={"mono " + ((localTotalLivePnl + totalActivePnl) >= 0 ? "pos" : "neg")}
                     style={{ fontSize: 18, fontWeight: 700 }}>
                  {(localTotalLivePnl + totalActivePnl) >= 0 ? "+" : ""}{money(localTotalLivePnl + totalActivePnl)}
                </div>
              </div>
            )}
          </div>
        </div>
      )}

      {/* Локальные боты API4ATKA */}
      <div className="sheet-label" style={{ padding: "12px 14px 0", display: "flex", alignItems: "center", justifyContent: "space-between" }}>
        <span>{T("bots.localApi4atka", "Локальные боты API4ATKA")}</span>
        <button className="m-button sm" onClick={() => { buzz(); onCreateLocal && onCreateLocal(); }}>
          <MIcon name="plus" size={14}/> Создать
        </button>
      </div>

      {/* Tab Активные / История для ЛОКАЛЬНЫХ ботов */}
      <div className="m-segmented" style={{ margin: "10px 14px" }}>
        <button className={tab === "active" ? "active" : ""} onClick={() => { buzz(); setTab("active"); }}>
          {T("bots.activeTab", "Активные")} <span className="n">{localActive.length}</span>
        </button>
        <button className={tab === "history" ? "active" : ""} onClick={() => { buzz(); setTab("history"); }}>
          {T("bots.historyTab", "История")} <span className="n">{localHistory.length}</span>
        </button>
      </div>

      <div style={{ padding: "0 14px" }}>
        {tab === "active" && localActive.length === 0 && (
          <div className="m-card" style={{ padding: 14, color: "var(--text-3)", fontSize: 12, textAlign: "center" }}>
            {T("bots.empty.hint", "Создай Grid/DCA-бота — работает на любой бирже")}
          </div>
        )}
        {tab === "history" && localHistory.length === 0 && (
          <div className="m-card" style={{ padding: 14, color: "var(--text-3)", fontSize: 12, textAlign: "center" }}>
            {T("bots.history.empty", "История пуста — остановленные боты появятся здесь")}
          </div>
        )}
        {(tab === "active" ? localActive : localHistory).map(b => (
          <div key={b.id} className="m-card"
            style={{ marginBottom: 8, padding: 12, cursor: onSelectLocalBot ? "pointer" : "default" }}
            onClick={() => { if (onSelectLocalBot) { buzz(); onSelectLocalBot(b); } }}>
            <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
              <span className="si" style={{
                width: 32, height: 32, borderRadius: 8,
                background: b.state === "running" ? "var(--long-bg)" : "var(--bg-2)",
                display: "grid", placeItems: "center",
              }}>
                <MIcon name="bot" size={16} color={b.state === "running" ? "var(--long)" : "var(--text-3)"}/>
              </span>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontWeight: 600, fontSize: 14 }}>{b.symbol}</div>
                <div className="muted" style={{ fontSize: 11 }}>
                  {b.bot_type.toUpperCase()} · {b.direction} · {b.exchange.toUpperCase()} · {b.state}
                </div>
              </div>
              <div style={{ textAlign: "right" }}>
                {(() => {
                  const upnl = b.live && typeof b.live.unrealized_pnl === "number" ? b.live.unrealized_pnl : null;
                  const realized = Number(b.total_pnl) || 0;
                  // Combined: realized + unrealized для running ботов
                  const total = upnl !== null ? realized + upnl : realized;
                  return (
                    <div className={"mono " + (total >= 0 ? "pos" : "neg")} style={{ fontSize: 13, fontWeight: 700 }}>
                      {total >= 0 ? "+" : ""}{money(total)}
                    </div>
                  );
                })()}
                {b.live && typeof b.live.unrealized_pnl === "number" && (
                  <div className="muted mono" style={{ fontSize: 10 }}>
                    uPnL <span className={b.live.unrealized_pnl >= 0 ? "pos" : "neg"}>
                      {b.live.unrealized_pnl >= 0 ? "+" : ""}{money(b.live.unrealized_pnl)}
                    </span>
                  </div>
                )}
                <div className="muted mono" style={{ fontSize: 10 }}>
                  inv {money(b.investment_usdt)}
                </div>
                {b.live && b.live.liquidation_price > 0 && (
                  <div className="mono" style={{ fontSize: 10, color: "var(--short)" }}>
                    liq {b.live.liquidation_price.toPrecision(5)}
                  </div>
                )}
              </div>
            </div>
            <div style={{ display: "flex", gap: 6, marginTop: 8 }}>
              <button className="m-button sm" style={{ flex: 1 }}
                onClick={(e) => { e.stopPropagation(); toggleLocalBot(b); }}>
                {b.state === "running" ? T("bots.stop", "⏸ Стоп") : T("bots.start", "▶ Старт")}
              </button>
              <button className="m-button danger sm"
                onClick={(e) => { e.stopPropagation(); deleteLocalBot(b); }}>
                <MIcon name="x" size={14}/>
              </button>
            </div>
          </div>
        ))}
      </div>

      {/* Биржевые боты — ТОЛЬКО для бирж где они есть (OKX). Для WEEX/Binance/Bybit скрываем полностью. */}
      {supported && (
        <>
          <div className="sheet-label" style={{ padding: "16px 14px 0" }}>
            {T("bots.exchangeBotsTitle", "Биржевые боты")} ({exchange.toUpperCase()})
          </div>
          <div className="m-segmented" style={{ margin: "10px 14px" }}>
            <button className={exTab === "active" ? "active" : ""} onClick={() => { buzz(); setExTab("active"); }}>
              {T("bots.activeTab", "Активные")} <span className="n">{(active || []).length}</span>
            </button>
            <button className={exTab === "history" ? "active" : ""} onClick={() => { buzz(); setExTab("history"); }}>
              {T("bots.historyTab", "История")} <span className="n">{(history || []).length}</span>
            </button>
          </div>
          {exTab === "history" && (
            <div className="m-filter-row" style={{ padding: "0 14px" }}>
              {[[7, T("common.period.7d", "7д")], [30, T("common.period.30d", "30д")], [90, T("common.period.90d", "90д")]].map(([d, l]) => (
                <button key={d} className={"m-chip" + (period === d ? " active" : "")}
                  onClick={() => { buzz(); setPeriod(d); }}>{l}</button>
              ))}
            </div>
          )}
          <div style={{ padding: "10px 14px 0" }}>
            {exTab === "active" && (
              (active || []).length === 0
                ? <EmptyState icon="bot" title={T("bots.emptyActive.title", "Нет биржевых ботов")} msg={T("bots.emptyActive.hint", "Запусти Grid / DCA-бота в приложении биржи.")}/>
                : (active || []).map(b => <BotCard key={b.id} b={b} onSelect={onSelectBot}/>)
            )}
            {exTab === "history" && (
              (history || []).length === 0
                ? <EmptyState icon="bot" title={T("bots.emptyHistory.title", "Нет истории")} msg={T("bots.emptyHistory.hint", "За период ботов не было.")}/>
                : (history || []).map(b => <BotCard key={b.id} b={b} history onSelect={onSelectBot}/>)
            )}
          </div>
        </>
      )}
    </div>
  );
};

const JournalScreen = ({ data, onRefresh, onToast, onCustomRange }) => {
  const [filter, setFilter] = useState("all");
  const [time, setTime] = useState("week");
  const [custom, setCustom] = useState(null);  // { from: "YYYY-MM-DD", to: "..." }
  const [showCustom, setShowCustom] = useState(false);
  const [refreshing, setRefreshing] = useState(false);
  const [ptr, setPtr] = useState(0);
  const scrollRef = useRef(null);
  const st = useRef({ y: 0, pulling: false, id: null });

  const counts = useMemo(() => {
    let all = 0, win = 0, loss = 0;
    data.journal.forEach(d => d.trades.forEach(t => { all++; t.pnl >= 0 ? win++ : loss++; }));
    return { all, win, loss };
  }, [data]);

  const days = useMemo(() => {
    const now = new Date();
    let from = null, to = null;
    if (time === "custom" && custom) {
      from = custom.from ? new Date(custom.from + "T00:00:00") : null;
      to = custom.to ? new Date(custom.to + "T23:59:59") : null;
    } else if (time === "today") {
      from = new Date(now); from.setHours(0, 0, 0, 0);
    } else if (time === "week") {
      from = new Date(now.getTime() - 7 * 86400000);
    } else if (time === "month") {
      from = new Date(now.getTime() - 30 * 86400000);
    }
    return data.journal
      .filter(d => {
        const dd = new Date(d.date + "T12:00:00Z");
        if (from && dd < from) return false;
        if (to && dd > to) return false;
        return true;
      })
      .map(d => ({ ...d, trades: d.trades.filter(t => filter === "all" || (filter === "win" ? t.pnl >= 0 : t.pnl < 0)) }))
      .filter(d => d.trades.length);
  }, [filter, time, custom, data]);

  // pull to refresh via pointer
  const down = (e) => {
    if (scrollRef.current && scrollRef.current.scrollTop <= 0) st.current = { y: e.clientY, pulling: true, id: e.pointerId };
  };
  const move = (e) => {
    if (!st.current.pulling || st.current.id !== e.pointerId) return;
    const d = e.clientY - st.current.y;
    if (d > 0 && scrollRef.current.scrollTop <= 0) setPtr(Math.min(70, d * 0.5));
  };
  const up = async () => {
    if (!st.current.pulling) return;
    if (ptr > 48) {
      setRefreshing(true); buzz();
      try { if (onRefresh) await onRefresh(); } catch (e) {}
      setRefreshing(false); setPtr(0);
    } else setPtr(0);
    st.current.pulling = false;
  };

  return (
    <>
      <div style={{ padding: "12px 14px 0" }}>
        <div className="m-segmented">
          <button className={filter === "all" ? "active" : ""} onClick={() => { buzz(); setFilter("all"); }}>Все <span className="n">{counts.all}</span></button>
          <button className={filter === "win" ? "active" : ""} onClick={() => { buzz(); setFilter("win"); }}>Прибыль <span className="n">{counts.win}</span></button>
          <button className={filter === "loss" ? "active" : ""} onClick={() => { buzz(); setFilter("loss"); }}>Убыток <span className="n">{counts.loss}</span></button>
        </div>
        <div className="m-filter-row">
          {[["today", T("stats.today", "Сегодня")], ["week", T("stats.week", "Неделя")], ["month", T("stats.month", "Месяц")]].map(([k, v]) => (
            <button key={k} className={"m-chip" + (time === k ? " active" : "")}
              onClick={() => { buzz(); setTime(k); setShowCustom(false); }}>{v}</button>
          ))}
          <button className={"m-chip" + (time === "custom" ? " active" : "")}
            onClick={() => { buzz(); setShowCustom(s => !s); if (time !== "custom") setTime("custom"); }}>
            {custom ? `${custom.from || "…"} – ${custom.to || "…"}` : T("stats.period", "Период")}
          </button>
        </div>
        {showCustom && (
          <div style={{ marginTop: 10, padding: 12, background: "var(--bg-1)", border: "1px solid var(--border)", borderRadius: "var(--r-2)" }}>
            <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8 }}>
              <div>
                <div className="muted" style={{ fontSize: 11, marginBottom: 4 }}>{T("common.from", "От")}</div>
                <input type="date" className="m-input" style={{ height: 38, fontSize: 13 }}
                  value={custom?.from || ""}
                  onChange={e => setCustom(c => ({ ...c, from: e.target.value }))}/>
              </div>
              <div>
                <div className="muted" style={{ fontSize: 11, marginBottom: 4 }}>{T("common.to", "До")}</div>
                <input type="date" className="m-input" style={{ height: 38, fontSize: 13 }}
                  value={custom?.to || ""}
                  onChange={e => setCustom(c => ({ ...c, to: e.target.value }))}/>
              </div>
            </div>
            <div style={{ display: "flex", gap: 8, marginTop: 10 }}>
              <button className="m-button ghost sm" style={{ flex: 1 }}
                onClick={() => { buzz(); setCustom(null); setTime("week"); setShowCustom(false); }}>{T("common.reset", "Сброс")}</button>
              <button className="m-button primary sm" style={{ flex: 2 }}
                onClick={async () => {
                  buzz();
                  if (!custom?.from && !custom?.to) {
                    onToast && onToast({ msg: T("journal.pickRange", "Укажи диапазон"), err: true }); return;
                  }
                  setShowCustom(false);
                  // Запрашиваем заново — биржевые closed-trades поддерживают date_from/date_to
                  if (onCustomRange) await onCustomRange(custom);
                }}>{T("common.apply", "Применить")}</button>
            </div>
          </div>
        )}
      </div>

      <div className="m-scroll" ref={scrollRef}
        onPointerDown={down} onPointerMove={move} onPointerUp={up} onPointerCancel={up}
        style={{ paddingTop: 6 }}>
        <div className="ptr" style={{ height: refreshing ? 36 : ptr }}>
          <span className={refreshing ? "spin" : ""} style={{ display: "inline-flex" }}><MIcon name="refresh" size={16}/></span>
          {refreshing ? T("common.refreshing", "Обновление…") : ptr > 48 ? T("common.releaseToRefresh", "Отпустите для обновления") : T("common.pullToRefresh", "Потяните вниз")}
        </div>

        {refreshing && <><SkelCard/><SkelCard/></>}

        {!refreshing && days.map(d => (
          <div key={d.date}>
            <div className="m-date-divider">
              {new Date(d.date).toLocaleDateString("ru-RU", { day: "numeric", month: "long" })}, {d.weekday}
            </div>
            {d.trades.map(t => <TradeCard key={t.id} t={t} onToast={onToast}/>)}
            <div className="day-summary">
              <span>{d.summary.trades} {T("home.tradesCount", "сделок")}</span><span className="sep">·</span>
              <span><span className="pos">{d.summary.wins}W</span> / <span className="neg">{d.summary.losses}L</span></span>
              <span className="sep">·</span>
              <span className={d.summary.pnl >= 0 ? "pos" : "neg"}>{signed(d.summary.pnl)}</span>
            </div>
          </div>
        ))}

        {!refreshing && days.length === 0 && (
          <EmptyState icon="journal" title={T("journal.emptyTrades.title", "Нет сделок")} msg={T("journal.emptyTrades.hint", "По этому фильтру сделок не найдено. Попробуй другой период.")}/>
        )}
      </div>
    </>
  );
};

// ------------------------------------------------------------
// Signals screen — swipe right approve / left reject + undo toast
// ------------------------------------------------------------
// Risk breakdown — рассчитывается из сигнала + настроек + баланса.
// Показывает реальную картину: сколько USDT под риском, что получишь на каждом TP,
// где ликвидация и какой R/R.
const SignalRisk = ({ s, balance, settings }) => {
  const sign = s.side === "LONG" ? 1 : -1;
  const eq = parseFloat(balance?.equity || 0);
  const avail = parseFloat(balance?.available || 0);
  // Размер позиции (USDT маржа) — по настройкам юзера
  const posUsdt = settings?.sizeMode === "percent_balance"
    ? Math.max(0, avail * (settings.riskPct || 1) / 100)
    : parseFloat(settings?.posSizeUsdt || 5);
  const lev = parseInt(s.lev || settings?.leverage || 10);
  const entry = parseFloat(s.entry || 0);
  const stop = parseFloat(s.stop || 0);
  if (!entry || posUsdt <= 0) return null;

  const notional = posUsdt * lev;
  const qty = notional / entry;
  const riskUsdt = stop > 0 ? Math.abs(entry - stop) * qty : 0;
  const riskPctEq = eq > 0 ? riskUsdt / eq * 100 : 0;
  const liq = lev > 0 ? (s.side === "LONG"
    ? entry * (1 - 1 / lev * 0.95)
    : entry * (1 + 1 / lev * 0.95)) : 0;
  const rr = (s.takes && s.takes.length > 0 && stop > 0)
    ? Math.abs(s.takes[0] - entry) / Math.abs(entry - stop) : 0;
  const tpRewards = (s.takes || []).map(tp => sign * (tp - entry) * qty);

  return (
    <div style={{
      marginTop: 12, padding: 12, background: "var(--bg-2)",
      borderRadius: "var(--r-1)", fontSize: 12, lineHeight: 1.65,
    }}>
      <div style={{ display: "flex", alignItems: "center", gap: 6, color: "var(--text-2)", fontWeight: 600, marginBottom: 8 }}>
        <MIcon name="layers" size={13} color="var(--accent)"/>
        Калькулятор риска
      </div>
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "4px 14px" }}>
        <div>
          <div className="muted" style={{ fontSize: 10 }}>{T("portfolio.margin", "Маржа")}</div>
          <div className="mono">{money(posUsdt)} <span className="muted">USDT</span></div>
        </div>
        <div>
          <div className="muted" style={{ fontSize: 10 }}>{T("portfolio.size", "Размер")}</div>
          <div className="mono">{qty.toFixed(qty > 1 ? 2 : 4)} {s.coin}</div>
        </div>
        <div>
          <div className="muted" style={{ fontSize: 10 }}>{T("risk.toSL", "Риск (до SL)")}</div>
          <div className="mono neg">{stop > 0 ? "-" + money(riskUsdt) : "—"}</div>
        </div>
        <div>
          <div className="muted" style={{ fontSize: 10 }}>{T("risk.percentOfBalance", "% от баланса")}</div>
          <div className="mono neg">{stop > 0 ? "-" + riskPctEq.toFixed(2) + "%" : "—"}</div>
        </div>
        <div>
          <div className="muted" style={{ fontSize: 10 }}>{T("risk.rrToTP1", "R/R до TP1")}</div>
          <div className="mono">{rr > 0 ? rr.toFixed(2) : "—"}</div>
        </div>
        <div>
          <div className="muted" style={{ fontSize: 10 }}>{T("home.liquidation", "Ликвидация")}</div>
          <div className="mono neg">{liq > 0 ? price(liq) : "—"}</div>
        </div>
      </div>
      {tpRewards.length > 0 && (
        <div style={{ marginTop: 10, paddingTop: 8, borderTop: "1px solid var(--border)" }}>
          <div className="muted" style={{ fontSize: 10, marginBottom: 4 }}>{T("risk.profitPerTP", "Прибыль на каждом TP")}</div>
          <div style={{ display: "grid", gridTemplateColumns: "repeat(" + Math.min(tpRewards.length, 4) + ", 1fr)", gap: 6 }}>
            {tpRewards.map((r, i) => (
              <div key={i} style={{ background: "var(--long-bg)", border: "1px solid var(--long-dim)", borderRadius: 4, padding: "4px 6px", textAlign: "center" }}>
                <div className="muted" style={{ fontSize: 9 }}>TP{i + 1}</div>
                <div className="mono pos" style={{ fontSize: 11, fontWeight: 600 }}>+{money(r)}</div>
              </div>
            ))}
          </div>
        </div>
      )}
      {riskPctEq > 10 && (
        <div style={{ marginTop: 8, color: "var(--warn)", fontSize: 11, display: "flex", gap: 6, alignItems: "center" }}>
          <MIcon name="lock" size={11}/>
          Риск &gt;10% от депозита — высокий
        </div>
      )}
    </div>
  );
};

const SignalCard = ({ s, onApprove, onReject, balance, settings }) => {
  const { dx, active, handlers } = useSwipe({ onRight: () => onApprove(s), onLeft: () => onReject(s), threshold: 0.38 });
  const reveal = dx > 8 ? "left" : dx < -8 ? "right" : null;
  return (
    <div className="swipe-wrap" style={{ marginBottom: 12 }}>
      <div className="swipe-actions approve-reject">
        <span className="left" style={{ opacity: reveal === "left" ? 1 : 0.3 }}><MIcon name="check" size={18}/> Открыть</span>
        <span className="right reject" style={{ opacity: reveal === "right" ? 1 : 0.3 }}>Skip <MIcon name="x" size={18}/></span>
      </div>
      <div className="swipe-fg m-card signal-card" {...handlers}
        style={{ transform: `translateX(${dx}px)`, transition: active ? "none" : "transform 180ms cubic-bezier(0.32,0.72,0,1)" }}>
        <div className="sig-head">
          <MIcon name="plane" size={16} color="var(--accent)"/>
          <span className="sig-channel">из канала «{s.channel}»</span>
          <span className="sig-time">{s.ago}</span>
        </div>
        <div className="sig-body">
          <div className="sig-pair">
            <Coin sym={s.coin}/>
            <span className="p">{s.sym}</span>
            <Side side={s.side}/>
          </div>
          <div className="sig-levels">
            <div className="sig-level"><span className="k">{T("portfolio.entry", "Вход")}</span><span className="v">{price(s.entry)}</span></div>
            {s.stop > 0 && <div className="sig-level"><span className="k">{T("signals.stop", "Стоп")}</span><span className="v neg">{price(s.stop)}</span></div>}
            {s.takes && s.takes.length > 0 && (
              <div className="sig-level"><span className="k">{T("signals.takes", "Тейки")}</span><span className="v tps">{s.takes.map((t, i) => <span key={i} className="tp">{price(t)}</span>)}</span></div>
            )}
            {s.riskPct > 0 && <div className="sig-level"><span className="k">{T("risk.title", "Риск")}</span><span className="v">{s.riskPct}% от баланса</span></div>}
          </div>
          <div className="sig-meta">
            {s.margin > 0 && <div><div className="k">{T("portfolio.margin", "Маржа")}</div><div className="v">~{money(s.margin)} <span style={{ fontSize: 11, color: "var(--text-3)" }}>USDT</span></div></div>}
            <div><div className="k">{T("lev.title", "Плечо")}</div><div className="v">{s.lev}x</div></div>
          </div>
          <SignalRisk s={s} balance={balance} settings={settings}/>
        </div>
        <div className="sig-actions">
          <button className="m-button long lg" onClick={() => onApprove(s)}><MIcon name="check" size={18}/> Открыть</button>
          <button className="m-button ghost lg" onClick={() => onReject(s)}><MIcon name="x" size={18}/> Skip</button>
        </div>
      </div>
    </div>
  );
};

const SignalsScreen = ({ signals, onApprove, onReject, onClearAll, balance, settings }) => (
  <div className="m-scroll">
    {signals.length === 0 ? (
      <EmptyState
        icon="coffee"
        title={T("signals.allProcessed", "Все сигналы обработаны")}
        msg={T("signals.emptyQueue", "Очередь пуста — отдохни. Новые сигналы из каналов появятся здесь автоматически.")}
      />
    ) : (
      <>
        {signals.length > 1 && onClearAll && (
          <button className="m-button ghost lg block" style={{ marginBottom: 10, color: "var(--warn)" }}
            onClick={() => { buzz(); onClearAll(); }}>
            <MIcon name="x" size={15}/> {T("signals.clearAll", "Очистить очередь")} ({signals.length})
          </button>
        )}
        {signals.map(s => (
          <SignalCard key={s.id} s={s} onApprove={onApprove} onReject={onReject}
            balance={balance} settings={settings}/>
        ))}
      </>
    )}
  </div>
);

// ------------------------------------------------------------
// Settings screen
// ------------------------------------------------------------
const SetRow = ({ icon, title, sub, right, ok, onClick }) => (
  <button className="m-set-row" onClick={() => { buzz(); onClick && onClick(); }}>
    {icon && <span className="si"><MIcon name={icon} size={17}/></span>}
    <span className="sc"><span className="st">{title}</span>{sub && <span className="ss">{sub}</span>}</span>
    <span className="sr">{ok && <span className="ok"><MIcon name="check" size={16}/></span>}{right}<MIcon name="chevR" size={16}/></span>
  </button>
);

const SettingsScreen = ({
  data, onConnectTg, onChangePwd, onLogout, onChangeMode, onChangeAutoConf, onChangeDryRun,
  onOpenLeverage, onOpenSize, onOpenExchange, onOpenApiCreds, onOpenBotToken,
  onOpenChannels, onOpenExchangeSignals, onOpenStats, onOpenPush, onOpenRiskGuards,
  onOpenPortfolio, onChangeTheme, onChangeLocale, onOpenStorm, onOpenForwards, plan,
  onToast,
}) => {
  const s = data.settings;
  const mode = s.mode;
  const sizeLabel = s.sizeMode === "percent_balance" ? `${s.riskPct}%` : `${s.posSizeUsdt} USDT`;
  const levLabel = s.levMode === "max" ? "MAX" : s.levMode === "from_signal" ? `${s.leverage}x (signal)` : `${s.leverage}x`;
  const exLabel = (s.exchange || "WEEX").toUpperCase();

  return (
    <div className="m-scroll">
      <div className="m-set-group">
        <div className="label">{T("settings.section.account", "Аккаунт")}</div>
        <div className="m-set-list">
          <SetRow icon="mail" title={s.email || "—"} sub={T("settings.row.emailVerified", "Email подтверждён")} ok/>
          <SetRow icon="lock" title={T("pwd.change", "Сменить пароль")} onClick={onChangePwd}/>
          <SetRow icon="logout" title={T("settings.row.logout", "Выйти")} onClick={onLogout}/>
        </div>
      </div>

      <div className="m-set-group">
        <div className="label">{T("settings.section.integrations", "Интеграции")}</div>
        <div className="m-set-list">
          <SetRow icon="plane"
            title="Telegram"
            sub={s.telegram || T("common.notConnected", "не подключён")}
            ok={s.tgConnected}
            onClick={onConnectTg}/>
          <SetRow icon="key"
            title={`${exLabel} API`}
            sub={s.apiKeyMask || T("common.notConfiguredM", "не настроен")}
            ok={s.apiKeyOk}
            onClick={onOpenApiCreds}/>
          <SetRow icon="bot"
            title={T("tg.title", "Confirm-Bot")}
            sub={s.confirmBot || T("common.notConnected", "не подключён")}
            ok={s.botLinked}
            onClick={onOpenBotToken}/>
        </div>
      </div>

      <div className="m-set-group">
        <div className="label">{T("settings.section.trading", "Торговля")}</div>
        <div className="m-set-list">
          <div className="m-set-row">
            <span className="si"><MIcon name="sliders" size={17}/></span>
            <span className="sc">
              <span className="st">{T("lev.mode", "Режим")}</span>
              <span className="ss">{mode === "auto" ? T("settings.row.autoOpen", "Авто-открытие сделок") : T("settings.row.manualConfirm", "Ручное подтверждение")}</span>
            </span>
            <span className="m-toggle2">
              <button className={mode === "auto" ? "active" : ""}
                onClick={() => { buzz(); onChangeMode && onChangeMode("auto"); }}>Auto</button>
              <button className={mode === "manual" ? "active" : ""}
                onClick={() => { buzz(); onChangeMode && onChangeMode("manual"); }}>Manual</button>
            </span>
          </div>
          {mode === "auto" && (
            <div className="m-set-row">
              <span className="si"><MIcon name="signal" size={17}/></span>
              <span className="sc">
                <span className="st">{T("settings.row.autoThreshold", "Порог авто-исполнения")}</span>
                <span className="ss">{(s.autoMinConfidence || "high") === "high"
                  ? T("settings.row.autoThresholdHigh", "Только уверенные (high)")
                  : T("settings.row.autoThresholdMed", "High + medium")}</span>
              </span>
              <span className="m-toggle2">
                <button className={(s.autoMinConfidence || "high") === "high" ? "active" : ""}
                  onClick={() => { buzz(); onChangeAutoConf && onChangeAutoConf("high"); }}>High</button>
                <button className={s.autoMinConfidence === "medium" ? "active" : ""}
                  onClick={() => { buzz(); onChangeAutoConf && onChangeAutoConf("medium"); }}>Med</button>
              </span>
            </div>
          )}
          <SetRow icon="layers" title={T("lev.title", "Плечо")}
            right={<span className="mono" style={{ color: "var(--text)" }}>{levLabel}</span>}
            onClick={onOpenLeverage}/>
          <SetRow icon="wallet" title={T("size.title", "Размер сделки")}
            right={<span className="mono" style={{ color: "var(--text)" }}>{sizeLabel}</span>}
            onClick={onOpenSize}/>
          <SetRow icon="key" title={T("ex.title", "Биржа")}
            right={<span className="mono" style={{ color: "var(--text)" }}>{exLabel}</span>}
            onClick={onOpenExchange}/>
          <SetRow icon="signal" title={T("ex.signalsByExchange", "Сигналы по биржам")}
            sub={T("ex.toggleAndLeverage", "Включить/выключить + плечо для каждой")}
            onClick={onOpenExchangeSignals}/>
        </div>
      </div>

      <div className="m-set-group">
        <div className="label">{T("channel.sources", "Каналы-источники")}</div>
        <div className="m-set-list">
          <button className="m-set-row" style={{ color: "var(--accent)" }} onClick={() => { buzz(); onOpenChannels && onOpenChannels(); }}>
            <span className="si" style={{ background: "var(--accent-dim)", color: "var(--accent)" }}>
              <MIcon name="plus" size={17}/>
            </span>
            <span className="sc"><span className="st" style={{ color: "var(--accent)" }}>{T("channel.manage", "Управление каналами")}</span></span>
          </button>
          {(s.channels || []).map((c, i) => (
            <SetRow key={(c.id || c.ref || c) + "_" + i} icon="signal"
              title={c.label || c.ref || String(c)}
              sub={c.ref && c.ref !== c.label ? c.ref : undefined}
              onClick={onOpenChannels}/>
          ))}
          {(!s.channels || s.channels.length === 0) && (
            <div style={{ padding: 14, color: "var(--text-3)", fontSize: 13 }}>{T("channel.noneAdded", "Каналы не добавлены")}</div>
          )}
          <SetRow icon="plane" title={T("forwards.title", "Пересылка сигналов")}
            sub={T("forwards.subtitle", "Дублируй сигналы в свои каналы")}
            onClick={onOpenForwards}/>
        </div>
      </div>

      <div className="m-set-group">
        <div className="label">{T("settings.section.notifyAnalytics", "Уведомления и аналитика")}</div>
        <div className="m-set-list">
          <SetRow icon="bell" title={T("push.title", "Push-уведомления")}
            sub={T("push.events", "Сигналы / открытие / закрытие / БУ")}
            onClick={onOpenPush}/>
          <SetRow icon="trendUp" title={T("stats.title", "Статистика")}
            sub={T("stats.breakdown", "Каналы · TP1/TP2/TP3 · SL до TP1")}
            onClick={onOpenStats}/>
          <SetRow icon="wallet" title={T("topbar.portfolio", "Портфель")}
            sub="Equity curve · drawdown"
            onClick={onOpenPortfolio}/>
        </div>
      </div>

      <div className="m-set-group">
        <div className="label">{T("settings.section.appearance", "Внешний вид")}</div>
        <div className="m-set-list">
          <div className="m-set-row">
            <span className="si"><MIcon name="sun" size={17}/></span>
            <span className="sc">
              <span className="st">{T("settings.row.theme", "Тема")}</span>
              <span className="ss">{s.theme === "light" ? T("common.light", "Светлая") : T("common.dark", "Тёмная")}</span>
            </span>
            <span className="m-toggle2">
              <button className={s.theme !== "light" ? "active" : ""}
                onClick={() => { buzz(); window.M_THEME.setTheme("dark"); onChangeTheme && onChangeTheme("dark"); }}>{T("common.dark", "Тёмная")}</button>
              <button className={s.theme === "light" ? "active" : ""}
                onClick={() => { buzz(); window.M_THEME.setTheme("light"); onChangeTheme && onChangeTheme("light"); }}>{T("common.light", "Светлая")}</button>
            </span>
          </div>
          <div className="m-set-row">
            <span className="si"><MIcon name="globe" size={17}/></span>
            <span className="sc">
              <span className="st">{T("settings.row.language", "Язык / Language")}</span>
              <span className="ss">{s.locale === "en" ? "English" : T("common.russian", "Русский")}</span>
            </span>
            <span className="m-toggle2">
              <button className={s.locale !== "en" ? "active" : ""}
                onClick={() => { buzz(); window.M_I18N.setLocale("ru"); onChangeLocale && onChangeLocale("ru"); }}>RU</button>
              <button className={s.locale === "en" ? "active" : ""}
                onClick={() => { buzz(); window.M_I18N.setLocale("en"); onChangeLocale && onChangeLocale("en"); }}>EN</button>
            </span>
          </div>
        </div>
      </div>

      <div className="m-set-group">
        <div className="label">{T("settings.section.security", "Безопасность")}</div>
        <div className="m-set-list">
          <div className="m-set-row">
            <span className="si"><MIcon name="lock" size={17}/></span>
            <span className="sc">
              <span className="st">{T("settings.row.dryRun", "DRY-RUN режим")}</span>
              <span className="ss">{s.dryRun ? T("settings.row.dryRunDesc", "Ордера НЕ отправляются — только лог") : T("settings.row.liveTrading", "Реальная торговля")}</span>
            </span>
            <span className={"m-switch" + (s.dryRun ? " on" : "")}
              onClick={() => { buzz(); onChangeDryRun && onChangeDryRun(!s.dryRun); }}/>
          </div>
          <SetRow icon="layers" title={T("risk.title", "Защита капитала")}
            sub={[
              s.maxDailyLossPct > 0 ? `daily ≤ ${s.maxDailyLossPct}%` : null,
              s.liqWarnPct > 0 ? `liq < ${s.liqWarnPct}%` : null,
              s.maxOpenPositions > 0 ? `≤${s.maxOpenPositions} поз.` : null,
            ].filter(Boolean).join(" · ") || T("common.notConfiguredF", "не настроена")}
            onClick={onOpenRiskGuards}/>
          <SetRow icon="stop" title={T("storm.title", "🛑 Шторм-режим")}
            sub={s.stormUntilMs > Date.now()
              ? `активен до ${new Date(s.stormUntilMs).toLocaleString("ru-RU", { hour: "2-digit", minute: "2-digit", day: "2-digit", month: "short" })}`
              : T("storm.description", "закрыть всё, заблокировать сигналы")}
            onClick={onOpenStorm}/>
        </div>
      </div>

      <div style={{ textAlign: "center", color: "var(--text-4)", fontSize: 11, fontFamily: "var(--font-mono)", padding: "24px 0 8px" }}>
        API4ATKA mobile · v2.2.0
      </div>
    </div>
  );
};

Object.assign(window, {
  PositionCard, DashboardScreen, TradeCard, JournalScreen,
  ModeSwitcher, SpotScreen, BotsScreen,
  SignalCard, SignalsScreen, SetRow, SettingsScreen,
});
