import { createFileRoute } from "@tanstack/react-router";
import { useEffect, useMemo, useRef, useState } from "react";
import {
  createChart,
  CandlestickSeries,
  LineSeries,
  HistogramSeries,
  type IChartApi,
  type ISeriesApi,
  type IPriceLine,
  type UTCTimestamp,
  type CandlestickData,
  type LineData,
  type HistogramData,
  LineStyle,
} from "lightweight-charts";
import {
  RSI, MACD, BollingerBands, ATR, EMA,
} from "technicalindicators";
import { Card } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label";
import { Switch } from "@/components/ui/switch";
import { Input } from "@/components/ui/input";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import {
  Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList,
} from "@/components/ui/command";
import {
  Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle,
} from "@/components/ui/dialog";
import { Activity, Check, ChevronsUpDown, Loader2 } from "lucide-react";
import { useDeltaProducts } from "@/hooks/use-delta-products";
import { useDeltaHistory, type Resolution } from "@/hooks/use-delta-candles";
import { cn } from "@/lib/utils";
import { useDeltaTickers } from "@/hooks/use-delta-tickers";
import { useBackendStream } from "@/hooks/use-backend-stream";
import { backendApi } from "@/lib/backend-client";
import { toast } from "sonner";
import { useAuth } from "@/lib/auth";
import { useConnectionToasts } from "@/hooks/use-connection-toasts";

export const Route = createFileRoute("/_app/chart")({ component: ChartPage });

const FALLBACK_SYMBOLS = ["BTCUSDT", "ETHUSDT", "SOLUSDT", "BNBUSDT", "XRPUSDT", "AVAXUSDT"];

type Candle = {
  time: UTCTimestamp;
  open: number;
  high: number;
  low: number;
  close: number;
  volume: number;
};

/** Merge Delta historical candles with in-memory live ticks for the current bucket. */
function useLiveCandles(symbol: string, resolution: Resolution) {
  const { candles: history, loading: historyLoading, bucketSec } = useDeltaHistory(symbol, resolution);
  const { tickers, connection } = useDeltaTickers([symbol]);
  const [candles, setCandles] = useState<Candle[]>([]);
  const latencyRef = useRef<number>(0);

  // Seed candles when history changes
  useEffect(() => {
    setCandles(history);
  }, [history]);

  // Merge live ticks into last bucket / append new bucket
  useEffect(() => {
    const t = tickers[symbol];
    if (!t) return;
    latencyRef.current = Date.now() - t.lastUpdate;
    const bucket = Math.floor(t.lastUpdate / 1000 / bucketSec) * bucketSec;
    setCandles((prev) => {
      const last = prev[prev.length - 1];
      if (!last || (last.time as number) < bucket) {
        const seed: Candle = { time: bucket as UTCTimestamp, open: t.price, high: t.price, low: t.price, close: t.price, volume: 0 };
        const next = [...prev, seed];
        return next.length > 600 ? next.slice(-600) : next;
      }
      if ((last.time as number) > bucket) return prev; // stale tick
      const updated: Candle = {
        ...last,
        high: Math.max(last.high, t.price),
        low: Math.min(last.low, t.price),
        close: t.price,
      };
      return [...prev.slice(0, -1), updated];
    });
  }, [tickers, symbol, bucketSec]);

  return { candles, connection, latencyMs: latencyRef.current, lastPrice: tickers[symbol]?.price, historyLoading };
}

interface IndicatorFlags {
  ema: boolean;
  bollinger: boolean;
  rsi: boolean;
  macd: boolean;
  atr: boolean;
}

const DEFAULT_INDICATORS: IndicatorFlags = { ema: true, bollinger: false, rsi: true, macd: false, atr: false };

const CONTRACT_FILTERS = [
  { id: "perpetual_futures", label: "Perpetuals" },
  { id: "futures", label: "Futures" },
  { id: "options", label: "Options" },
  { id: "spot", label: "Spot" },
  { id: "all", label: "All" },
] as const;
type ContractFilter = (typeof CONTRACT_FILTERS)[number]["id"];

function matchesFilter(ct: string, f: ContractFilter) {
  if (f === "all") return true;
  if (f === "options") return ct.includes("options");
  if (f === "futures") return ct === "futures";
  if (f === "spot") return ct === "spot" || ct === "";
  return ct === f;
}

function ChartPage() {
  const { user } = useAuth();
  const [symbol, setSymbol] = useState(() => {
    if (typeof window !== "undefined") {
      const s = window.sessionStorage.getItem("chart:symbol");
      if (s) { window.sessionStorage.removeItem("chart:symbol"); return s; }
    }
    return "BTCUSDT";
  });
  const [pickerOpen, setPickerOpen] = useState(false);
  const [contractFilter, setContractFilter] = useState<ContractFilter>("perpetual_futures");
  const { products, loading: productsLoading } = useDeltaProducts();
  const allOptions = useMemo(() => {
    if (!products.length) return FALLBACK_SYMBOLS.map((s) => ({ symbol: s, description: s, contract_type: "perpetual_futures" }));
    return products
      .filter((p) => p.state === undefined || p.state === "live")
      .map((p) => ({ symbol: p.symbol, description: p.description || p.symbol, contract_type: p.contract_type || "" }));
  }, [products]);
  const symbolOptions = useMemo(() => {
    return allOptions
      .filter((o) => matchesFilter(o.contract_type, contractFilter))
      .sort((a, b) => a.symbol.localeCompare(b.symbol));
  }, [allOptions, contractFilter]);
  const currentDesc = allOptions.find((o) => o.symbol === symbol)?.description;
  const [ind, setInd] = useState<IndicatorFlags>(DEFAULT_INDICATORS);
  const [resolution, setResolution] = useState<Resolution>("1m");
  const { candles, connection, latencyMs, lastPrice, historyLoading } = useLiveCandles(symbol, resolution);
  const { positions } = useBackendStream();
  const readOnly = Boolean(user?.impersonating);
  useConnectionToasts(`${symbol} feed`, connection);

  // ---- Manual trade panel state ----
  const [tradeSide, setTradeSide] = useState<"LONG" | "SHORT">("LONG");
  const [tradeSize, setTradeSize] = useState<string>("100");
  const [tradeLeverage, setTradeLeverage] = useState<string>("5");
  const [tradeSlPct, setTradeSlPct] = useState<string>("1.0");
  const [tradeTpPct, setTradeTpPct] = useState<string>("2.0");
  const [tradeSubmitting, setTradeSubmitting] = useState(false);
  const tradeEntry = lastPrice ?? 0;
  const tradeSl = useMemo(() => {
    const p = Number(tradeSlPct) / 100;
    if (!tradeEntry || !Number.isFinite(p)) return 0;
    return +(tradeSide === "LONG" ? tradeEntry * (1 - p) : tradeEntry * (1 + p)).toFixed(2);
  }, [tradeEntry, tradeSlPct, tradeSide]);
  const tradeTp = useMemo(() => {
    const p = Number(tradeTpPct) / 100;
    if (!tradeEntry || !Number.isFinite(p)) return 0;
    return +(tradeSide === "LONG" ? tradeEntry * (1 + p) : tradeEntry * (1 - p)).toFixed(2);
  }, [tradeEntry, tradeTpPct, tradeSide]);
  const tradeValid =
    tradeEntry > 0 &&
    Number(tradeSize) > 0 &&
    tradeSl > 0 &&
    tradeTp > 0 &&
    (tradeSide === "LONG" ? tradeSl < tradeEntry && tradeTp > tradeEntry : tradeSl > tradeEntry && tradeTp < tradeEntry);

  async function submitManualTrade() {
    if (!tradeValid || readOnly) return;
    setTradeSubmitting(true);
    try {
      const r = await backendApi.placeManualTrade({
        coin_symbol: symbol,
        position_side: tradeSide,
        entry_price: tradeEntry,
        stop_loss: tradeSl,
        take_profit: tradeTp,
        order_size_value: Number(tradeSize),
        leverage: Number(tradeLeverage) || 1,
        order_sizing_type: "FIXED_AMOUNT",
      });
      toast.success(`${tradeSide} ${symbol} placed`, {
        description: `Entry ${tradeEntry.toFixed(2)} · SL ${tradeSl.toFixed(2)} · TP ${tradeTp.toFixed(2)}`,
      });
      void r;
    } catch (e) {
      toast.error(e instanceof Error ? e.message : "Failed to place trade");
    } finally {
      setTradeSubmitting(false);
    }
  }


  // Screen-reader / low-vision summary of the latest indicator values. Recomputed
  // once per candle update so an aria-live consumer can hear the key numbers.
  const summary = useMemo(() => {
    if (candles.length === 0) return null;
    const closes = candles.map((c) => c.close);
    const last = closes[closes.length - 1];
    const rsiArr = RSI.calculate({ period: 14, values: closes });
    const emaFast = EMA.calculate({ period: 9, values: closes });
    const emaSlow = EMA.calculate({ period: 21, values: closes });
    const macdArr = MACD.calculate({ values: closes, fastPeriod: 12, slowPeriod: 26, signalPeriod: 9, SimpleMAOscillator: false, SimpleMASignal: false });
    const rsi = rsiArr[rsiArr.length - 1];
    const ef = emaFast[emaFast.length - 1];
    const es = emaSlow[emaSlow.length - 1];
    const macd = macdArr[macdArr.length - 1];
    const cross = ef != null && es != null ? (ef > es ? "bullish (fast above slow)" : "bearish (fast below slow)") : "unknown";
    return { last, rsi, ef, es, cross, macd };
  }, [candles]);

  // Find the open position for this symbol (SL/TP drag target)
  const position = useMemo(
    () => positions.find((p) => p.coin_symbol === symbol && p.order_status !== "CANCELED"),
    [positions, symbol],
  );

  // Chart refs
  const containerRef = useRef<HTMLDivElement | null>(null);
  const rsiRef = useRef<HTMLDivElement | null>(null);
  const macdRef = useRef<HTMLDivElement | null>(null);
  const chartRef = useRef<IChartApi | null>(null);
  const candleSeriesRef = useRef<ISeriesApi<"Candlestick"> | null>(null);
  const volumeSeriesRef = useRef<ISeriesApi<"Histogram"> | null>(null);
  const emaFastRef = useRef<ISeriesApi<"Line"> | null>(null);
  const emaSlowRef = useRef<ISeriesApi<"Line"> | null>(null);
  const bbUpperRef = useRef<ISeriesApi<"Line"> | null>(null);
  const bbLowerRef = useRef<ISeriesApi<"Line"> | null>(null);
  const bbMidRef = useRef<ISeriesApi<"Line"> | null>(null);
  const atrRef = useRef<ISeriesApi<"Line"> | null>(null);
  const rsiChartRef = useRef<IChartApi | null>(null);
  const rsiSeriesRef = useRef<ISeriesApi<"Line"> | null>(null);
  const macdChartRef = useRef<IChartApi | null>(null);
  const macdMainRef = useRef<ISeriesApi<"Line"> | null>(null);
  const macdSignalRef = useRef<ISeriesApi<"Line"> | null>(null);
  const macdHistRef = useRef<ISeriesApi<"Histogram"> | null>(null);
  const slLineRef = useRef<IPriceLine | null>(null);
  const tpLineRef = useRef<IPriceLine | null>(null);
  const entryLineRef = useRef<IPriceLine | null>(null);

  // Draft SL/TP for drag/edit → shown in "Apply changes" dialog
  const [draftSl, setDraftSl] = useState<number | null>(null);
  const [draftTp, setDraftTp] = useState<number | null>(null);
  const [confirmOpen, setConfirmOpen] = useState(false);
  const [applying, setApplying] = useState(false);

  // Init main chart
  useEffect(() => {
    if (!containerRef.current) return;
    const chart = createChart(containerRef.current, {
      layout: { background: { color: "transparent" }, textColor: "#cbd5e1", fontSize: 11 },
      grid: { vertLines: { color: "rgba(148,163,184,0.06)" }, horzLines: { color: "rgba(148,163,184,0.06)" } },
      rightPriceScale: { borderColor: "rgba(148,163,184,0.15)" },
      timeScale: { borderColor: "rgba(148,163,184,0.15)", timeVisible: true, secondsVisible: false },
      crosshair: { mode: 1 },
      autoSize: true,
    });
    chartRef.current = chart;
    candleSeriesRef.current = chart.addSeries(CandlestickSeries, {
      upColor: "#10b981", downColor: "#ef4444", borderVisible: false,
      wickUpColor: "#10b981", wickDownColor: "#ef4444",
    });
    volumeSeriesRef.current = chart.addSeries(HistogramSeries, {
      color: "#64748b66",
      priceFormat: { type: "volume" },
      priceScaleId: "volume",
      lastValueVisible: false,
      priceLineVisible: false,
    });
    chart.priceScale("volume").applyOptions({ scaleMargins: { top: 0.8, bottom: 0 } });
    return () => { chart.remove(); chartRef.current = null; };
  }, []);

  // Init RSI/MACD sub-charts on flag toggle
  useEffect(() => {
    if (ind.rsi && rsiRef.current && !rsiChartRef.current) {
      const c = createChart(rsiRef.current, {
        layout: { background: { color: "transparent" }, textColor: "#cbd5e1", fontSize: 10 },
        grid: { vertLines: { color: "rgba(148,163,184,0.05)" }, horzLines: { color: "rgba(148,163,184,0.05)" } },
        rightPriceScale: { borderColor: "rgba(148,163,184,0.15)" },
        timeScale: { visible: false },
        autoSize: true,
      });
      rsiChartRef.current = c;
      rsiSeriesRef.current = c.addSeries(LineSeries, { color: "#a855f7", lineWidth: 2 });
      rsiSeriesRef.current.createPriceLine({ price: 70, color: "#ef4444", lineWidth: 1, lineStyle: LineStyle.Dashed, axisLabelVisible: true, title: "70" });
      rsiSeriesRef.current.createPriceLine({ price: 30, color: "#10b981", lineWidth: 1, lineStyle: LineStyle.Dashed, axisLabelVisible: true, title: "30" });
    }
    if (!ind.rsi && rsiChartRef.current) {
      rsiChartRef.current.remove();
      rsiChartRef.current = null;
      rsiSeriesRef.current = null;
    }
    if (ind.macd && macdRef.current && !macdChartRef.current) {
      const c = createChart(macdRef.current, {
        layout: { background: { color: "transparent" }, textColor: "#cbd5e1", fontSize: 10 },
        grid: { vertLines: { color: "rgba(148,163,184,0.05)" }, horzLines: { color: "rgba(148,163,184,0.05)" } },
        rightPriceScale: { borderColor: "rgba(148,163,184,0.15)" },
        timeScale: { visible: false },
        autoSize: true,
      });
      macdChartRef.current = c;
      macdHistRef.current = c.addSeries(HistogramSeries, { color: "#64748b" });
      macdMainRef.current = c.addSeries(LineSeries, { color: "#3b82f6", lineWidth: 2 });
      macdSignalRef.current = c.addSeries(LineSeries, { color: "#f59e0b", lineWidth: 1 });
    }
    if (!ind.macd && macdChartRef.current) {
      macdChartRef.current.remove();
      macdChartRef.current = null;
      macdMainRef.current = macdSignalRef.current = macdHistRef.current = null;
    }
  }, [ind.rsi, ind.macd]);

  // Manage indicator overlay series on main chart based on flags
  useEffect(() => {
    const chart = chartRef.current;
    if (!chart) return;
    const ensure = (
      ref: React.MutableRefObject<ISeriesApi<"Line"> | null>,
      color: string,
      enabled: boolean,
      lineWidth: 1 | 2 = 1,
    ) => {
      if (enabled && !ref.current) ref.current = chart.addSeries(LineSeries, { color, lineWidth, priceLineVisible: false, lastValueVisible: false });
      if (!enabled && ref.current) { chart.removeSeries(ref.current); ref.current = null; }
    };
    ensure(emaFastRef, "#22d3ee", ind.ema, 2);
    ensure(emaSlowRef, "#f472b6", ind.ema, 2);
    ensure(bbUpperRef, "#94a3b8", ind.bollinger);
    ensure(bbLowerRef, "#94a3b8", ind.bollinger);
    ensure(bbMidRef, "#64748b", ind.bollinger);
    ensure(atrRef, "#f59e0b", ind.atr);
  }, [ind.ema, ind.bollinger, ind.atr]);

  // Feed data into series whenever candles change
  useEffect(() => {
    if (!candleSeriesRef.current || candles.length === 0) return;
    const data: CandlestickData[] = candles.map((c) => ({ time: c.time, open: c.open, high: c.high, low: c.low, close: c.close }));
    candleSeriesRef.current.setData(data);
    if (volumeSeriesRef.current) {
      const vol: HistogramData[] = candles.map((c) => ({
        time: c.time,
        value: c.volume,
        color: c.close >= c.open ? "#10b98144" : "#ef444444",
      }));
      volumeSeriesRef.current.setData(vol);
    }

    const closes = candles.map((c) => c.close);
    const highs = candles.map((c) => c.high);
    const lows = candles.map((c) => c.low);
    const times = candles.map((c) => c.time);
    const asLine = (values: (number | undefined)[], offset: number): LineData[] =>
      values
        .map((v, i) => (v == null ? null : ({ time: times[i + offset] as UTCTimestamp, value: v } as LineData)))
        .filter((x): x is LineData => x != null && x.time != null);

    if (ind.ema && emaFastRef.current && emaSlowRef.current) {
      const fast = EMA.calculate({ period: 9, values: closes });
      const slow = EMA.calculate({ period: 21, values: closes });
      emaFastRef.current.setData(asLine(fast, closes.length - fast.length));
      emaSlowRef.current.setData(asLine(slow, closes.length - slow.length));
    }
    if (ind.bollinger && bbUpperRef.current && bbLowerRef.current && bbMidRef.current) {
      const bb = BollingerBands.calculate({ period: 20, stdDev: 2, values: closes });
      const off = closes.length - bb.length;
      bbUpperRef.current.setData(asLine(bb.map((b) => b.upper), off));
      bbLowerRef.current.setData(asLine(bb.map((b) => b.lower), off));
      bbMidRef.current.setData(asLine(bb.map((b) => b.middle), off));
    }
    if (ind.atr && atrRef.current) {
      const atr = ATR.calculate({ period: 14, high: highs, low: lows, close: closes });
      atrRef.current.setData(asLine(atr, closes.length - atr.length));
    }
    if (ind.rsi && rsiSeriesRef.current) {
      const rsi = RSI.calculate({ period: 14, values: closes });
      rsiSeriesRef.current.setData(asLine(rsi, closes.length - rsi.length));
    }
    if (ind.macd && macdMainRef.current && macdSignalRef.current && macdHistRef.current) {
      const macd = MACD.calculate({
        values: closes, fastPeriod: 12, slowPeriod: 26, signalPeriod: 9,
        SimpleMAOscillator: false, SimpleMASignal: false,
      });
      const off = closes.length - macd.length;
      macdMainRef.current.setData(asLine(macd.map((m) => m.MACD), off));
      macdSignalRef.current.setData(asLine(macd.map((m) => m.signal), off));
      const hist: HistogramData[] = [];
      macd.forEach((m, i) => {
        if (m.histogram == null) return;
        hist.push({ time: times[i + off] as UTCTimestamp, value: m.histogram, color: m.histogram >= 0 ? "#10b98166" : "#ef444466" });
      });
      macdHistRef.current.setData(hist);
    }
  }, [candles, ind]);

  // Position price-lines (entry / SL / TP)
  useEffect(() => {
    const series = candleSeriesRef.current;
    if (!series) return;
    // Clear old lines
    for (const ref of [entryLineRef, slLineRef, tpLineRef]) {
      if (ref.current) { series.removePriceLine(ref.current); ref.current = null; }
    }
    if (!position) { setDraftSl(null); setDraftTp(null); return; }
    entryLineRef.current = series.createPriceLine({
      price: position.entry_price, color: "#38bdf8", lineWidth: 1,
      lineStyle: LineStyle.Dotted, axisLabelVisible: true, title: `ENTRY ${position.position_side}`,
    });
    const sl = draftSl ?? position.stop_loss;
    const tp = draftTp ?? position.take_profit;
    slLineRef.current = series.createPriceLine({
      price: sl, color: "#ef4444", lineWidth: 2, lineStyle: LineStyle.Solid,
      axisLabelVisible: true, title: `SL ${sl.toFixed(2)}`,
    });
    tpLineRef.current = series.createPriceLine({
      price: tp, color: "#10b981", lineWidth: 2, lineStyle: LineStyle.Solid,
      axisLabelVisible: true, title: `TP ${tp.toFixed(2)}`,
    });
    if (draftSl == null) setDraftSl(position.stop_loss);
    if (draftTp == null) setDraftTp(position.take_profit);
  }, [position, draftSl, draftTp]);

  // Drag SL/TP by dragging on the price axis area of the chart
  useEffect(() => {
    const chart = chartRef.current;
    const series = candleSeriesRef.current;
    const container = containerRef.current;
    if (!chart || !series || !container || !position || readOnly) return;

    let dragging: "sl" | "tp" | null = null;
    const priceAt = (y: number) => series.coordinateToPrice(y);
    const near = (a: number, b: number) => Math.abs(a - b) / b < 0.006;

    const onDown = (e: PointerEvent) => {
      const rect = container.getBoundingClientRect();
      const y = e.clientY - rect.top;
      const price = priceAt(y);
      if (price == null) return;
      const sl = draftSl ?? position.stop_loss;
      const tp = draftTp ?? position.take_profit;
      if (near(price, sl)) dragging = "sl";
      else if (near(price, tp)) dragging = "tp";
      if (dragging) {
        container.setPointerCapture(e.pointerId);
        container.style.cursor = "ns-resize";
      }
    };
    const onMove = (e: PointerEvent) => {
      if (!dragging) return;
      const rect = container.getBoundingClientRect();
      const p = priceAt(e.clientY - rect.top);
      if (p == null) return;
      if (dragging === "sl") setDraftSl(Number(p.toFixed(2)));
      else setDraftTp(Number(p.toFixed(2)));
    };
    const onUp = (e: PointerEvent) => {
      if (dragging) {
        container.releasePointerCapture(e.pointerId);
        container.style.cursor = "";
        const sl = draftSl ?? position.stop_loss;
        const tp = draftTp ?? position.take_profit;
        if (sl !== position.stop_loss || tp !== position.take_profit) setConfirmOpen(true);
        dragging = null;
      }
    };
    container.addEventListener("pointerdown", onDown);
    container.addEventListener("pointermove", onMove);
    container.addEventListener("pointerup", onUp);
    return () => {
      container.removeEventListener("pointerdown", onDown);
      container.removeEventListener("pointermove", onMove);
      container.removeEventListener("pointerup", onUp);
    };
  }, [position, draftSl, draftTp, readOnly]);

  // Validation for the draft SL/TP
  const validation = useMemo(() => {
    if (!position || draftSl == null || draftTp == null) return { ok: false, msg: "" };
    const entry = position.entry_price;
    if (position.position_side === "LONG") {
      if (!(draftSl < entry)) return { ok: false, msg: "SL must be below entry for LONG" };
      if (!(draftTp > entry)) return { ok: false, msg: "TP must be above entry for LONG" };
    } else {
      if (!(draftSl > entry)) return { ok: false, msg: "SL must be above entry for SHORT" };
      if (!(draftTp < entry)) return { ok: false, msg: "TP must be below entry for SHORT" };
    }
    return { ok: true, msg: "" };
  }, [position, draftSl, draftTp]);

  async function applyChanges() {
    if (!position || draftSl == null || draftTp == null || !validation.ok) return;
    setApplying(true);
    try {
      await backendApi.updatePositionSlTp(position._id, { stop_loss: draftSl, take_profit: draftTp });
      toast.success("SL/TP updated");
      setConfirmOpen(false);
    } catch (e) {
      toast.error(e instanceof Error ? e.message : "Failed to update");
    } finally {
      setApplying(false);
    }
  }

  const wsHealthy = connection.state === "open";
  const latencyLabel = latencyMs > 0 ? `${latencyMs} ms` : "—";

  return (
    <div className="grid gap-4">
      <Card className="glass-strong p-4">
        <div className="flex flex-wrap items-center gap-3">
          <div className="flex items-center gap-2">
            <Label className="text-xs uppercase text-muted-foreground">Symbol</Label>
            <Popover open={pickerOpen} onOpenChange={setPickerOpen}>
              <PopoverTrigger asChild>
                <Button
                  variant="outline"
                  role="combobox"
                  aria-expanded={pickerOpen}
                  className="w-56 justify-between font-mono"
                >
                  <span className="truncate">{symbol}</span>
                  <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-60" />
                </Button>
              </PopoverTrigger>
              <PopoverContent className="w-96 p-0" align="start">
                <div className="flex flex-wrap gap-1 border-b border-border/50 p-2">
                  {CONTRACT_FILTERS.map((f) => (
                    <button
                      key={f.id}
                      type="button"
                      onClick={() => setContractFilter(f.id)}
                      className={cn(
                        "rounded-md px-2 py-1 text-[11px] uppercase tracking-wider transition",
                        contractFilter === f.id
                          ? "bg-primary/20 text-primary"
                          : "text-muted-foreground hover:bg-muted/40",
                      )}
                    >
                      {f.label}
                    </button>
                  ))}
                </div>
                <Command
                  filter={(value, search) => {
                    const s = search.toLowerCase();
                    return value.toLowerCase().includes(s) ? 1 : 0;
                  }}
                >
                  <CommandInput placeholder={productsLoading ? "Loading coins…" : `Search ${symbolOptions.length} ${CONTRACT_FILTERS.find((f) => f.id === contractFilter)?.label.toLowerCase()}…`} />
                  <CommandList className="max-h-80">
                    <CommandEmpty>No coin found.</CommandEmpty>
                    <CommandGroup>
                      {symbolOptions.map((opt) => (
                        <CommandItem
                          key={opt.symbol}
                          value={`${opt.symbol} ${opt.description}`}
                          onSelect={() => { setSymbol(opt.symbol); setPickerOpen(false); }}
                        >
                          <Check className={cn("mr-2 h-4 w-4", symbol === opt.symbol ? "opacity-100" : "opacity-0")} />
                          <div className="flex min-w-0 flex-1 items-center justify-between gap-2">
                            <span className="truncate font-mono text-xs">{opt.symbol}</span>
                            {opt.contract_type && (
                              <span className="shrink-0 text-[10px] uppercase text-muted-foreground">
                                {opt.contract_type.replace(/_/g, " ")}
                              </span>
                            )}
                          </div>
                        </CommandItem>
                      ))}
                    </CommandGroup>
                  </CommandList>
                </Command>
              </PopoverContent>
            </Popover>
            {currentDesc && currentDesc !== symbol && (
              <span className="hidden text-[11px] text-muted-foreground md:inline">{currentDesc}</span>
            )}
          </div>
          <div className="ml-auto flex items-center gap-2 text-xs">
            <span className={`inline-flex h-2 w-2 rounded-full ${wsHealthy ? "bg-emerald-400 animate-pulse" : "bg-amber-400"}`} aria-hidden />
            <span className="text-muted-foreground">{wsHealthy ? "Live" : connection.state}</span>
            <span className="text-muted-foreground">·</span>
            <Activity className="h-3.5 w-3.5 text-muted-foreground" aria-hidden />
            <span className="tabular-nums text-muted-foreground">{latencyLabel} latency</span>
            {lastPrice != null && (<><span className="text-muted-foreground">·</span>
              <span className="font-mono tabular-nums">{lastPrice.toFixed(2)}</span></>)}
          </div>
        </div>
        <div className="mt-3 flex flex-wrap items-center gap-4 text-xs">
          <div className="inline-flex rounded-md border border-border/50 p-0.5" role="group" aria-label="Timeframe">
            {(["1m","5m","15m","30m","1h","4h","1d","1w"] as Resolution[]).map((r) => (
              <button
                key={r}
                type="button"
                onClick={() => setResolution(r)}
                aria-pressed={resolution === r}
                className={cn(
                  "rounded px-2 py-1 text-[11px] uppercase tracking-wider transition",
                  resolution === r ? "bg-primary/20 text-primary" : "text-muted-foreground hover:bg-muted/40",
                )}
              >
                {r}
              </button>
            ))}
          </div>
          <div className="h-4 w-px bg-border/50" aria-hidden />
          {(Object.keys(DEFAULT_INDICATORS) as (keyof IndicatorFlags)[]).map((k) => (
            <label key={k} className="flex items-center gap-2">
              <Switch checked={ind[k]} onCheckedChange={(v) => setInd((s) => ({ ...s, [k]: v }))} />
              <span className="uppercase tracking-wider">{k === "ema" ? "EMA cross" : k}</span>
            </label>
          ))}
        </div>
      </Card>

      <Card className="glass-strong relative p-2">
        <div ref={containerRef} className="h-[440px] w-full" aria-label={`${symbol} live candlestick chart`} role="img" />
        {ind.rsi && <div ref={rsiRef} className="h-[110px] w-full border-t border-border/50" aria-label="RSI indicator" role="img" />}
        {ind.macd && <div ref={macdRef} className="h-[110px] w-full border-t border-border/50" aria-label="MACD indicator" role="img" />}
        {candles.length === 0 && (
          <div className="pointer-events-none absolute inset-0 grid place-items-center px-4 text-center text-xs text-muted-foreground">
            <div className="inline-flex flex-col items-center gap-2">
              <span className="inline-flex items-center gap-2">
                <Loader2 className="h-4 w-4 animate-spin" />
                {historyLoading ? "Loading Delta history" : "Waiting for data on"} <span className="font-mono text-foreground">{symbol}</span>…
              </span>
              <span className="text-[11px] opacity-70">
                Illiquid contracts (options, weekly futures) may not stream. Try a Perpetual like BTCUSDT / ETHUSDT.
              </span>
            </div>
          </div>
        )}
      </Card>

      {/* Screen-reader summary of key indicator values; visually hidden. */}
      <div className="sr-only" aria-live="polite" aria-atomic="true">
        {summary
          ? `${symbol} at ${summary.last?.toFixed(2)}. RSI ${summary.rsi != null ? summary.rsi.toFixed(1) : "n/a"}. EMA cross ${summary.cross}. MACD ${summary.macd?.MACD != null ? summary.macd.MACD.toFixed(3) : "n/a"}, signal ${summary.macd?.signal != null ? summary.macd.signal.toFixed(3) : "n/a"}. Feed ${connection.state}${latencyMs != null ? `, latency ${latencyMs} milliseconds` : ""}.`
          : `${symbol} loading chart data.`}
      </div>

      {/* ---- Manual Trade Panel ---- */}
      <Card className="glass-strong p-4" aria-label="Place manual trade">
        <div className="flex flex-wrap items-end gap-4">
          <div>
            <div className="text-xs uppercase text-muted-foreground">Place trade on</div>
            <div className="font-mono text-sm">{symbol}</div>
            <div className="text-[11px] text-muted-foreground">
              Entry {tradeEntry > 0 ? tradeEntry.toFixed(2) : "—"} (market)
            </div>
          </div>
          <div
            className="inline-flex rounded-md border border-border/50 p-0.5"
            role="group"
            aria-label="Trade side"
          >
            <button
              type="button"
              onClick={() => setTradeSide("LONG")}
              aria-pressed={tradeSide === "LONG"}
              className={cn(
                "rounded px-3 py-1 text-xs font-semibold uppercase tracking-wider transition",
                tradeSide === "LONG" ? "bg-emerald-500/20 text-emerald-300" : "text-muted-foreground hover:bg-muted/40",
              )}
            >
              Long
            </button>
            <button
              type="button"
              onClick={() => setTradeSide("SHORT")}
              aria-pressed={tradeSide === "SHORT"}
              className={cn(
                "rounded px-3 py-1 text-xs font-semibold uppercase tracking-wider transition",
                tradeSide === "SHORT" ? "bg-rose-500/20 text-rose-300" : "text-muted-foreground hover:bg-muted/40",
              )}
            >
              Short
            </button>
          </div>
          <div>
            <Label className="text-xs" htmlFor="trade-size">Size (USD)</Label>
            <Input
              id="trade-size"
              type="number" min="1" step="1"
              value={tradeSize}
              onChange={(e) => setTradeSize(e.target.value)}
              className="h-9 w-28 font-mono"
              disabled={readOnly}
            />
          </div>
          <div>
            <Label className="text-xs" htmlFor="trade-lev">Leverage</Label>
            <Input
              id="trade-lev"
              type="number" min="1" max="125" step="1"
              value={tradeLeverage}
              onChange={(e) => setTradeLeverage(e.target.value)}
              className="h-9 w-20 font-mono"
              disabled={readOnly}
            />
          </div>
          <div>
            <Label className="text-xs" htmlFor="trade-sl">SL %</Label>
            <Input
              id="trade-sl"
              type="number" min="0.1" step="0.1"
              value={tradeSlPct}
              onChange={(e) => setTradeSlPct(e.target.value)}
              className="h-9 w-20 font-mono"
              disabled={readOnly}
            />
            <div className="mt-1 font-mono text-[11px] text-rose-400">{tradeSl > 0 ? tradeSl.toFixed(2) : "—"}</div>
          </div>
          <div>
            <Label className="text-xs" htmlFor="trade-tp">TP %</Label>
            <Input
              id="trade-tp"
              type="number" min="0.1" step="0.1"
              value={tradeTpPct}
              onChange={(e) => setTradeTpPct(e.target.value)}
              className="h-9 w-20 font-mono"
              disabled={readOnly}
            />
            <div className="mt-1 font-mono text-[11px] text-emerald-400">{tradeTp > 0 ? tradeTp.toFixed(2) : "—"}</div>
          </div>
          <Button
            data-testid="place-trade-btn"
            className={cn(
              "ml-auto min-w-[160px]",
              tradeSide === "LONG" ? "bg-emerald-500 hover:bg-emerald-400 text-emerald-950" : "bg-rose-500 hover:bg-rose-400 text-rose-950",
            )}
            onClick={submitManualTrade}
            disabled={readOnly || !tradeValid || tradeSubmitting}
          >
            {tradeSubmitting ? <Loader2 className="mr-2 h-4 w-4 animate-spin" /> : null}
            {tradeSide === "LONG" ? "Buy / Long" : "Sell / Short"}
          </Button>
        </div>
        {!tradeValid && tradeEntry > 0 && (
          <div className="mt-2 text-xs text-destructive">
            Check SL/TP: for {tradeSide}, {tradeSide === "LONG" ? "SL must be below entry and TP above" : "SL must be above entry and TP below"}.
          </div>
        )}
        {readOnly && (
          <div className="mt-2 text-xs text-amber-400">Read-only: impersonation sessions cannot place trades.</div>
        )}
      </Card>



      {position ? (
        <Card className="glass-strong p-4">
          <div className="flex flex-wrap items-center gap-3">
            <div>
              <div className="text-xs uppercase text-muted-foreground">Open position on {symbol}</div>
              <div className="font-mono text-sm">
                {position.position_side} · entry {position.entry_price.toFixed(2)} · size {position.size}
              </div>
              <div className="mt-1 text-[11px] text-muted-foreground">
                Drag the red (SL) or green (TP) line on the chart, or edit below. You'll confirm before saving.
              </div>
            </div>
            <div className="ml-auto grid grid-cols-2 gap-3">
              <div>
                <Label className="text-xs">Stop-loss</Label>
                <Input
                  type="number" step="0.01" value={draftSl ?? ""} disabled={readOnly}
                  onChange={(e) => setDraftSl(Number(e.target.value))} className="h-9 w-32 font-mono"
                />
              </div>
              <div>
                <Label className="text-xs">Take-profit</Label>
                <Input
                  type="number" step="0.01" value={draftTp ?? ""} disabled={readOnly}
                  onChange={(e) => setDraftTp(Number(e.target.value))} className="h-9 w-32 font-mono"
                />
              </div>
            </div>
            <Button
              disabled={readOnly || !validation.ok || (draftSl === position.stop_loss && draftTp === position.take_profit)}
              onClick={() => setConfirmOpen(true)}
            >
              Apply changes
            </Button>
          </div>
          {!validation.ok && draftSl != null && draftTp != null && (
            <div className="mt-2 text-xs text-destructive">{validation.msg}</div>
          )}
          {readOnly && (
            <div className="mt-2 text-xs text-amber-400">Read-only: impersonation session cannot modify SL/TP.</div>
          )}
        </Card>
      ) : (
        <Card className="glass-strong p-4 text-xs text-muted-foreground">
          No open position on {symbol} — approve a signal from the Dashboard to see SL/TP handles here.
        </Card>
      )}

      <Dialog open={confirmOpen} onOpenChange={setConfirmOpen}>
        <DialogContent className="glass-strong">
          <DialogHeader><DialogTitle>Confirm SL/TP change</DialogTitle></DialogHeader>
          {position && (
            <div className="grid gap-2 text-sm">
              <div><span className="text-muted-foreground">Position:</span> {position.position_side} {symbol} @ {position.entry_price.toFixed(2)}</div>
              <div className="grid grid-cols-2 gap-4 rounded-md border border-border/60 p-3">
                <div>
                  <div className="text-[11px] uppercase text-muted-foreground">Before</div>
                  <div className="font-mono">SL {position.stop_loss.toFixed(2)}</div>
                  <div className="font-mono">TP {position.take_profit.toFixed(2)}</div>
                </div>
                <div>
                  <div className="text-[11px] uppercase text-primary">After</div>
                  <div className="font-mono">SL {(draftSl ?? 0).toFixed(2)}</div>
                  <div className="font-mono">TP {(draftTp ?? 0).toFixed(2)}</div>
                </div>
              </div>
              {!validation.ok && <div className="text-xs text-destructive">{validation.msg}</div>}
            </div>
          )}
          <DialogFooter>
            <Button variant="ghost" onClick={() => setConfirmOpen(false)}>Cancel</Button>
            <Button onClick={applyChanges} disabled={!validation.ok || applying}>
              {applying ? <Loader2 className="mr-2 h-4 w-4 animate-spin" /> : null}
              Confirm & apply
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </div>
  );
}
