/* ============================================
   VELOX — App shell + main App
   ============================================ */

const NAV = [
  { section: "Trading" },
  { id: "dashboard",  label: "Dashboard",   icon: "dashboard" },
  { id: "indices",    label: "Nifty / Sensex", icon: "trend" },
  { id: "options",    label: "Option Chain", icon: "options" },
  { id: "watchlist",  label: "Watchlist",    icon: "eye" },
  { id: "positions",  label: "Positions",    icon: "positions", badge: "6" },
  { id: "orders",     label: "Orders & Trades", icon: "history" },
  { section: "Algos" },
  { id: "algos",          label: "Strategies",     icon: "bot" },
  { id: "strategy-guide", label: "Strategy Guide",  icon: "book" },
  { id: "monitor",        label: "Live Monitor",    icon: "activity", badge: "3" },
  { id: "trades",         label: "Strategy Trades", icon: "chart" },
  { id: "backtest",       label: "Backtest",        icon: "backtest" },
  { section: "Insights" },
  { id: "reports",    label: "P&L Reports",  icon: "chart" },
  { id: "journal",    label: "Trade Journal", icon: "history" },
  { section: "Account" },
  { id: "settings",   label: "Settings",     icon: "settings" },
];

const PAGE_TITLES = {
  dashboard: "Dashboard", indices: "Indices", options: "Option Chain",
  watchlist: "Watchlist", positions: "Positions", orders: "Orders & Trades", algos: "Algorithms",
  monitor: "Live Monitor", trades: "Strategy Trades", backtest: "Backtest", reports: "P&L Reports",
  journal: "Trade Journal", settings: "Settings",
};

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "theme": "light",
  "accent": "green",
  "layout": "multi"
}/*EDITMODE-END*/;

const App = () => {
  const [authed, setAuthed] = React.useState(false);
  const [user, setUser] = React.useState(null);
  const [page, setPage] = React.useState("dashboard");
  const [profileOpen, setProfileOpen] = React.useState(false);
  const [kotakStatus, setKotakStatus] = React.useState(null); // null | { connected, mobile, ucc }

  // Tweaks
  const tweaksRef = React.useRef(null);
  const [tweaks, setTweak] = (typeof useTweaks === "function") ? useTweaks(TWEAK_DEFAULTS) : [TWEAK_DEFAULTS, () => {}];

  React.useEffect(() => {
    document.documentElement.setAttribute("data-theme", tweaks.theme);
    document.documentElement.setAttribute("data-accent", tweaks.accent);
  }, [tweaks.theme, tweaks.accent]);

  const [isDemo, setIsDemo] = React.useState(false);
  const [refreshInterval, setRefreshInterval] = React.useState(30_000); // ms, default 30s
  const [refreshing, setRefreshing] = React.useState(false);
  const portfolioCallbackRef = React.useRef(null);

  // Market data — live mode starts empty; demo mode uses mock builders
  const [market, setMarket] = React.useState(() => buildInitialMarket());
  const [holdings, setHoldings]   = React.useState([]);
  const [positions, setPositions] = React.useState([]);
  const [margin, setMargin]       = React.useState({ available: 0, used: 0 });
  const [trades] = React.useState(() => buildTrades());
  const [pnlHistory, setPnlHistory] = React.useState([]);
  const [algos, setAlgos] = React.useState(() => ALGOS.map(a => ({ ...a }))); // seeded from catalog, replaced by Firestore
  const [subscribed, setSubscribed] = React.useState(["RELIANCE", "TCS", "HDFCBANK", "INFY", "ITC", "BHARTIARTL", "ICICIBANK", "MARUTI", "NIFTY", "SENSEX"]);
  const [toasts, setToasts] = React.useState([]);
  const [events, setEvents] = React.useState([]);
  const [configAlgo, setConfigAlgo] = React.useState(null);
  const [tradeModal, setTradeModal] = React.useState(null);
  const [wsStatus, setWsStatus] = React.useState("disconnected");
  const [reAuthModal, setReAuthModal] = React.useState(null); // { pendingOrder } | null
  const [orders, setOrders] = React.useState([]);
  const [workerPing, setWorkerPing] = React.useState(null);

  // Always-current ref so WS onconnect can read latest subscribed without stale closure
  const subscribedRef = React.useRef(subscribed);
  React.useEffect(() => { subscribedRef.current = subscribed; }, [subscribed]);

  // ── Kotak direct WebSocket — real-time ticks, no Firebase in the data path ──
  React.useEffect(() => {
    if (!authed || isDemo) return;

    // Reverse-lookup: numeric token → symbol (for stocks)
    const tokenToSym = {};
    Object.entries(NSE_TOKENS).forEach(([sym, tok]) => { tokenToSym[String(tok)] = sym; });

    // Index name → symbol (for index feeds — tk is name string not numeric)
    const INDEX_NAME_TO_SYM = {
      "Nifty 50":          "NIFTY",
      "Nifty Bank":        "BANKNIFTY",
      "SENSEX":            "SENSEX",
      "Nifty Fin Service": "FINNIFTY",
      "Nifty MidCap":      "MIDCPNIFTY",
    };

    const subscribeAll = (syms) => {
      const indexTokens = syms.filter(s => INDEX_SCRIPS[s]).map(s => INDEX_SCRIPS[s]);
      const stockTokens = syms.filter(s => !INDEX_SCRIPS[s]).map(s => {
        const hardcoded = NSE_TOKENS[s];
        if (hardcoded) return `nse_cm|${hardcoded}`;
        const found = API.searchScrips(s, 20);
        const match = found.find(x => x.sym === s) ?? found[0];
        return match?.token ? `nse_cm|${match.token}` : null;
      }).filter(Boolean);
      if (indexTokens.length) VeloxWs.subscribeIndex(indexTokens);
      if (stockTokens.length) VeloxWs.subscribe(stockTokens);
    };

    VeloxWs.onStatus = (status, detail) => {
      setWsStatus(status);
      if (status === "connected") {
        // Re-subscribe full current watchlist — handles reconnects and the loadPrefs race
        const allSyms = [...new Set([...subscribedRef.current, "NIFTY", "BANKNIFTY", "SENSEX", "FINNIFTY"])];
        subscribeAll(allSyms);
      }
      if (status === "error" || status === "disconnected") {
        console.warn("[WS]", status, detail ?? "");
      }
    };

    VeloxWs.onTick = (tick) => {
      // Try hardcoded map first; if miss, look up scrip master cache (O(1) via reverse map)
      let sym = INDEX_NAME_TO_SYM[tick.scrip] ?? tokenToSym[tick.scrip] ?? tokenToSym[tick.token];
      if (!sym) {
        const rawTk = String(tick.scrip ?? tick.token ?? "");
        const found = API.symForToken(rawTk);
        if (found) { tokenToSym[rawTk] = found; sym = found; }
      }
      setMarket(prev => {
        if (!sym) return prev;

        const existing = prev.stocks.find(s => s.sym === sym);
        const applyTick = (s) => {
          const spark = [...s.sparkline, tick.ltp];
          if (spark.length > 30) spark.shift();
          return {
            ...s,
            price:     tick.ltp,
            prevClose: tick.prevClose || s.prevClose,
            dayChg:    tick.change,
            dayChgPct: tick.changePct,
            high:      tick.high  || Math.max(s.high || tick.ltp, tick.ltp),
            low:       tick.low   || Math.min(s.low  || tick.ltp, tick.ltp),
            volume:    tick.volume || s.volume,
            lastTick:  tick.ltp > s.price ? 1 : tick.ltp < s.price ? -1 : 0,
            sparkline: spark,
          };
        };
        const stocks = existing
          ? prev.stocks.map(s => s.sym === sym ? applyTick(s) : s)
          : [...prev.stocks, applyTick({ sym, name: API.searchScrips(sym, 1)[0]?.name || sym, price: 0, prevClose: 0, dayChg: 0, dayChgPct: 0, high: 0, low: 0, volume: 0, sparkline: [], lastTick: 0 })];

        const indices = prev.indices.map(idx => {
          if (idx.sym !== sym) return idx;
          const spark = [...idx.sparkline, tick.ltp];
          if (spark.length > 60) spark.shift();
          const candles = [...idx.candles];
          const last = candles[candles.length - 1];
          if (last) candles[candles.length - 1] = {
            ...last, c: tick.ltp,
            h: Math.max(last.h, tick.ltp),
            l: Math.min(last.l, tick.ltp),
          };
          return {
            ...idx,
            price:     tick.ltp,
            prevClose: tick.prevClose || idx.prevClose,
            dayChg:    tick.change,
            dayChgPct: tick.changePct,
            lastTick:  tick.ltp > idx.price ? 1 : tick.ltp < idx.price ? -1 : 0,
            sparkline: spark,
            candles,
          };
        });

        return { stocks, indices };
      });

      // Update matching open positions — match by token (or exact symbol),
      // NEVER by startsWith because option symbols start with the underlying name
      // (HDFCBANK26JUN760CE starts with HDFCBANK → stock ticks would clobber option LTP)
      setPositions(prev => prev.map(p => {
        const matches =
          (tick.token && p.token && String(tick.token) === String(p.token)) ||
          (sym && p.sym && p.sym === sym);
        if (!matches) return p;
        const dir = p.side === "BUY" ? 1 : -1;
        return { ...p, ltp: tick.ltp, pnl: +((tick.ltp - p.avg) * p.qty * dir).toFixed(2) };
      }));
    };

    // Index name mapping for Kotak ifs subscribe type
    const INDEX_SCRIPS = {
      NIFTY:     "nse_cm|Nifty 50",
      BANKNIFTY: "nse_cm|Nifty Bank",
      SENSEX:    "bse_cm|SENSEX",
      FINNIFTY:  "nse_cm|Nifty Fin Service",
    };

    // Connect WebSocket using session tokens from Firestore
    API.loadKotakSession().then(s => {
      if (!s) return;
      API.refreshCachedSession(s); // prime the browser-side order session cache
      VeloxWs.connect(s.accessToken, s.sid ?? "");
      // Connect order/position streaming WebSocket (baseUrl from login)
      if (typeof VeloxHsi !== "undefined" && s.baseUrl) {
        VeloxHsi.connect(s.accessToken, s.sid ?? "", s.baseUrl);
        VeloxHsi.onOrder = (o) => {
          console.log("[HSI] order update", o);
          setOrders(prev => {
            const idx = prev.findIndex(x => x.nOrdNo === o.nOrdNo);
            return idx >= 0
              ? prev.map((x, i) => i === idx ? { ...x, ...o } : x)
              : [o, ...prev];
          });
          // Toast on terminal status changes
          const st = (o.ordSt ?? "").toLowerCase();
          const sym = o.sym ?? o.trdSym ?? "";
          const side = o.trnsTp === "B" ? "BUY" : "SELL";
          if (st === "complete" || st === "traded") {
            addToast({ type: "up", title: `${side} filled`, body: `${sym} × ${o.qty} @ ₹${o.avgPrc}` });
          } else if (st === "cancelled" || st === "rejected") {
            addToast({ type: "warn", title: `Order ${st}`, body: `${sym} · ${o.rejRsn && o.rejRsn !== "--" ? o.rejRsn : st}` });
          }
        };
        VeloxHsi.onPosition = (p) => {
          console.log("[HSI] position update", p);
          setPositions(prev => {
            const idx = prev.findIndex(x => x.sym === p.sym || x.token === p.token);
            return idx >= 0
              ? prev.map((x, i) => i === idx ? { ...x, ...p } : x)
              : [p, ...prev];
          });
        };
      }
    });

    // Initial subscribe is now handled by onStatus("connected") via subscribeAll + subscribedRef
    return () => { VeloxWs.disconnect(); unsubStrategies(); };
  }, [authed, user]);

  // Re-subscribe when watchlist changes while WS is already connected
  React.useEffect(() => {
    if (!authed || !VeloxWs.isConnected) return;
    const INDEX_SCRIPS_MAP = { NIFTY: "nse_cm|Nifty 50", BANKNIFTY: "nse_cm|Nifty Bank", SENSEX: "bse_cm|SENSEX", FINNIFTY: "nse_cm|Nifty Fin Service" };
    const allSyms = [...new Set([...subscribed, "NIFTY", "BANKNIFTY", "SENSEX", "FINNIFTY"])];
    const indexTokens = allSyms.filter(s => INDEX_SCRIPS_MAP[s]).map(s => INDEX_SCRIPS_MAP[s]);
    const stockTokens = allSyms.filter(s => !INDEX_SCRIPS_MAP[s]).map(sym => {
      const hardcoded = NSE_TOKENS[sym];
      if (hardcoded) return `nse_cm|${hardcoded}`;
      const results = API.searchScrips(sym, 20);
      const found = results.find(s => s.sym === sym) ?? results[0];
      return found?.token ? `nse_cm|${found.token}` : null;
    }).filter(Boolean);
    if (indexTokens.length) VeloxWs.subscribeIndex(indexTokens);
    if (stockTokens.length) VeloxWs.subscribe(stockTokens);
  }, [subscribed]);

  // Persist watchlist to Firestore — only after prefs have been loaded (avoids race on login)
  const _wlSaveTimer = React.useRef(null);
  const _prefsLoaded = React.useRef(false);
  React.useEffect(() => {
    if (!authed || !_prefsLoaded.current) return;
    clearTimeout(_wlSaveTimer.current);
    _wlSaveTimer.current = setTimeout(() => {
      API.savePrefs({ watchlist: subscribed }).catch(() => {});
    }, 800);
  }, [subscribed, authed]);

  // ── Fallback mock tick — only in demo mode (live mode shows stale data) ──
  React.useEffect(() => {
    if (!authed || !isDemo) return;
    let lastRealTick = 0;
    const origOnTick = VeloxWs.onTick;
    VeloxWs.onTick = (tick) => { lastRealTick = Date.now(); origOnTick(tick); };

    const id = setInterval(() => {
      if (Date.now() - lastRealTick < 5_000) return; // real data is fresh — skip mock
      setMarket(prev => {
        const next = {
          stocks:  prev.stocks.map(s => ({ ...s, sparkline: [...s.sparkline] })),
          indices: prev.indices.map(i => ({ ...i, sparkline: [...i.sparkline] })),
        };
        return applyTick(next);
      });
    }, 1500);
    return () => clearInterval(id);
  }, [authed]);

  // Auto-events for live monitor (demo mode only)
  React.useEffect(() => {
    if (!authed || !isDemo) return;
    const tick = () => {
      const running = algos.filter(a => a.running);
      if (!running.length) return;
      const a = running[Math.floor(Math.random() * running.length)];
      const isBuy = Math.random() > 0.5;
      const strike = 24750 + Math.floor(Math.random() * 4) * 50;
      const sym = `NIFTY ${strike} ${Math.random() > 0.5 ? "CE" : "PE"}`;
      const evt = {
        type: isBuy ? "buy" : "sell",
        title: `${a.name}: ${isBuy ? "BUY" : "SELL"} ${sym} × 50`,
        body: isBuy ? `Spike +0.42% in 4s — entered at ₹${(120 + Math.random()*60).toFixed(2)}` : `Momentum decay detected — exit at ₹${(140 + Math.random()*40).toFixed(2)} · ${Math.random()>0.5 ? "+" : "-"}₹${Math.floor(rand2(200,1800))}`,
        time: new Date().toLocaleTimeString("en-IN", { hour: "2-digit", minute: "2-digit", second: "2-digit" }),
      };
      setEvents(prev => [evt, ...prev].slice(0, 40));
      if (Math.random() > 0.4) {
        addToast({
          type: isBuy ? "info" : "up",
          title: `${a.name} · ${isBuy ? "Entered" : "Exited"}`,
          body: `${sym} × 50 ${isBuy ? "filled" : "closed +" + Math.floor(rand2(200,1500))}`,
        });
      }
    };
    const id = setInterval(tick, 9000 + Math.random() * 5000);
    // fire one immediately to seed
    setTimeout(tick, 1200);
    return () => clearInterval(id);
  }, [authed, algos]);

  function rand2(a, b) { return Math.random() * (b - a) + a; }

  const addToast = (t) => {
    const id = Math.random().toString(36).slice(2);
    setToasts(prev => [...prev, { ...t, id, ts: Date.now() }]);
    setTimeout(() => setToasts(prev => prev.filter(x => x.id !== id)), 4500);
  };

  const onLogin = (u = {}) => {
    const demo = !!u.demo;
    setIsDemo(demo);
    setUser({ name: u.name || u.email || (demo ? "Demo User" : "Trader"), uid: u.uid || "" });

    if (demo) {
      // Demo mode: populate with mock data
      const initMarket = buildInitialMarket();
      setMarket(initMarket);
      setHoldings(buildHoldings(initMarket.stocks));
      setPositions(buildPositions());
      setMargin({ available: 248_650, used: 82_400 });
      setPnlHistory(buildPnlHistory(30));
      setKotakStatus({ connected: false, mobile: "", ucc: "" });
      setAuthed(true);
      addToast({ type: "info", title: "Preview mode", body: "Showing demo data. Sign in to trade live." });
      return;
    }

    // Live mode: start empty, fill from API
    setHoldings([]);
    setPositions([]);
    setMargin({ available: 0, used: 0 });
    API.loadKotakProfile().then(p => setKotakStatus({ connected: p?.isConnected ?? false, mobile: p?.mobile ?? "", ucc: p?.ucc ?? "" })).catch(() => {});
    setAuthed(true);
    API.loadScripMaster().catch(() => {});
    addToast({ type: "up", title: "Welcome back!", body: "Connected to Kotak Securities." });

    // Retry helper: 2 retries with 2s then 4s delay
    const withRetry = (fn, attempts = 3, delay = 2000) =>
      fn().catch(e => attempts > 1
        ? new Promise(r => setTimeout(r, delay)).then(() => withRetry(fn, attempts - 1, delay * 2))
        : Promise.reject(e));

    // Load saved preferences (theme, refresh interval, watchlist)
    withRetry(() => API.loadPrefs()).then(prefs => {
      if (prefs) {
        if (prefs.theme)           setTweak("theme", prefs.theme);
        if (prefs.accent)          setTweak("accent", prefs.accent);
        if (prefs.layout)          setTweak("layout", prefs.layout);
        if (prefs.refreshInterval) setRefreshInterval(prefs.refreshInterval);
        if (prefs.watchlist?.length) setSubscribed(prefs.watchlist);
      }
    }).finally(() => { _prefsLoaded.current = true; });

    // Seed catalog on first run, then subscribe to live strategy state
    API.seedStrategiesIfEmpty(ALGOS).catch(() => {});
    const unsubStrategies = API.listenStrategies(docs => {
      if (!docs.length) return;
      setAlgos(prev => prev.map(a => {
        const s = docs.find(x => x.id === a.id);
        if (!s) return a;
        // Strip legacy `running` / `config` from top-level doc — those are now
        // owned by the configs subcollection (merged separately).
        const { running, config, ...rest } = s;
        return { ...a, ...rest };
      }));
    });

    // Aggregate config-level running state across all algos
    API.listenAllConfigs(list => {
      setAlgos(prev => prev.map(a => {
        const configs = list.filter(c => c.algoId === a.id);
        const runningConfigs = configs.filter(c => c.running);
        return {
          ...a,
          configs,
          runningConfigCount: runningConfigs.length,
          running: runningConfigs.length > 0,  // override top-level for UI accuracy
        };
      }));
    });

    const portfolioCb = ({ positions: pos, holdings: hlds, margin: mg }) => {
      if (pos)  setPositions(pos);
      if (hlds) {
        setHoldings(hlds);
        const cumul = hlds.reduce((s, h) => s + (h.pnl || 0), 0);
        API.savePnlSnapshot(cumul).catch(() => {});
        setPnlHistory(prev => {
          const today = new Date().toISOString().slice(0, 10);
          const without = prev.filter(p => p.date !== today);
          return [...without, { date: today, cumul }].sort((a, b) => a.date.localeCompare(b.date));
        });
      }
      if (mg) setMargin(mg);
    };
    portfolioCallbackRef.current = portfolioCb;
    API.startPortfolioRefresh(market.stocks, portfolioCb, refreshInterval);
    API.listenAlgoEvents(evts => setEvents(evts));
    API.listenWorkerPing("main", ping => setWorkerPing(ping));
    withRetry(() => API.loadPnlHistory(30)).then(h => { if (h?.length) setPnlHistory(h); }).catch(() => {});
  };

  // Restart portfolio refresh when interval changes (only in live mode)
  React.useEffect(() => {
    if (!authed || isDemo || !portfolioCallbackRef.current) return;
    API.startPortfolioRefresh(market.stocks, portfolioCallbackRef.current, refreshInterval);
  }, [refreshInterval, authed, isDemo]);

  const onChangeRefreshInterval = (ms) => {
    setRefreshInterval(ms);
    if (!isDemo) API.savePrefs({ refreshInterval: ms }).catch(() => {});
  };

  const onManualRefresh = async () => {
    if (isDemo || refreshing) return;
    setRefreshing(true);
    try {
      const [posRes, holdsRes, marginRes] = await Promise.allSettled([
        API.getPositions(), API.getHoldings(), API.getMargin(),
      ]);
      if (posRes.status === "fulfilled")   { const d = posRes.value?.data;   if (Array.isArray(d)) setPositions(d.map(kotakPositionToLocal)); }
      if (holdsRes.status === "fulfilled") { const d = holdsRes.value?.data; if (Array.isArray(d)) setHoldings(d.map(h => kotakHoldingToLocal(h, market.stocks))); }
      if (marginRes.status === "fulfilled") setMargin(kotakMarginToLocal(marginRes.value ?? {}));
    } catch (_) {}
    setRefreshing(false);
  };

  const onLogout = () => {
    VeloxWs.disconnect();
    API.stopAll();
    API.signOut().catch(() => {});
    setAuthed(false);
    setUser(null);
    setKotakStatus(null);
    setProfileOpen(false);
  };

  const onTrade = (stock, side) => setTradeModal({ kind: "eq", stock, side });
  const onTradeOption = (idx, sym, type, ltp, scrip) => setTradeModal({ kind: "opt", idx, strike: sym, type, ltp, scrip });

  const _buildOrderPayload = (form) => {
    let tradingSymbol, exchange;
    if (form.scrip) {
      // Option order — use the exact scrip object from the option chain
      tradingSymbol = form.scrip.trdSym;
      exchange      = form.scrip.exch;   // e.g. "nse_fo" or "bse_fo"
    } else {
      const baseSym   = form.sym.split(" ")[0];
      const scripInfo = API.searchScrips(baseSym, 20).find(s => s.sym === baseSym)
        ?? API.searchScrips(baseSym, 1)[0];
      tradingSymbol = scripInfo?.trdSym ?? `${baseSym}-EQ`;
      exchange      = scripInfo?.exch   ?? "nse_cm";
    }
    return {
      tradingSymbol,
      exchange,
      transactionType: form.side,
      quantity:        form.qty,
      price:           form.priceType !== "MKT" ? form.price : 0,
      product:         form.product,
      orderType:       form.priceType === "MKT" ? "MKT" : form.priceType === "LMT" ? "L" : form.priceType,
      validity:        "DAY",
    };
  };

  const _submitOrder = async (payload, form) => {
    const result = await API.placeOrder(payload);
    addToast({ type: "up", title: "Order confirmed", body: `${form.sym} · ${form.side} · ₹${result?.nOrdNo ?? ""}` });
    API.saveOrderEvent({
      orderNo: result?.nOrdNo, sym: form.sym, side: form.side,
      qty: form.qty, price: form.price, product: form.product,
      status: "PLACED", ts: Date.now(),
    }).catch(() => {});
  };

  const _isUnauthError = (e) =>
    e?.code === "functions/unauthenticated" ||
    e?.code === "unauthenticated" ||
    (e?.message ?? "").toLowerCase().includes("session expired") ||
    (e?.message ?? "").includes("UNAUTHENTICATED");

  const handleTradeSubmit = async (form) => {
    setTradeModal(null);
    addToast({ type: "info", title: "Placing order…", body: `${form.sym} × ${form.qty} @ ${form.priceType === "MKT" ? "Market" : `₹${form.price}`}` });
    try {
      await _submitOrder(_buildOrderPayload(form), form);
    } catch (e) {
      if (_isUnauthError(e)) {
        setReAuthModal({ pendingOrder: { payload: _buildOrderPayload(form), form } });
      } else {
        addToast({ type: "warn", title: "Order failed", body: e?.message ?? String(e) });
      }
    }
  };

  const onConfigure = (a) => setConfigAlgo(a);

  // Toggle all configs of an algo: if any running → stop all; else start the most-recent one
  // (or create a Default config from the legacy top-level config if subcollection is empty).
  const onToggleAlgo = async (id) => {
    const algo = algos.find(a => a.id === id);
    try {
      // Snapshot the configs once
      const configs = await new Promise((resolve) => {
        const unsub = API.listenConfigs(id, list => { unsub(); resolve(list); });
      });

      const running = configs.filter(c => c.running);
      if (running.length > 0) {
        // Stop ALL running configs
        await Promise.all(running.map(c => API.setConfigRunning(id, c.id, false)));
        addToast({ type: "warn", title: `${algo.name} stopped`, body: `${running.length} config(s) stopped` });
      } else if (configs.length > 0) {
        // Prefer the one named "Default" — fall back to first
        const target = configs.find(c => (c.name ?? "").toLowerCase() === "default") ?? configs[0];
        await API.setConfigRunning(id, target.id, true);
        addToast({ type: "up", title: `${algo.name} started`, body: `Config "${target.name ?? "—"}" is live` });
      } else {
        // No configs yet — migrate from legacy top-level config or use defaults
        const legacy = algo.config ?? {};
        const newId = await API.saveConfig(id, null, {
          name: "Default", running: true,
          config: legacy, startedAt: Date.now(),
        });
        addToast({ type: "up", title: `${algo.name} started`, body: `Created config "Default" (${newId.slice(0, 6)}) and went live` });
      }
    } catch (e) {
      addToast({ type: "warn", title: "Algo toggle failed", body: e?.message ?? String(e) });
    }
  };

  // Save a config from the modal. If form has _configId, update existing; otherwise create new.
  // If user clicked "Start", set running=true on the saved config.
  const onStartAlgoWithConfig = async (algo, form, opts = {}) => {
    const { run = true, asCopy = false } = opts;
    const configId = asCopy ? null : (form._configId ?? null);
    const name = form._configName ?? "Untitled";
    // Strip internal fields before persisting
    const { _configId: _a, _configName: _b, ...config } = form;
    try {
      const savedId = await API.saveConfig(algo.id, configId, {
        name, config,
        ...(run ? { running: true, startedAt: Date.now() } : {}),
      });
      addToast({ type: "up",
        title: `${algo.name}: "${name}" ${run ? "is live" : "saved"}`,
        body: run ? `${form.instrument} · budget ₹${(form.optionBudget ?? 0).toLocaleString("en-IN")}` : "Settings saved" });
      return savedId;
    } catch (e) {
      addToast({ type: "warn", title: "Save config failed", body: e?.message ?? String(e) });
    }
  };

  const _posPayload = (p, side, qty, tag) => {
    const baseSym = p.sym.split(" ")[0];
    const scrips = API.searchScrips(baseSym, 20);
    const info = scrips.find(s => s.sym === baseSym) ?? scrips[0];
    return {
      tradingSymbol:   info?.trdSym ?? p.trdSym ?? `${baseSym}-EQ`,
      exchange:        info?.exch   ?? p.exch ?? (p.type === "OPT" ? "nse_fo" : "nse_cm"),
      transactionType: side,
      quantity:        qty,
      product:         p.type === "OPT" ? "NRML" : "CNC",
      orderType:       "MKT",
      validity:        "DAY",
      tag,
    };
  };

  const onSquareOff = async (idx) => {
    const p = positions[idx];
    addToast({ type: "info", title: "Squaring off…", body: `${p.sym} × ${p.qty}` });
    setPositions(prev => prev.filter((_, i) => i !== idx));
    try {
      await API.placeOrder(_posPayload(p, p.side === "BUY" ? "SELL" : "BUY", p.qty, "manual-exit"));
      addToast({ type: p.pnl >= 0 ? "up" : "down", title: "Position closed", body: `${p.sym} · ${p.pnl >= 0 ? "+" : ""}${fmtINR(p.pnl, 0)}` });
    } catch (e) {
      if (_isUnauthError(e)) setReAuthModal({ pendingOrder: null });
      else addToast({ type: "warn", title: "Exit order failed", body: e?.message ?? String(e) });
    }
  };

  const onSquareOffAll = async () => {
    addToast({ type: "warn", title: "Squaring off all positions", body: `${positions.length} positions queued for exit` });
    const copy = [...positions];
    setPositions([]);
    for (const p of copy) {
      try {
        await API.placeOrder(_posPayload(p, p.side === "BUY" ? "SELL" : "BUY", p.qty, "square-off-all"));
      } catch (e) {
        if (_isUnauthError(e)) { setReAuthModal({ pendingOrder: null }); break; }
        console.warn("Exit failed for", p.sym, e.message);
      }
    }
  };

  const onPartialExit = async (position, exitQty) => {
    addToast({ type: "info", title: "Partial exit…", body: `${position.sym} × ${exitQty}` });
    try {
      await API.placeOrder(_posPayload(position, position.side === "BUY" ? "SELL" : "BUY", exitQty, "partial-exit"));
      addToast({ type: "up", title: "Partial exit placed", body: `${position.sym} × ${exitQty}` });
    } catch (e) {
      if (_isUnauthError(e)) setReAuthModal({ pendingOrder: null });
      else addToast({ type: "warn", title: "Partial exit failed", body: e?.message ?? String(e) });
    }
  };

  const onAddMore = async (position, addQty) => {
    addToast({ type: "info", title: "Adding to position…", body: `${position.sym} × ${addQty}` });
    try {
      await API.placeOrder(_posPayload(position, position.side, addQty, "add-more"));
      addToast({ type: "up", title: "Add-more order placed", body: `${position.sym} × ${addQty} ${position.side}` });
    } catch (e) {
      if (_isUnauthError(e)) setReAuthModal({ pendingOrder: null });
      else addToast({ type: "warn", title: "Add-more failed", body: e?.message ?? String(e) });
    }
  };

  if (!authed) return <LoginScreen onLogin={onLogin}/>;

  // Map page id -> component
  const renderPage = () => {
    switch (page) {
      case "dashboard": return <Dashboard market={market} holdings={holdings} positions={positions} pnlHistory={pnlHistory} navigate={setPage} layout={tweaks.layout} margin={margin} algos={algos} isDemo={isDemo}/>;
      case "indices":   return <IndicesScreen market={market}/>;
      case "options":   return <OptionChainScreen market={market} onTradeOption={onTradeOption}/>;
      case "watchlist": return <WatchlistScreen market={market} onTrade={onTrade} addToast={addToast} subscribed={subscribed} setSubscribed={setSubscribed} wsStatus={wsStatus}/>;
      case "positions": return <PositionsScreen positions={positions} onSquareOff={onSquareOff} onSquareOffAll={onSquareOffAll} addToast={addToast} margin={margin} onPartialExit={onPartialExit} onAddMore={onAddMore}/>;
      case "orders":    return <OrdersScreen addToast={addToast} liveOrders={orders}/>;
      case "algos":     return <AlgosScreen algos={algos} onConfigure={onConfigure} onToggle={onToggleAlgo} workerPing={workerPing} onOpenLive={(id) => setPage(`live-${id}`)}/>;
      case "live-max-profit":      return <MaxProfitLiveScreen      onBack={() => setPage("algos")}/>;
      case "live-momentum-burst":  return <MomentumBurstLiveScreen  onBack={() => setPage("algos")}/>;
      case "live-option-dip-buyer":return <OptionDipBuyerLiveScreen onBack={() => setPage("algos")}/>;
      case "live-candle-burst":    return <CandleBurstLiveScreen    onBack={() => setPage("algos")}/>;
      case "strategy-guide": return <StrategyGuideScreen/>;
      case "monitor":   return <AlgoMonitorScreen algos={algos} market={market} events={events}/>;
      case "trades":    return <TradesScreen/>;
      case "backtest":  return <BacktestScreen algos={algos}/>;
      case "reports":   return <ReportsScreen pnlHistory={pnlHistory} isDemo={isDemo}/>;
      case "journal":   return <JournalScreen trades={trades} isDemo={isDemo}/>;
      case "settings":  return <SettingsScreen/>;
      default: return null;
    }
  };

  const nifty   = market.indices.find(i => i.sym === "NIFTY");
  const sensex  = market.indices.find(i => i.sym === "SENSEX");
  const banknifty = market.indices.find(i => i.sym === "BANKNIFTY");

  return (
    <div className="app">
      {/* Sidebar */}
      <aside className="side">
        <div className="side-brand">
          <div className="side-brand-mark">V</div>
          <div>
            <div className="side-brand-name">Velox</div>
            <div className="side-brand-tag">Auto-Trading</div>
          </div>
        </div>
        <nav className="side-nav">
          {NAV.map((n, i) =>
            n.section
              ? <div key={i} className="side-section">{n.section}</div>
              : (
                <div key={n.id}
                  className={"side-item " + (page === n.id ? "active" : "")}
                  onClick={() => setPage(n.id)}>
                  <span className="ico"><Icon name={n.icon} size={17}/></span>
                  <span>{n.label}</span>
                  {n.badge && <span className="badge">{n.badge}</span>}
                </div>
              )
          )}
        </nav>
        <div className="side-foot" style={{ position: "relative" }}>
          {/* Profile dropdown */}
          {profileOpen && (
            <div style={{
              position: "absolute", bottom: "calc(100% + 8px)", left: 8, right: 8,
              background: "var(--surface)", border: "1px solid var(--border)",
              borderRadius: 10, boxShadow: "0 8px 24px rgba(0,0,0,0.18)",
              zIndex: 200, overflow: "hidden",
            }}
              onMouseLeave={() => setProfileOpen(false)}
            >
              {/* User info header */}
              <div style={{ padding: "12px 14px", borderBottom: "1px solid var(--border)", background: "var(--surface-2)" }}>
                <div style={{ fontWeight: 600, fontSize: 13 }}>{user?.name || "Trader"}</div>
                <div style={{ fontSize: 11, color: "var(--text-3)", marginTop: 2 }}>
                  {kotakStatus?.connected
                    ? <><span style={{ color: "var(--up)" }}>● </span>Kotak connected · {kotakStatus.ucc}</>
                    : <><span style={{ color: "var(--down)" }}>● </span>Kotak not connected</>}
                </div>
              </div>

              {/* Kotak connect option if not configured or not connected */}
              {(!kotakStatus?.connected || !kotakStatus?.mobile) && (
                <div style={{ padding: "10px 14px", cursor: "pointer", fontSize: 13, display: "flex", alignItems: "center", gap: 8 }}
                  onClick={() => { setProfileOpen(false); setPage("settings"); }}
                  onMouseEnter={e => e.currentTarget.style.background = "var(--surface-2)"}
                  onMouseLeave={e => e.currentTarget.style.background = ""}>
                  <Icon name="settings" size={14}/>
                  {kotakStatus?.mobile ? "Reconnect Kotak" : "Configure Kotak account"}
                </div>
              )}

              <div style={{ padding: "10px 14px", cursor: "pointer", fontSize: 13, display: "flex", alignItems: "center", gap: 8 }}
                onClick={() => { setProfileOpen(false); setPage("settings"); }}
                onMouseEnter={e => e.currentTarget.style.background = "var(--surface-2)"}
                onMouseLeave={e => e.currentTarget.style.background = ""}>
                <Icon name="settings" size={14}/> Settings
              </div>

              <div style={{ height: 1, background: "var(--border)" }}/>

              <div style={{ padding: "10px 14px", cursor: "pointer", fontSize: 13, color: "var(--down)", display: "flex", alignItems: "center", gap: 8 }}
                onClick={onLogout}
                onMouseEnter={e => e.currentTarget.style.background = "var(--surface-2)"}
                onMouseLeave={e => e.currentTarget.style.background = ""}>
                <Icon name="x" size={14}/> Sign out
              </div>
            </div>
          )}

          <div style={{ display: "flex", alignItems: "center", gap: 10, flex: 1, minWidth: 0, cursor: "pointer" }}
            onClick={() => setProfileOpen(o => !o)}>
            <div className="side-avatar" style={{ flexShrink: 0, position: "relative" }}>
              {(user?.name || "T")[0].toUpperCase()}
              {kotakStatus && (
                <span style={{
                  position: "absolute", bottom: -2, right: -2,
                  width: 8, height: 8, borderRadius: "50%",
                  background: kotakStatus.connected ? "var(--up)" : "var(--down)",
                  border: "1.5px solid var(--surface)",
                }}/>
              )}
            </div>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div className="side-user-name">{user?.name || "Trader"}</div>
              <div className="side-user-email" style={{ fontSize: 11 }}>
                {kotakStatus?.connected ? `Kotak · ${kotakStatus.ucc}` : kotakStatus?.mobile ? "Kotak session expired" : "Kotak not configured"}
              </div>
            </div>
            <Icon name="chevronRight" size={12} style={{ color: "var(--text-3)", flexShrink: 0 }}/>
          </div>
        </div>
      </aside>

      {/* Main */}
      <main className="main">
        <div className="topbar">
          <h1>{PAGE_TITLES[page]}</h1>
          <MarketStatusBadge/>
          <div className="topbar-tickers">
            <Ticker label="NIFTY" idx={nifty}/>
            <Ticker label="SENSEX" idx={sensex}/>
            <Ticker label="BANKNIFTY" idx={banknifty}/>
          </div>
          <button className="topbar-icon-btn" title="Notifications">
            <Icon name="bell" size={17}/>
            <span className="dot"/>
          </button>
          {!isDemo && (
            <div style={{ display: "flex", alignItems: "center", gap: 4 }}>
              <button className="topbar-icon-btn" title="Refresh portfolio now"
                onClick={onManualRefresh} disabled={refreshing}
                style={{ opacity: refreshing ? 0.5 : 1 }}>
                <Icon name="refresh" size={16} style={{ animation: refreshing ? "spin 1s linear infinite" : "none" }}/>
              </button>
              <select
                value={refreshInterval}
                onChange={e => onChangeRefreshInterval(Number(e.target.value))}
                title="Auto-refresh interval"
                style={{
                  fontSize: 11, background: "var(--surface-2)", border: "1px solid var(--border)",
                  borderRadius: 6, color: "var(--text-2)", padding: "3px 6px", cursor: "pointer",
                }}>
                <option value={15000}>15s</option>
                <option value={30000}>30s</option>
                <option value={60000}>1m</option>
                <option value={120000}>2m</option>
                <option value={300000}>5m</option>
              </select>
            </div>
          )}
          <button className="topbar-icon-btn" title="Toggle theme"
            onClick={() => {
              const next = tweaks.theme === "dark" ? "light" : "dark";
              setTweak("theme", next);
              if (!isDemo) API.savePrefs({ theme: next, accent: tweaks.accent, layout: tweaks.layout, refreshInterval }).catch(() => {});
            }}>
            <Icon name={tweaks.theme === "dark" ? "sun" : "moon"} size={17}/>
          </button>
        </div>
        {renderPage()}
      </main>

      {/* Toast container */}
      <div className="toast-wrap">
        {toasts.map(t => (
          <div key={t.id} className={"toast " + (t.type || "")}>
            <div style={{ flex: 1 }}>
              <div className="toast-title">{t.title}</div>
              {t.body && <div className="toast-body">{t.body}</div>}
              <div className="toast-time">{new Date(t.ts).toLocaleTimeString("en-IN", { hour: "2-digit", minute: "2-digit", second: "2-digit" })}</div>
            </div>
            <button className="btn-icon btn-ghost" style={{ width: 24, height: 24 }} onClick={() => setToasts(p => p.filter(x => x.id !== t.id))}>
              <Icon name="x" size={13}/>
            </button>
          </div>
        ))}
      </div>

      {/* Modals */}
      {configAlgo && <AlgoConfigModal algo={configAlgo} onClose={() => setConfigAlgo(null)} onStart={onStartAlgoWithConfig} addToast={addToast}/>}
      {tradeModal && <TradeModal data={tradeModal} onClose={() => setTradeModal(null)} onSubmit={handleTradeSubmit}/>}
      {reAuthModal && <ReAuthModal
        pendingOrder={reAuthModal.pendingOrder}
        onClose={() => setReAuthModal(null)}
        onSuccess={async (pendingOrder) => {
          setReAuthModal(null);
          // Reload fresh session tokens, update cached session, reconnect WebSockets
          try {
            const s = await API.loadKotakSession();
            if (s) {
              API.refreshCachedSession(s);
              VeloxWs.disconnect();
              VeloxWs.connect(s.accessToken, s.sid ?? "");
              if (typeof VeloxHsi !== "undefined" && s.baseUrl) {
                VeloxHsi.disconnect();
                VeloxHsi.connect(s.accessToken, s.sid ?? "", s.baseUrl);
              }
            }
          } catch (_) {}
          if (!pendingOrder) return;
          try {
            await _submitOrder(pendingOrder.payload, pendingOrder.form);
          } catch (e) {
            addToast({ type: "warn", title: "Order failed after re-auth", body: e?.message ?? String(e) });
          }
        }}
        addToast={addToast}
      />}

      {/* Tweaks panel */}
      {TweaksPanel && (
        <TweaksPanel ref={tweaksRef} title="Tweaks">
          <TweakSection title="Appearance">
            <TweakRadio label="Theme" value={tweaks.theme} options={[{value:"light",label:"Light"},{value:"dark",label:"Dark"}]} onChange={v => setTweak("theme", v)}/>
            <TweakColor label="Accent" value={tweaks.accent}
              options={[
                { value: "green",  label: "Velox Green",  swatches: ["#00B981"] },
                { value: "violet", label: "Violet",       swatches: ["#7C3AED"] },
                { value: "blue",   label: "Blue",         swatches: ["#2563EB"] },
                { value: "orange", label: "Orange",       swatches: ["#EA580C"] },
              ]}
              onChange={v => { setTweak("accent", v); if (!isDemo) API.savePrefs({ theme: tweaks.theme, accent: v, layout: tweaks.layout, refreshInterval }).catch(() => {}); }}/>
          </TweakSection>
          <TweakSection title="Layout">
            <TweakRadio label="Dashboard panes" value={tweaks.layout}
              options={[{value:"multi",label:"Multi-pane"},{value:"single",label:"Single-pane"}]}
              onChange={v => { setTweak("layout", v); if (!isDemo) API.savePrefs({ theme: tweaks.theme, accent: tweaks.accent, layout: v, refreshInterval }).catch(() => {}); }}/>
          </TweakSection>
        </TweaksPanel>
      )}
    </div>
  );
};

const Ticker = ({ label, idx }) => {
  const up = idx.dayChg >= 0;
  return (
    <div style={{ display: "flex", flexDirection: "column", alignItems: "flex-end" }}>
      <div className="row" style={{ gap: 6, alignItems: "baseline" }}>
        <span className="tt-name">{label}</span>
        <span className={"tt-val " + (idx.lastTick === 1 ? "tick-flash-up" : idx.lastTick === -1 ? "tick-flash-down" : "")}>
          {fmtNum(idx.price, 2)}
        </span>
      </div>
      <span className={"tt-chg " + (up ? "up" : "down")}>
        {up ? "▲" : "▼"} {fmtNum(Math.abs(idx.dayChg), 2)} ({fmtPct(idx.dayChgPct)})
      </span>
    </div>
  );
};

/* ---------------- Re-Auth Modal ---------------- */
const ReAuthModal = ({ pendingOrder, onClose, onSuccess, addToast }) => {
  const [totp, setTotp] = React.useState("");
  const [mpin, setMpin] = React.useState("");
  const [loading, setLoading] = React.useState(false);

  const submit = async () => {
    if (totp.length !== 6 || !mpin) return;
    setLoading(true);
    try {
      await API.kotakConnectDirect({ totp, mpin });
      addToast({ type: "up", title: "Re-authenticated", body: "Session refreshed" });
      await onSuccess(pendingOrder);
    } catch (e) {
      addToast({ type: "warn", title: "Re-auth failed", body: e?.message ?? String(e) });
    } finally { setLoading(false); }
  };

  return (
    <div style={{ position: "fixed", inset: 0, background: "rgba(15,20,25,.6)", zIndex: 200, display: "grid", placeItems: "center", padding: 24 }} onClick={onClose}>
      <div style={{ background: "var(--surface)", borderRadius: 16, width: "100%", maxWidth: 360, boxShadow: "var(--shadow-lg)", padding: "20px 24px" }} onClick={e => e.stopPropagation()}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 12 }}>
          <span style={{ fontWeight: 700, fontSize: 15 }}>Session Expired — Re-authenticate</span>
          <button className="btn-icon btn-ghost" onClick={onClose}><Icon name="x" size={14}/></button>
        </div>
        <p style={{ fontSize: 13, opacity: 0.6, marginBottom: 16 }}>
          Your Kotak session expired. Enter TOTP + MPIN to refresh and place the pending order.
        </p>
        {pendingOrder && (
          <div style={{ background: "var(--surface)", borderRadius: 8, padding: "8px 12px", fontSize: 12, marginBottom: 14, opacity: 0.8 }}>
            Pending: <b>{pendingOrder.form.side}</b> {pendingOrder.form.sym} × {pendingOrder.form.qty}
          </div>
        )}
        <label style={{ fontSize: 12, opacity: 0.5 }}>TOTP (6-digit)</label>
        <input className="input" maxLength={6} value={totp} onChange={e => setTotp(e.target.value.replace(/\D/g, ""))}
          placeholder="000000" style={{ marginTop: 4, marginBottom: 10, letterSpacing: 6, fontSize: 18 }} autoFocus/>
        <label style={{ fontSize: 12, opacity: 0.5 }}>MPIN</label>
        <input className="input" type="password" maxLength={6} value={mpin} onChange={e => setMpin(e.target.value)}
          placeholder="••••••" style={{ marginTop: 4, marginBottom: 14 }}
          onKeyDown={e => e.key === "Enter" && submit()}/>
        <div style={{ display: "flex", gap: 8 }}>
          <button className="btn btn-ghost" style={{ flex: 1 }} onClick={onClose}>Cancel order</button>
          <button className="btn btn-primary" style={{ flex: 2 }} disabled={loading || totp.length !== 6 || !mpin} onClick={submit}>
            {loading ? "Authenticating…" : "Re-auth & Place Order"}
          </button>
        </div>
      </div>
    </div>
  );
};

/* ---------------- Trade Modal ---------------- */
const TradeModal = ({ data, onClose, onSubmit }) => {
  const [side, setSide] = React.useState(data.side || "BUY");
  const [qty, setQty] = React.useState(data.kind === "opt" ? (data.scrip?.lotSize || 50) : 1);
  const [price, setPrice] = React.useState(data.kind === "opt" ? data.ltp : data.stock.price);
  const [priceType, setPriceType] = React.useState("MKT");
  const [product, setProduct] = React.useState(data.kind === "opt" ? "NRML" : "CNC");

  React.useEffect(() => {
    if (data.kind === "opt") setPrice(data.ltp);
    else setPrice(data.stock.price);
  }, [data]);

  const sym = data.kind === "opt" ? `${data.idx} ${data.strike} ${data.type}` : data.stock.sym;
  const total = qty * price;

  return (
    <div style={{ position: "fixed", inset: 0, background: "rgba(15,20,25,.55)", zIndex: 100, display: "grid", placeItems: "center", padding: 24 }} onClick={onClose}>
      <div style={{ background: "var(--surface)", borderRadius: 16, width: "100%", maxWidth: 460, boxShadow: "var(--shadow-lg)" }} onClick={e => e.stopPropagation()}>
        <div style={{ padding: "16px 20px", borderBottom: "1px solid var(--border)" }} className="row-between">
          <div>
            <div style={{ fontSize: 16, fontWeight: 700 }}>{sym}</div>
            <div style={{ fontSize: 12, color: "var(--text-3)" }}>
              {data.kind === "opt" ? "NSE · Options" : data.stock.name + " · NSE"}
              {" · "}LTP <span className="mono" style={{ fontWeight: 600 }}>{fmtNum(data.kind === "opt" ? data.ltp : data.stock.price)}</span>
            </div>
          </div>
          <button className="btn-icon btn-ghost" onClick={onClose}><Icon name="x" size={16}/></button>
        </div>

        <div style={{ padding: "16px 20px" }}>
          <div className="row" style={{ background: "var(--surface-2)", borderRadius: 10, padding: 4, marginBottom: 16 }}>
            <button onClick={() => setSide("BUY")}
              style={{ flex: 1, padding: "8px 0", borderRadius: 7, fontWeight: 600, fontSize: 13,
                background: side === "BUY" ? "var(--up)" : "transparent",
                color: side === "BUY" ? "white" : "var(--text-2)" }}>Buy</button>
            <button onClick={() => setSide("SELL")}
              style={{ flex: 1, padding: "8px 0", borderRadius: 7, fontWeight: 600, fontSize: 13,
                background: side === "SELL" ? "var(--down)" : "transparent",
                color: side === "SELL" ? "white" : "var(--text-2)" }}>Sell</button>
          </div>

          <div className="grid" style={{ gridTemplateColumns: "1fr 1fr", gap: 12, marginBottom: 12 }}>
            <div className="field">
              <label className="field-label">Quantity</label>
              <input className="input mono" type="number" value={qty} onChange={e => setQty(+e.target.value)}/>
            </div>
            <div className="field">
              <label className="field-label">Price ({priceType})</label>
              <input className="input mono" type="number" step="0.05" value={price} onChange={e => setPrice(+e.target.value)} disabled={priceType === "MKT"}/>
            </div>
          </div>

          <div className="row" style={{ gap: 8, marginBottom: 12 }}>
            {["MKT", "LMT", "SL", "SL-M"].map(t => (
              <button key={t} className={"chip " + (priceType === t ? "brand" : "")} onClick={() => setPriceType(t)}>{t}</button>
            ))}
          </div>

          <div className="row" style={{ gap: 8, marginBottom: 16 }}>
            {(data.kind === "opt" ? ["NRML", "MIS"] : ["CNC", "MIS"]).map(p => (
              <button key={p} className={"chip " + (product === p ? "brand" : "")} onClick={() => setProduct(p)}>{p}</button>
            ))}
          </div>

          <div style={{ background: "var(--surface-2)", padding: 12, borderRadius: 10 }} className="row-between">
            <span className="muted" style={{ fontSize: 12 }}>Approx. {side === "BUY" ? "spend" : "receive"}</span>
            <span className="mono" style={{ fontWeight: 700, fontSize: 15 }}>{fmtINR(total)}</span>
          </div>
        </div>

        <div style={{ padding: "14px 20px", borderTop: "1px solid var(--border)", display: "flex", gap: 8 }}>
          <button className="btn btn-secondary" style={{ flex: 1, justifyContent: "center" }} onClick={onClose}>Cancel</button>
          <button className={"btn " + (side === "BUY" ? "btn-buy" : "btn-sell")} style={{ flex: 2, justifyContent: "center" }}
            onClick={() => onSubmit({ sym, side, qty, price, priceType, product, scrip: data.kind === "opt" ? data.scrip : null })}>
            Place {side} order
          </button>
        </div>
      </div>
    </div>
  );
};

/* ---- Market status badge ---- */
function isNseOpen() {
  const now = new Date();
  const day = now.getDay(); // 0=Sun, 6=Sat
  if (day === 0 || day === 6) return false;
  // Convert to IST (UTC+5:30)
  const istMs = now.getTime() + (5.5 * 60 * 60 * 1000);
  const ist = new Date(istMs);
  const h = ist.getUTCHours(), m = ist.getUTCMinutes();
  const mins = h * 60 + m;
  return mins >= 9 * 60 + 15 && mins < 15 * 60 + 30;
}

const MarketStatusBadge = () => {
  const [now, setNow] = React.useState(new Date());
  React.useEffect(() => {
    const id = setInterval(() => setNow(new Date()), 1000);
    return () => clearInterval(id);
  }, []);

  const open = isNseOpen();
  const istMs = now.getTime() + (5.5 * 60 * 60 * 1000);
  const ist = new Date(istMs);
  const timeStr = ist.toISOString().slice(11, 19) + " IST";

  return (
    <div className="market-status" title={open ? "NSE market is open" : "NSE market is closed"}>
      <span className={open ? "pulse" : ""} style={open ? {} : { background: "var(--text-3)", borderRadius: "50%", display: "inline-block", width: 7, height: 7 }}/>
      {open ? "Market open" : "Market closed"} · {timeStr}
    </div>
  );
};

ReactDOM.createRoot(document.getElementById("root")).render(<App/>);
