/* chart.jsx — Candlestick spot chart + order flow histogram, time-synced */

const { useEffect: chUseEffect, useMemo: chUseMemo, useRef: chUseRef, useState: chUseState } = React;

// ── Build initial candle series, tuned per timeframe ────────────────────────
const TF_CONFIG = {
  '1m':  { N: 50, rangePct: 0.0011, volPct: 0.00022, wickPct: 0.00008 },
  '5m':  { N: 50, rangePct: 0.0023, volPct: 0.00046, wickPct: 0.00016 },
  '15m': { N: 50, rangePct: 0.0062, volPct: 0.00087, wickPct: 0.00028 },
  '1h':  { N: 50, rangePct: 0.0142, volPct: 0.00175, wickPct: 0.00055 },
};

function buildCandles(tf = '1m', spot = 103284.5) {
  const cfg = TF_CONFIG[tf] || TF_CONFIG['1m'];
  const end = spot;
  const start = spot * (1 - cfg.rangePct);
  const vol = spot * cfg.volPct;
  const wick = spot * cfg.wickPct;
  const candles = [];
  let p = start;
  for (let i = 0; i < cfg.N; i++) {
    const target = start + (end - start) * (i / (cfg.N - 1));
    const meanReversion = (target - p) * 0.18;
    const o = p;
    const move = meanReversion + (Math.random() - 0.5) * vol;
    const c = o + move;
    const hi = Math.max(o, c) + wick * (0.4 + Math.random() * 1.2);
    const lo = Math.min(o, c) - wick * (0.4 + Math.random() * 1.2);
    candles.push({ o, h: hi, l: lo, c });
    p = c;
  }
  const last = candles[candles.length - 1];
  last.c = end;
  last.h = Math.max(last.h, last.c + wick * 0.5);
  last.l = Math.min(last.l, last.c - wick * 1.2);
  return candles;
}

// ── Candlestick chart ──────────────────────────────────────────────────────
function CandleChart({ candles, strike, width, height, padLeft, padRight, padTop, padBottom }) {
  const N = candles.length;
  const allLo = Math.min(...candles.map(c => c.l), strike);
  const allHi = Math.max(...candles.map(c => c.h), strike);
  // Range floor must scale with the price magnitude — a hardcoded "60" works for BTC at ~103k
  // but blows up the chart for low-priced coins like DOGE (0.128) or LIT (0.87).
  const rawRange = allHi - allLo;
  const relFloor = Math.max(allHi, 1e-6) * 0.0006;
  const range = Math.max(relFloor, rawRange);
  const yMin = allLo - range * 0.15;
  const yMax = allHi + range * 0.15;

  const plotW = width - padLeft - padRight;
  const slot = plotW / N;
  const bodyW = Math.max(2, slot * 0.55);

  const ix = i => padLeft + slot * i + slot / 2;
  const iy = p => padTop + (1 - (p - yMin) / (yMax - yMin)) * (height - padTop - padBottom);

  // Y ticks — step adapts to the absolute range
  const ticks = chUseMemo(() => {
    const range = yMax - yMin;
    // Find a nice round step that yields ~5–7 ticks
    const rawStep = range / 6;
    const magnitude = Math.pow(10, Math.floor(Math.log10(Math.max(rawStep, 1e-6))));
    const candidates = [1, 2, 2.5, 5, 10].map(m => m * magnitude);
    const step = candidates.find(s => range / s <= 7) || candidates[candidates.length - 1];
    const t = [];
    const lo = Math.ceil(yMin / step) * step;
    for (let v = lo; v <= yMax; v += step) t.push(v);
    return t;
  }, [yMin, yMax]);

  const last = candles[candles.length - 1];
  const lastClose = last.c;
  const lastUp = last.c >= last.o;
  const lastY = iy(lastClose);
  const strikeY = iy(strike);

  // Pill geometry — right gutter, comfortably inset from the chart edge
  const pillW = 84;
  const pillX = width - padRight + (padRight - pillW) / 2;
  const labelPad = 10;

  // Hide axis tick labels that would collide with the strike/last-price pills
  const visibleTicks = ticks.filter(v => {
    const y = iy(v);
    return Math.abs(y - strikeY) > 14 && Math.abs(y - lastY) > 14;
  });

  return (
    <svg width={width} height={height} className="block" style={{ overflow: 'visible' }}>
      {/* Y grid */}
      {ticks.map(v => (
        <line key={v}
          x1={padLeft} x2={width - padRight}
          y1={iy(v)} y2={iy(v)}
          stroke="#141414" strokeWidth="1" shapeRendering="crispEdges" />
      ))}
      {/* X grid markers every 5 candles */}
      {candles.map((_, i) => (i % 5 === 0 ? (
        <line key={`vx${i}`}
          x1={ix(i)} x2={ix(i)}
          y1={padTop} y2={height - padBottom}
          stroke="#141414" strokeWidth="1" shapeRendering="crispEdges" />
      ) : null))}

      {/* Strike line — amber dashed */}
      <line
        x1={padLeft} x2={width - padRight}
        y1={strikeY} y2={strikeY}
        stroke="#E8A33D" strokeWidth="1" strokeDasharray="4 3" shapeRendering="crispEdges"
      />
      <g>
        <rect x={pillX} y={strikeY - 11} width={pillW} height={22} rx="3" fill="#1F1610" stroke="#E8A33D" strokeWidth="1" />
        <text x={pillX + pillW / 2} y={strikeY + 4} textAnchor="middle" fill="#E8A33D" fontFamily="JetBrains Mono, ui-monospace, monospace" fontWeight="700" fontSize="12">{strike.toLocaleString(undefined, { maximumFractionDigits: strike >= 1000 ? 0 : strike >= 10 ? 2 : 4 })}</text>
      </g>

      {/* Candles */}
      {candles.map((c, i) => {
        const up = c.c >= c.o;
        const color = up ? '#2EBD85' : '#EC4F65';
        const bodyTop = iy(Math.max(c.o, c.c));
        const bodyBot = iy(Math.min(c.o, c.c));
        const bodyH = Math.max(1.5, bodyBot - bodyTop);
        const cx = ix(i);
        return (
          <g key={i}>
            {/* wick */}
            <line x1={cx} x2={cx} y1={iy(c.h)} y2={iy(c.l)} stroke={color} strokeWidth="1.2" shapeRendering="crispEdges" />
            {/* body */}
            <rect
              x={cx - bodyW / 2}
              y={bodyTop}
              width={bodyW}
              height={bodyH}
              fill={color}
              opacity={up ? 1 : 1}
              shapeRendering="crispEdges"
            />
          </g>
        );
      })}

      {/* Y-axis labels — on the LEFT, right-aligned, so they never collide with right-side pills */}
      {visibleTicks.map(v => (
        <text key={`l${v}`}
          x={padLeft - labelPad} y={iy(v) + 3.5}
          textAnchor="end"
          fontFamily="JetBrains Mono, ui-monospace, monospace" fontSize="10" fill="#777">
          {v.toLocaleString(undefined, { maximumFractionDigits: v >= 1000 ? 0 : v >= 10 ? 2 : 4 })}
        </text>
      ))}

      {/* Last-price marker */}
      <line x1={padLeft} x2={width - padRight} y1={lastY} y2={lastY} stroke={lastUp ? '#2EBD85' : '#EC4F65'} strokeWidth="1" strokeDasharray="2 3" />
      <rect x={pillX} y={lastY - 11} width={pillW} height={22} rx="3" fill={lastUp ? '#2EBD85' : '#EC4F65'} />
      <text x={pillX + pillW / 2} y={lastY + 4} textAnchor="middle" fill={lastUp ? '#000' : '#fff'} fontFamily="JetBrains Mono, ui-monospace, monospace" fontWeight="700" fontSize="12">
        {lastClose.toLocaleString(undefined,{minimumFractionDigits: lastClose >= 1000 ? 1 : lastClose >= 10 ? 2 : 4, maximumFractionDigits: lastClose >= 1000 ? 1 : lastClose >= 10 ? 2 : 4})}
      </text>
    </svg>
  );
}

// ── Lightweight Charts wrapper ──────────────────────────────────────────────
//    Same input shape as <CandleChart> (candles: {o,h,l,c}[], strike), so it
//    drops straight into ChartStack. Adds synthetic timestamps anchored to now
//    so the time axis is meaningful, and renders a dashed amber strike line
//    with a right-edge price label via createPriceLine().
const TF_SECONDS = { '1m': 60, '5m': 300, '15m': 900, '1h': 3600 };

function LightweightCandleChart({ candles, strike, width, height, timeframe, spot }) {
  const containerRef = chUseRef(null);
  const chartRef = chUseRef(null);
  const seriesRef = chUseRef(null);
  const strikeLineRef = chUseRef(null);

  // Create chart once
  chUseEffect(() => {
    if (!containerRef.current || !window.LightweightCharts) return;
    const LC = window.LightweightCharts;
    const chart = LC.createChart(containerRef.current, {
      width, height,
      layout: {
        background: { type: 'solid', color: '#000000' },
        textColor: '#888888',
        fontFamily: 'JetBrains Mono, ui-monospace, monospace',
        fontSize: 10,
      },
      grid: {
        vertLines: { color: '#141414' },
        horzLines: { color: '#141414' },
      },
      rightPriceScale: {
        borderColor: '#1C1C1C',
        scaleMargins: { top: 0.12, bottom: 0.12 },
      },
      timeScale: {
        borderColor: '#1C1C1C',
        timeVisible: true,
        secondsVisible: false,
        rightOffset: 4,
      },
      crosshair: {
        mode: LC.CrosshairMode.Normal,
        vertLine: { color: '#2A2A2A', width: 1, style: 0, labelBackgroundColor: '#1F252F' },
        horzLine: { color: '#2A2A2A', width: 1, style: 0, labelBackgroundColor: '#1F252F' },
      },
      handleScale: false,
      handleScroll: false,
    });
    const series = chart.addCandlestickSeries({
      upColor: '#2EBD85',
      downColor: '#EC4F65',
      borderUpColor: '#2EBD85',
      borderDownColor: '#EC4F65',
      wickUpColor: '#2EBD85',
      wickDownColor: '#EC4F65',
      priceFormat: {
        type: 'price',
        precision: spot >= 1000 ? 1 : spot >= 10 ? 2 : 4,
        minMove: spot >= 1000 ? 0.1 : spot >= 10 ? 0.01 : 0.0001,
      },
    });
    chartRef.current = chart;
    seriesRef.current = series;
    return () => { chart.remove(); chartRef.current = null; seriesRef.current = null; strikeLineRef.current = null; };
  }, []);

  // Resize
  chUseEffect(() => {
    if (chartRef.current) chartRef.current.applyOptions({ width, height });
  }, [width, height]);

  // Update price format when scale changes (BTC ↔ DOGE)
  chUseEffect(() => {
    if (!seriesRef.current) return;
    seriesRef.current.applyOptions({
      priceFormat: {
        type: 'price',
        precision: spot >= 1000 ? 1 : spot >= 10 ? 2 : 4,
        minMove: spot >= 1000 ? 0.1 : spot >= 10 ? 0.01 : 0.0001,
      },
    });
  }, [spot]);

  // Push candle data
  chUseEffect(() => {
    if (!seriesRef.current || !chartRef.current) return;
    const tfSec = TF_SECONDS[timeframe] || 60;
    const nowSec = Math.floor(Date.now() / 1000);
    // Round anchor to tf boundary so candle times look clean on the axis
    const anchor = Math.floor(nowSec / tfSec) * tfSec;
    const data = candles.map((c, i) => ({
      time: anchor - (candles.length - 1 - i) * tfSec,
      open: c.o, high: c.h, low: c.l, close: c.c,
    }));
    seriesRef.current.setData(data);
    // Span the full chart width — without this, default bar spacing leaves a
    // huge empty area on the right with only ~50 candles.
    chartRef.current.timeScale().fitContent();
  }, [candles, timeframe]);

  // Also fit on resize so it keeps filling the width as the viewport changes
  chUseEffect(() => {
    if (chartRef.current) chartRef.current.timeScale().fitContent();
  }, [width]);

  // Strike line — recreate when value changes
  chUseEffect(() => {
    if (!seriesRef.current || strike == null) return;
    if (strikeLineRef.current) {
      try { seriesRef.current.removePriceLine(strikeLineRef.current); } catch {}
    }
    strikeLineRef.current = seriesRef.current.createPriceLine({
      price: strike,
      color: '#E8A33D',
      lineWidth: 1,
      lineStyle: 2, // dashed
      axisLabelVisible: true,
      title: 'STRIKE',
    });
  }, [strike]);

  return <div ref={containerRef} style={{ width, height }} />;
}

// ── Popping P&L chips overlay — chips spawn at the same anchor point near
//    the bottom-left of the chart and rise up + fade out, one after another.
function PopChipsOverlay() {
  const [chips, setChips] = chUseState([]);
  const nextId = chUseRef(0);
  chUseEffect(() => {
    let cancelled = false;
    const spawn = () => {
      if (cancelled) return;
      const isWin = Math.random() < 0.62;
      const amt = Math.floor(1 + Math.random() * 22);
      const id = nextId.current++;
      setChips(prev => [...prev, { id, isWin, amt }].slice(-8));
      setTimeout(() => {
        if (cancelled) return;
        setChips(prev => prev.filter(c => c.id !== id));
      }, 2200);
      // Steady rhythm so chips queue up nicely one after another
      setTimeout(spawn, 320 + Math.random() * 240);
    };
    setTimeout(spawn, 400);
    return () => { cancelled = true; };
  }, []);

  return (
    <div className="absolute pointer-events-none z-10" style={{ left: 12, bottom: 32, width: 70 }}>
      {chips.map(c => (
        <div
          key={c.id}
          style={{
            position: 'absolute',
            left: 0,
            bottom: 0,
            animation: 'pop-pnl 2.2s cubic-bezier(0.22, 0.61, 0.36, 1) forwards',
          }}
          className={`font-mono text-[12.5px] font-semibold ${c.isWin ? 'text-green' : 'text-red'}`}
        >
          {c.isWin ? '+' : '−'}${c.amt}
        </div>
      ))}
    </div>
  );
}

// ── OrderFlowChart (unchanged shape) ────────────────────────────────────────
function OrderFlowChart({ data, width, height, padLeft, padRight, padTop, padBottom }) {
  const N = data.length;
  const maxY = Math.max(...data.map(d => Math.max(d.yes, d.no))) * 1.05;
  const plotH = height - padTop - padBottom;
  const zeroY = padTop + plotH / 2;
  const barAreaW = width - padLeft - padRight;
  const slot = barAreaW / N;
  const barW = Math.max(2, slot * 0.62);
  const scale = (v) => (v / maxY) * (plotH / 2);

  return (
    <svg width={width} height={height} className="block" style={{ overflow: 'visible' }}>
      <line x1={padLeft} x2={width - padRight} y1={zeroY} y2={zeroY} stroke="#1C1C1C" strokeWidth="1" shapeRendering="crispEdges" />
      {data.map((_, i) => (i % 5 === 0 ? (
        <line key={`ox${i}`}
          x1={padLeft + slot * i + slot/2} x2={padLeft + slot * i + slot/2}
          y1={padTop} y2={height - padBottom}
          stroke="#141414" strokeWidth="1" shapeRendering="crispEdges" />
      ) : null))}
      {data.map((d, i) => {
        const cx = padLeft + slot * i + slot / 2;
        const hY = scale(d.yes);
        const hN = scale(d.no);
        return (
          <g key={i}>
            <rect x={cx - barW/2} y={zeroY - hY} width={barW} height={hY} fill="#2EBD85" opacity="0.9" shapeRendering="crispEdges" />
            <rect x={cx - barW/2} y={zeroY}      width={barW} height={hN} fill="#EC4F65" opacity="0.85" shapeRendering="crispEdges" />
          </g>
        );
      })}
      <text x={width - padRight + 4} y={zeroY + 3} fontFamily="JetBrains Mono" fontSize="9.5" fill="#555">0</text>
      <text x={width - padRight + 4} y={padTop + 8} fontFamily="JetBrains Mono" fontSize="9.5" fill="#2EBD85">YES</text>
      <text x={width - padRight + 4} y={height - padBottom - 2} fontFamily="JetBrains Mono" fontSize="9.5" fill="#EC4F65">NO</text>
    </svg>
  );
}

// ── Combined ChartStack (the centerpiece) ───────────────────────────────────
function ChartStack({ width, height, market, timeframe = '1m' }) {
  const spot = market && market.spot ? market.spot : 103284.5;
  const [candles, setCandles] = chUseState(() => buildCandles(timeframe, spot));
  const [flow, setFlow] = chUseState(() => window.TR.buildFlow(timeframe));

  // Two cadences:
  //  · fast tick (~280ms): update last candle's h/l/c only — feels alive, no shifting
  //  · slow tick (every ~10 fast ticks): close current candle, start fresh + append flow bucket
  const tickCount = chUseRef(0);
  chUseEffect(() => {
    const id = setInterval(() => {
      tickCount.current += 1;
      const slow = tickCount.current % 10 === 0;
      setCandles(prev => {
        const next = prev.map(c => ({ ...c }));
        const last = next[next.length - 1];
        const driftScale = spot * 0.00008;
        const drift = (Math.random() - 0.48) * driftScale * 2;
        const newC = Math.max(spot * 0.985, Math.min(spot * 1.015, last.c + drift));
        last.c = Math.round(newC * 10000) / 10000;
        last.h = Math.max(last.h, last.c);
        last.l = Math.min(last.l, last.c);
        if (slow) {
          const o = last.c;
          const c = o + (Math.random() - 0.5) * spot * 0.00045;
          next.shift();
          next.push({ o, h: Math.max(o, c) + spot * 0.00012, l: Math.min(o, c) - spot * 0.00012, c });
        }
        return next;
      });
      if (slow) {
        setFlow(prev => {
          const yes = Math.round(12000 + Math.random() * 22000);
          const no  = Math.round(8000  + Math.random() * 14000);
          return [...prev.slice(1), { t: prev[prev.length - 1].t + 1, yes, no }];
        });
      }
    }, 280);
    return () => clearInterval(id);
  }, [spot]);

  const topH = Math.floor(height * 0.74);
  const flowH = height - topH - 26;

  const padLeft = 70, padRight = 102, padTop = 12, padBottomSpot = 18, padBottomFlow = 14;

  const last5 = flow.slice(-5);
  const yesSum = last5.reduce((a, d) => a + d.yes, 0);
  const noSum  = last5.reduce((a, d) => a + d.no, 0);
  const net = yesSum - noSum;
  const netSide = net >= 0 ? 'YES' : 'NO';

  const tfLabels = {
    '1m':  ['-50m','-37m','-25m','-12m','now'],
    '5m':  ['-4h','-3h','-2h','-1h','now'],
    '15m': ['-12h','-9h','-6h','-3h','now'],
    '1h':  ['-2d','-36h','-24h','-12h','now'],
  };
  const tLabels = tfLabels[timeframe] || tfLabels['1m'];

  const lastCandle = candles[candles.length - 1];
  const candleChange = lastCandle.c - candles[0].o;
  const candleChangePct = (candleChange / candles[0].o) * 100;

  return (
    <div className="flex flex-col h-full">
      {/* Top chart pane */}
      <div className="relative" style={{ height: topH }}>
        <div className="absolute top-2 left-3 z-10 flex flex-col gap-0.5">
          <div className="flex items-center gap-3">
            <span className="font-mono text-[10px] tracking-[0.18em] uppercase text-text-2">{market.asset || 'BTC-USD'}</span>
            <span className="font-mono text-[10px] text-text-3">{timeframe}</span>
            <span className="flex items-center gap-1.5">
              <span className="w-1.5 h-1.5 rounded-full bg-green live-dot" />
              <span className="font-mono text-[10px] text-text-2">live</span>
            </span>
          </div>
          {/* OHLC — Lighter-style: current candle values, bigger */}
          <div className="flex items-center gap-2 font-mono text-[11.5px]">
            <span className="text-text-3">O</span><span className="text-text-2">{lastCandle.o.toFixed(spot >= 1000 ? 1 : spot >= 10 ? 2 : 4)}</span>
            <span className="text-text-3 ml-1">H</span><span className="text-green">{lastCandle.h.toFixed(spot >= 1000 ? 1 : spot >= 10 ? 2 : 4)}</span>
            <span className="text-text-3 ml-1">L</span><span className="text-red">{lastCandle.l.toFixed(spot >= 1000 ? 1 : spot >= 10 ? 2 : 4)}</span>
            <span className="text-text-3 ml-1">C</span>
            <span className={`font-semibold text-[14px] ${lastCandle.c >= lastCandle.o ? 'text-green' : 'text-red'}`}>{lastCandle.c.toFixed(spot >= 1000 ? 1 : spot >= 10 ? 2 : 4)}</span>
            <span className={`font-mono text-[10.5px] ml-1 ${candleChange >= 0 ? 'text-green' : 'text-red'}`}>
              {candleChange >= 0 ? '+' : ''}{candleChange.toFixed(spot >= 1000 ? 1 : 2)} · {candleChangePct.toFixed(2)}%
            </span>
          </div>
        </div>

        {/* (Target pill removed \u2014 the amber pill + dashed line on the price axis already mark the strike at its correct price level.) */}

        <LightweightCandleChart
          candles={candles}
          strike={market.strike}
          width={width}
          height={topH}
          timeframe={timeframe}
          spot={spot}
        />
        <PopChipsOverlay />
      </div>

      {/* Order-flow header */}
      <div className="h-[26px] hl-b hl-t px-3 flex items-center justify-between">
        <div className="flex items-center gap-4">
          <span className="font-mono text-[10px] tracking-[0.18em] uppercase text-text-2">ORDER FLOW</span>
          <span className="font-mono text-[10px] text-text-3">TAKER VOLUME · {timeframe.toUpperCase()}</span>
        </div>
        <div className="flex items-center gap-4">
          <span className="font-mono text-[10px] text-text-3">YES</span>
          <span className="font-mono text-[11px] text-green">{window.TR.fmtUsd(yesSum, 0)}</span>
          <span className="font-mono text-[10px] text-text-3">NO</span>
          <span className="font-mono text-[11px] text-red">{window.TR.fmtUsd(noSum, 0)}</span>
          <span className="font-mono text-[10px] text-text-3">·</span>
          <span className={`font-mono text-[11px] ${net >= 0 ? 'text-green' : 'text-red'}`}>
            NET {net >= 0 ? '+' : '−'}{window.TR.fmtUsd(Math.abs(net), 0)} · {netSide} buying pressure
          </span>
        </div>
      </div>

      {/* Bottom chart pane */}
      <div className="relative" style={{ height: flowH }}>
        <OrderFlowChart
          data={flow}
          width={width}
          height={flowH}
          padLeft={padLeft} padRight={padRight}
          padTop={padTop} padBottom={padBottomFlow}
        />
        <div className="absolute left-0 right-0 bottom-1 pointer-events-none flex justify-between font-mono text-[9.5px] text-text-3 px-3" style={{ paddingRight: padRight + 4, paddingLeft: padLeft + 4 }}>
          {tLabels.map(l => <span key={l}>{l}</span>)}
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { CandleChart, LightweightCandleChart, OrderFlowChart, ChartStack });
