// signal.jsx — 進出場訊號面板
// smc-analyst · E 量化交易部 / indicator-specialist · B 前端工坊
// 參考 repo:
//   joshyattridge/smart-money-concepts → OB / BOS / FVG / Swing
//   twopirllc/pandas-ta               → EMA 排列、RSI 分區、MACD 策略

// ── Swing High / Low 偵測（SMC 核心）────────────────────────
function detectSwings(data, lb = 5) {
  const highs = [], lows = [];
  for (let i = lb; i < data.length - lb; i++) {
    const isH = data.slice(i - lb, i).every(d => d.high <= data[i].high) &&
                data.slice(i + 1, i + lb + 1).every(d => d.high <= data[i].high);
    const isL = data.slice(i - lb, i).every(d => d.low  >= data[i].low) &&
                data.slice(i + 1, i + lb + 1).every(d => d.low  >= data[i].low);
    if (isH) highs.push({ i, price: +data[i].high.toFixed(2), date: data[i].fullDate || data[i].date || '' });
    if (isL) lows.push({  i, price: +data[i].low.toFixed(2),  date: data[i].fullDate || data[i].date || '' });
  }
  return { highs, lows };
}

// ── FVG（Fair Value Gap）偵測 ──────────────────────────────
// 多頭 FVG：bar[i-2].high < bar[i].low → 價格可能回補此缺口（做多進場區）
// 空頭 FVG：bar[i-2].low  > bar[i].high → 缺口上方為壓力
function detectFVG(data) {
  const bull = [], bear = [];
  for (let i = 2; i < data.length; i++) {
    if (data[i - 2].high < data[i].low) {
      bull.push({ top: +data[i].low.toFixed(2), bottom: +data[i - 2].high.toFixed(2), i });
    }
    if (data[i - 2].low > data[i].high) {
      bear.push({ top: +data[i - 2].low.toFixed(2), bottom: +data[i].high.toFixed(2), i });
    }
  }
  // 只保留未被填補的近期 FVG
  const unfilled = (list, isBull) => list.slice(-10).filter(fvg => {
    const after = data.slice(fvg.i + 1);
    return isBull
      ? !after.some(d => d.low < fvg.bottom)   // 多頭 FVG 底部未被跌破
      : !after.some(d => d.high > fvg.top);     // 空頭 FVG 頂部未被突破
  });
  return { bull: unfilled(bull, true).slice(-3), bear: unfilled(bear, false).slice(-3) };
}

// ── Bullish Order Block（OB）偵測 ──────────────────────────
// 多頭 OB：強勢上升波段前的最後一根下跌蠟燭（機構建倉位置）
function detectBullOB(data, swingHighs) {
  const obs = [];
  for (let i = 3; i < data.length - 3; i++) {
    if (data[i].close >= data[i].open) continue; // 非下跌棒
    const prevHigh = swingHighs.filter(s => s.i < i).slice(-1)[0];
    if (!prevHigh) continue;
    // 後續 5 根中有棒收盤突破前高 → BOS 確認，此棒為 OB
    const bos = data.slice(i + 1, i + 6).some(d => d.close > prevHigh.price);
    if (bos) obs.push({
      top:    +data[i].open.toFixed(2),
      bottom: +data[i].close.toFixed(2),
      i, date: data[i].fullDate || data[i].date || '',
    });
  }
  // 只保留現價以下、未被跌破的 OB
  return obs.filter(ob => {
    const after = data.slice(ob.i + 1);
    return !after.some(d => d.close < ob.bottom);
  }).slice(-3);
}

// ── EMA 計算 ──────────────────────────────────────────────
function emaArr(arr, n) {
  const k = 2 / (n + 1);
  let prev = arr[0];
  return arr.map((v, i) => { if (i === 0) return prev; prev = v * k + prev * (1 - k); return prev; });
}

// ── 訊號計算核心 ──────────────────────────────────────────
function computeSignal(data, macdObj, rsiArr, bbObj, atrArr, pivotObj) {
  if (!data || data.length < 60) return null;

  const closes = data.map(d => d.close);
  const e20  = emaArr(closes, 20);
  const e50  = emaArr(closes, 50);
  const e200 = emaArr(closes, 200);
  const n    = data.length - 1;

  const cur    = data[n].close;
  const atr    = atrArr[n] || (cur * 0.02);
  const rsiNow = rsiArr[n];
  const histNow  = macdObj.hist[n];
  const histPrev = macdObj.hist[n - 1];

  // Swing / OB / FVG
  const swings = detectSwings(data);
  const fvg    = detectFVG(data);
  const bullOB = detectBullOB(data, swings.highs);

  // 最近 swing high / low（做出場目標和停損參考）
  const lastSwingHigh = swings.highs.slice(-1)[0];
  const lastSwingLow  = swings.lows.slice(-1)[0];

  // ── 多頭條件評分（6 條）────────────────────────────────
  const conds = [
    {
      key: 'ema',
      label: 'EMA 多頭排列',
      en: 'EMA Bull Alignment',
      ok: e20[n] > e50[n] && e50[n] > e200[n],
      warn: e20[n] > e50[n] && e50[n] <= e200[n],
      detail: `EMA20 ${e20[n].toFixed(0)} · EMA50 ${e50[n].toFixed(0)} · EMA200 ${e200[n].toFixed(0)}`,
    },
    {
      key: 'macd',
      label: 'MACD 動能向上',
      en: 'MACD Momentum',
      ok: histNow > 0 && histNow > histPrev,
      warn: histNow > 0 && histNow <= histPrev,
      detail: `直方圖 ${histNow?.toFixed(2)} ${histNow > histPrev ? '↑擴大' : '↓收縮'}`,
    },
    {
      key: 'rsi',
      label: 'RSI 適合進場',
      en: 'RSI Entry Zone',
      ok: rsiNow >= 40 && rsiNow <= 65,
      warn: rsiNow > 65 && rsiNow < 75,
      detail: `RSI ${rsiNow?.toFixed(1)} · ${rsiNow < 30 ? '超賣反彈區' : rsiNow > 75 ? '超買謹慎' : rsiNow >= 40 && rsiNow <= 65 ? '最佳進場區間' : '可接受'}`,
    },
    {
      key: 'bb',
      label: '布林回調帶附近',
      en: 'Bollinger Pullback',
      ok: cur < bbObj.mid[n] && cur > bbObj.lower[n],
      warn: cur >= bbObj.mid[n] && cur < bbObj.upper[n],
      detail: `現價 ${cur} · 中軌 ${bbObj.mid[n]?.toFixed(0)} · 下軌 ${bbObj.lower[n]?.toFixed(0)}`,
    },
    {
      key: 'pivot',
      label: '支撐位以上',
      en: 'Above Pivot Support',
      ok: cur > pivotObj.support,
      warn: cur > pivotObj.pivot * 0.98 && cur <= pivotObj.support,
      detail: `Pivot ${pivotObj.pivot} · 支撐 ${pivotObj.support} · 壓力 ${pivotObj.resistance}`,
    },
    {
      key: 'ob',
      label: 'Order Block / FVG 支撐',
      en: 'SMC Demand Zone',
      ok: bullOB.length > 0 && cur >= bullOB[bullOB.length - 1].bottom,
      warn: fvg.bull.length > 0,
      detail: bullOB.length > 0
        ? `最近 OB：${bullOB[bullOB.length - 1].bottom}–${bullOB[bullOB.length - 1].top}`
        : fvg.bull.length > 0
          ? `未填 FVG：${fvg.bull[fvg.bull.length - 1].bottom}–${fvg.bull[fvg.bull.length - 1].top}`
          : '無明顯 OB / FVG',
    },
  ];

  const score    = conds.filter(c => c.ok).length;
  const warnCnt  = conds.filter(c => !c.ok && c.warn).length;
  const signal   = score >= 4 ? 'LONG' : score <= 1 ? 'AVOID' : 'WAIT';

  // ── 進場 / 出場 / 停損 ────────────────────────────────
  // 進場區：最近 OB 頂部 or EMA20
  const entryRef = bullOB.length > 0
    ? { low: bullOB[bullOB.length - 1].bottom, high: bullOB[bullOB.length - 1].top, src: 'Order Block' }
    : { low: +(e20[n] * 0.99).toFixed(0), high: +(e20[n] * 1.01).toFixed(0), src: 'EMA20 回測' };

  // 進場價（單一數字）：OB 頂部 or EMA20 × 1.005 取整
  const entryPrice = bullOB.length > 0
    ? bullOB[bullOB.length - 1].top
    : Math.round(e20[n] * 1.005);

  // 出場目標 T1 = 最近 swing high，T2 = Pivot 壓力
  const t1 = lastSwingHigh ? lastSwingHigh.price : +pivotObj.resistance.toFixed(0);
  const t2 = +(Math.max(pivotObj.resistance, t1) * 1.02).toFixed(0);

  // 停損：最近 swing low or OB 底部 - ATR
  const sl = lastSwingLow
    ? +(lastSwingLow.price - atr * 0.3).toFixed(0)
    : +(e50[n] - atr).toFixed(0);

  // R:R
  const entryMid = (entryRef.low + entryRef.high) / 2;
  const rr = sl < entryMid && t1 > entryMid
    ? +((t1 - entryMid) / (entryMid - sl)).toFixed(1)
    : null;

  return {
    signal, score, warnCnt, conds,
    cur, atr,
    entryRef, entryPrice, t1, t2, sl, rr,
    e20: +e20[n].toFixed(0), e50: +e50[n].toFixed(0), e200: +e200[n].toFixed(0),
    bullOB, bearFVG: fvg.bear, bullFVG: fvg.bull,
    lastSwingHigh, lastSwingLow,
  };
}

// ── SignalPanel UI ────────────────────────────────────────
function SignalPanel({ data, macdObj, rsiArr, bbObj, atrArr, pivotObj, rtClose }) {
  const sig = React.useMemo(
    () => computeSignal(data, macdObj, rsiArr, bbObj, atrArr, pivotObj),
    [data]
  );

  if (!sig) return (
    <div className="fund-empty" style={{ padding: 40 }}>資料不足，需至少 60 根 K 線</div>
  );

  const cur = rtClose || sig.cur;

  const sigStyle = {
    LONG:  { bg: 'rgba(76,175,80,0.07)', border: 'var(--bull)', text: 'var(--bull)', label: '多頭進場' },
    WAIT:  { bg: 'rgba(184,146,77,0.09)', border: 'var(--gold)', text: 'var(--gold-deep)', label: '觀望等待' },
    AVOID: { bg: 'rgba(239,83,80,0.07)', border: 'var(--bear)', text: 'var(--bear)', label: '迴避風險' },
  }[sig.signal];

  const condIcon = (c) => c.ok ? '✅' : c.warn ? '⚠️' : '❌';

  return (
    <div style={{ padding: '20px 0', maxWidth: 900 }}>

      {/* ── 主訊號卡 ────────────────────────────────── */}
      <div style={{
        display: 'flex', gap: 16, alignItems: 'stretch',
        background: sigStyle.bg, border: `1.5px solid ${sigStyle.border}`,
        borderRadius: 4, padding: '16px 20px', marginBottom: 20,
      }}>
        {/* 訊號燈 */}
        <div style={{ textAlign: 'center', minWidth: 110 }}>
          <div style={{ fontFamily: 'Times New Roman,serif', fontStyle: 'italic', fontSize: 13, color: 'var(--ink-mute)', marginBottom: 6 }}>
            Signal
          </div>
          <div style={{ fontSize: 42, fontWeight: 800, color: sigStyle.text, letterSpacing: '.06em', lineHeight: 1 }}>
            {sig.signal}
          </div>
          <div style={{ fontFamily: 'BiauKai,標楷體,serif', fontSize: 20, color: sigStyle.text, marginTop: 6 }}>
            {sigStyle.label}
          </div>
        </div>

        {/* 強度條 */}
        <div style={{ flex: 1, display: 'flex', flexDirection: 'column', justifyContent: 'center', gap: 8 }}>
          <div style={{ fontFamily: 'Times New Roman,serif', fontStyle: 'italic', fontSize: 13, color: 'var(--ink-mute)' }}>
            Condition Score
          </div>
          <div style={{ display: 'flex', gap: 5 }}>
            {[0,1,2,3,4,5].map(i => (
              <div key={i} style={{
                flex: 1, height: 10, borderRadius: 2,
                background: i < sig.score ? sigStyle.border : 'rgba(0,0,0,0.08)',
                transition: 'background .3s',
              }}/>
            ))}
          </div>
          <div style={{ fontSize: 14, color: 'var(--ink-mute)', fontFamily: 'BiauKai,標楷體,serif' }}>
            {sig.score} / 6 條件符合{sig.warnCnt > 0 ? ` · ${sig.warnCnt} 條件警示` : ''}
          </div>
        </div>

        {/* 現價 */}
        <div style={{ textAlign: 'right', minWidth: 90 }}>
          <div style={{ fontFamily: 'Times New Roman,serif', fontStyle: 'italic', fontSize: 13, color: 'var(--ink-mute)' }}>現價</div>
          <div style={{ fontSize: 32, fontWeight: 700, color: 'var(--ink)', fontFamily: 'Times New Roman,serif' }}>
            {cur.toLocaleString()}
          </div>
        </div>
      </div>

      {/* ── 進場 / 目標 / 停損 三欄 ──────────────────── */}
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 12, marginBottom: 20 }}>

        {/* 進場區 */}
        <div style={{
          padding: '18px 20px', border: '1px solid var(--paper-line)',
          background: 'var(--paper)', borderLeft: '4px solid var(--gold)',
        }}>
          <div style={{ fontFamily: 'Times New Roman,serif', fontStyle: 'italic', fontSize: 13, color: 'var(--ink-mute)', marginBottom: 8 }}>
            Entry Price · 進場價
          </div>
          <div style={{ fontSize: 34, fontWeight: 700, fontFamily: 'Times New Roman,serif', color: 'var(--gold-deep)', lineHeight: 1 }}>
            {sig.entryPrice.toLocaleString()}
          </div>
          <div style={{ fontSize: 13, color: 'var(--ink-mute)', marginTop: 8, fontFamily: 'BiauKai,標楷體,serif' }}>
            {sig.entryRef.src}
          </div>
        </div>

        {/* 目標價 */}
        <div style={{
          padding: '18px 20px', border: '1px solid var(--paper-line)',
          background: 'var(--paper)', borderLeft: '4px solid var(--bull)',
        }}>
          <div style={{ fontFamily: 'Times New Roman,serif', fontStyle: 'italic', fontSize: 13, color: 'var(--ink-mute)', marginBottom: 8 }}>
            Target · 出場目標
          </div>
          <div style={{ display: 'flex', gap: 16, alignItems: 'baseline' }}>
            <div>
              <div style={{ fontSize: 13, color: 'var(--ink-mute)', marginBottom: 3 }}>T1 前高</div>
              <div style={{ fontSize: 32, fontWeight: 700, fontFamily: 'Times New Roman,serif', color: '#4caf50', lineHeight: 1 }}>
                {sig.t1.toLocaleString()}
              </div>
            </div>
            <div>
              <div style={{ fontSize: 13, color: 'var(--ink-mute)', marginBottom: 3 }}>T2 延伸</div>
              <div style={{ fontSize: 22, fontWeight: 600, fontFamily: 'Times New Roman,serif', color: '#4caf50', opacity: .7, lineHeight: 1 }}>
                {sig.t2.toLocaleString()}
              </div>
            </div>
          </div>
          {sig.lastSwingHigh && (
            <div style={{ fontSize: 13, color: 'var(--ink-mute)', marginTop: 8, fontFamily: 'BiauKai,標楷體,serif' }}>
              近期前高 {sig.lastSwingHigh.date}
            </div>
          )}
        </div>

        {/* 停損 + R:R */}
        <div style={{
          padding: '18px 20px', border: '1px solid var(--paper-line)',
          background: 'var(--paper)', borderLeft: '4px solid var(--bear)',
        }}>
          <div style={{ fontFamily: 'Times New Roman,serif', fontStyle: 'italic', fontSize: 13, color: 'var(--ink-mute)', marginBottom: 8 }}>
            Stop Loss · 停損參考
          </div>
          <div style={{ fontSize: 34, fontWeight: 700, fontFamily: 'Times New Roman,serif', color: '#ef5350', lineHeight: 1 }}>
            {sig.sl.toLocaleString()}
          </div>
          {sig.rr != null && (
            <div style={{ marginTop: 10 }}>
              <span style={{ fontFamily: 'Times New Roman,serif', fontSize: 18, color: sig.rr >= 2 ? '#4caf50' : 'var(--ink-mute)', fontWeight: 700 }}>
                R:R = 1 : {sig.rr}
              </span>
              <span style={{ fontSize: 13, color: 'var(--ink-mute)', marginLeft: 8 }}>
                {sig.rr >= 3 ? '優異' : sig.rr >= 2 ? '良好' : sig.rr >= 1.5 ? '尚可' : '偏低'}
              </span>
            </div>
          )}
          <div style={{ fontSize: 13, color: 'var(--ink-mute)', marginTop: 8, fontFamily: 'BiauKai,標楷體,serif' }}>
            近期前低 − ATR×0.3
          </div>
        </div>
      </div>

      {/* ── 條件明細 ─────────────────────────────────── */}
      <div style={{ marginBottom: 20 }}>
        <div style={{
          fontFamily: 'Times New Roman,serif', fontStyle: 'italic',
          fontSize: 14, color: 'var(--ink-mute)', marginBottom: 12, letterSpacing: '.05em',
        }}>
          Condition Checklist · 條件明細
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
          {sig.conds.map(c => (
            <div key={c.key} style={{
              display: 'flex', alignItems: 'flex-start', gap: 12,
              padding: '14px 16px', background: 'var(--paper)',
              border: '1px solid var(--paper-line)',
              borderLeft: `4px solid ${c.ok ? 'var(--bull)' : c.warn ? 'var(--gold)' : 'var(--bear)'}`,
            }}>
              <span style={{ fontSize: 18, flexShrink: 0, lineHeight: 1.4 }}>{condIcon(c)}</span>
              <div>
                <div style={{ fontFamily: 'BiauKai,標楷體,serif', fontSize: 16, color: 'var(--ink)', marginBottom: 4 }}>
                  {c.label}
                  <span style={{ fontFamily: 'Times New Roman,serif', fontStyle: 'italic', fontSize: 12, color: 'var(--ink-mute)', marginLeft: 8 }}>
                    {c.en}
                  </span>
                </div>
                <div style={{ fontSize: 13, color: 'var(--ink-mute)', fontFamily: 'Times New Roman,serif' }}>
                  {c.detail}
                </div>
              </div>
            </div>
          ))}
        </div>
      </div>

      {/* ── SMC 關鍵價位 ──────────────────────────────── */}
      {(sig.bullOB.length > 0 || sig.bullFVG.length > 0) && (
        <div style={{ marginBottom: 20 }}>
          <div style={{
            fontFamily: 'Times New Roman,serif', fontStyle: 'italic',
            fontSize: 14, color: 'var(--ink-mute)', marginBottom: 12, letterSpacing: '.05em',
          }}>
            SMC Key Levels · 智慧資金關鍵價位
          </div>
          <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap' }}>
            {sig.bullOB.map((ob, i) => (
              <div key={'ob' + i} style={{
                padding: '10px 16px', background: 'rgba(76,175,80,0.08)',
                border: '1px solid rgba(76,175,80,0.3)', borderRadius: 3, fontSize: 14,
              }}>
                <span style={{ color: '#4caf50', fontFamily: 'Times New Roman,serif', fontStyle: 'italic' }}>OB </span>
                <span style={{ color: 'var(--ink)', fontFamily: 'Times New Roman,serif' }}>
                  {ob.bottom} – {ob.top}
                </span>
                <span style={{ color: 'var(--ink-mute)', marginLeft: 8, fontSize: 12 }}>需求區 {ob.date}</span>
              </div>
            ))}
            {sig.bullFVG.map((fvg, i) => (
              <div key={'fvg' + i} style={{
                padding: '10px 16px', background: 'rgba(255,152,0,0.08)',
                border: '1px solid rgba(255,152,0,0.3)', borderRadius: 3, fontSize: 14,
              }}>
                <span style={{ color: '#ff9800', fontFamily: 'Times New Roman,serif', fontStyle: 'italic' }}>FVG </span>
                <span style={{ color: 'var(--ink)', fontFamily: 'Times New Roman,serif' }}>
                  {fvg.bottom} – {fvg.top}
                </span>
                <span style={{ color: 'var(--ink-mute)', marginLeft: 8, fontSize: 12 }}>未填缺口（回補目標）</span>
              </div>
            ))}
          </div>
        </div>
      )}

      {/* ── 底部說明 ─────────────────────────────────── */}
      <div style={{ fontSize: 13, color: 'var(--ink-mute)', fontFamily: 'BiauKai,標楷體,serif', lineHeight: 2 }}>
        ※ 訊號邏輯參考 Smart Money Concepts（SMC）：Order Block 為機構建倉區，
        FVG 為未填缺口，BOS 確認趨勢。EMA 排列、MACD、RSI 作為輔助確認。
        <br/>
        ※ 停損採近期前低 − ATR×0.3，避免假跌破。R:R ≥ 2 為合格進場條件。
        本工具每次載入新 K 線後自動更新，不構成投資建議。
      </div>
    </div>
  );
}

// ════════════════════════════════════════════════════════════
//  MACD 策略：黃金交叉 / 死亡交叉 + 背離進出場
//  indicator-specialist · B 前端工坊
//  參考 twopirllc/pandas-ta → MACD / Divergence 邏輯
// ════════════════════════════════════════════════════════════

// ── 交叉偵測 ─────────────────────────────────────────────
function detectMACDCross(dif, dea) {
  const n = dif.length - 1;
  let lastGolden = null, lastDeath = null;
  for (let i = 1; i <= n; i++) {
    if (dif[i] == null || dea[i] == null || dif[i-1] == null || dea[i-1] == null) continue;
    if (dif[i-1] < dea[i-1] && dif[i] >= dea[i]) lastGolden = i;
    if (dif[i-1] > dea[i-1] && dif[i] <= dea[i]) lastDeath  = i;
  }
  return {
    lastGolden, lastDeath,
    barsAgoGolden: lastGolden != null ? n - lastGolden : null,
    barsAgoDeath:  lastDeath  != null ? n - lastDeath  : null,
    isBull:        dif[n] > dea[n],
    aboveZero:     dif[n] > 0,
    difNow: dif[n], deaNow: dea[n], histNow: (dif[n] - dea[n]) * 2,
  };
}

// ── 動態回看根數（ATR 波動率調整）────────────────────────
// 高波動（ATR/price > 3%）→ 縮短回看（最少 20）
// 低波動（ATR/price < 1%）→ 延長回看（最多 60）
function calcDynamicLookback(data, baseLen) {
  var bl = baseLen || 40;
  if (!data || data.length < 15) return bl;
  var trs = [];
  for (var i = 1; i < data.length; i++) {
    var h = data[i].high, l = data[i].low, pc = data[i-1].close;
    trs.push(Math.max(h - l, Math.abs(h - pc), Math.abs(l - pc)));
  }
  var last14 = trs.slice(-14);
  var atr = last14.reduce(function(a, b) { return a + b; }, 0) / last14.length;
  var currentPrice = data[data.length - 1].close;
  if (!currentPrice || currentPrice === 0) return bl;
  var atrPct = atr / currentPrice * 100;
  if (atrPct > 3) return Math.max(20, Math.round(bl * 0.5));
  if (atrPct < 1) return Math.min(60, Math.round(bl * 1.5));
  return bl;
}

// ── MACD 背離偵測（pandas-ta divergence 邏輯）────────────
// 多頭背離：價格創新低，MACD 直方圖未創新低（底部背離 → 轉多訊號）
// 空頭背離：價格創新高，MACD 直方圖未創新高（頂部背離 → 轉空訊號）
function detectMACDDiv(data, hist, lookback = 40) {
  const n    = data.length;
  const lb   = Math.min(lookback, n - 5);
  const half = Math.floor(lb / 2);
  if (lb < 10) return { bull: false, bear: false };

  const prices = data.slice(-lb).map(d => d.low);
  const phigh  = data.slice(-lb).map(d => d.high);
  const hs     = hist.slice(-lb).filter(v => v != null);

  // 把前後半段各自取極值
  const p1lo = Math.min(...prices.slice(0, half));
  const p2lo = Math.min(...prices.slice(half));
  const h1lo = Math.min(...hs.slice(0, half));
  const h2lo = Math.min(...hs.slice(half));

  const p1hi = Math.max(...phigh.slice(0, half));
  const p2hi = Math.max(...phigh.slice(half));
  const h1hi = Math.max(...hs.slice(0, half));
  const h2hi = Math.max(...hs.slice(half));

  // 多頭背離：後段價格更低 但直方圖更高（空方動能衰竭）
  const bull = p2lo < p1lo * 0.999 && h2lo > h1lo * 0.9;
  // 空頭背離：後段價格更高 但直方圖更低（多方動能衰竭）
  const bear = p2hi > p1hi * 1.001 && h2hi < h1hi * 0.9;

  return { bull, bear };
}

// ── RSI 背離偵測 ──────────────────────────────────────────
// 多頭背離：後段價格更低，但 RSI 更高（空方動能衰竭）
// 空頭背離：後段價格更高，但 RSI 更低（多方動能衰竭）
function detectRSIDiv(data, rsiSeries, lookback) {
  const lb = lookback || 40;
  const n  = data.length;
  const lbActual = Math.min(lb, n - 5);
  const half = Math.floor(lbActual / 2);
  if (lbActual < 10) return { bull: false, bear: false };

  const prices = data.slice(-lbActual).map(d => d.low);
  const phigh  = data.slice(-lbActual).map(d => d.high);
  const rs     = rsiSeries.slice(-lbActual).filter(v => v != null);
  if (rs.length < lbActual * 0.7) return { bull: false, bear: false };

  const p1lo = Math.min(...prices.slice(0, half));
  const p2lo = Math.min(...prices.slice(half));
  const r1lo = Math.min(...rs.slice(0, half));
  const r2lo = Math.min(...rs.slice(half));

  const p1hi = Math.max(...phigh.slice(0, half));
  const p2hi = Math.max(...phigh.slice(half));
  const r1hi = Math.max(...rs.slice(0, half));
  const r2hi = Math.max(...rs.slice(half));

  // 多頭背離：後段價格更低，RSI 卻更高
  const bull = p2lo < p1lo * 0.999 && r2lo > r1lo * 0.9;
  // 空頭背離：後段價格更高，RSI 卻更低
  const bear = p2hi > p1hi * 1.001 && r2hi < r1hi * 0.9;

  return { bull, bear };
}

// ── MACD 策略面板用：計算 ATR(14) ────────────────────────
function calcATR14(data) {
  if (!data || data.length < 2) return 0;
  var trs = [];
  for (var i = 1; i < data.length; i++) {
    var h = data[i].high, l = data[i].low, pc = data[i-1].close;
    trs.push(Math.max(h - l, Math.abs(h - pc), Math.abs(l - pc)));
  }
  var last14 = trs.slice(-14);
  return last14.reduce(function(a, b) { return a + b; }, 0) / last14.length;
}

// ── MACD 策略面板用：找最近 40 根內的 Swing High/Low (lookback=5) ──
function findRecentSwing(data, lookback) {
  var lb = lookback || 5;
  var window = Math.min(40 + lb, data.length);
  var slice  = data.slice(-window);
  var lastHigh = null, lastLow = null;
  for (var i = lb; i < slice.length - lb; i++) {
    var isH = slice.slice(i - lb, i).every(function(d) { return d.high <= slice[i].high; }) &&
              slice.slice(i + 1, i + lb + 1).every(function(d) { return d.high <= slice[i].high; });
    var isL = slice.slice(i - lb, i).every(function(d) { return d.low  >= slice[i].low; }) &&
              slice.slice(i + 1, i + lb + 1).every(function(d) { return d.low  >= slice[i].low; });
    if (isH) lastHigh = slice[i].high;
    if (isL) lastLow  = slice[i].low;
  }
  return { swingHigh: lastHigh, swingLow: lastLow };
}

// ── KDJ 計算（Stochastic 變形）────────────────────────────
// K = 前N日 Stochastic Fast %K 再以 smoothK 平滑
// D = K 的 smoothD 期 SMA
// J = 3×K - 2×D
function calcKDJ(data, n = 9, smoothK = 3, smoothD = 3) {
  const highs  = data.map(d => d.high);
  const lows   = data.map(d => d.low);
  const closes = data.map(d => d.close);
  const len = data.length;

  // Stochastic raw
  const rawK = new Array(len).fill(null);
  for (let i = n - 1; i < len; i++) {
    const hh = Math.max(...highs.slice(i - n + 1, i + 1));
    const ll = Math.min(...lows.slice(i - n + 1, i + 1));
    rawK[i] = hh === ll ? 50 : (closes[i] - ll) / (hh - ll) * 100;
  }

  // Smooth K (SMA of rawK)
  const K = new Array(len).fill(null);
  for (let i = n + smoothK - 2; i < len; i++) {
    const slice = rawK.slice(i - smoothK + 1, i + 1).filter(v => v !== null);
    if (slice.length === smoothK) K[i] = slice.reduce((a, b) => a + b, 0) / smoothK;
  }

  // D = SMA(K, smoothD)
  const D = new Array(len).fill(null);
  for (let i = 0; i < len; i++) {
    const slice = K.slice(Math.max(0, i - smoothD + 1), i + 1).filter(v => v !== null);
    if (slice.length >= 1) D[i] = slice.reduce((a, b) => a + b, 0) / slice.length;
  }

  // J = 3K - 2D
  const J = K.map((k, i) => k !== null && D[i] !== null ? 3 * k - 2 * D[i] : null);

  return { K, D, J };
}

function interpretKDJ(K, D, J) {
  const len = K.length;
  const lastK = K[len - 1], lastD = D[len - 1], lastJ = J[len - 1];
  const prevK = K[len - 2], prevD = D[len - 2];
  if (lastK == null || lastD == null) return null;

  // 金叉：K 由下往上穿越 D
  const kGolden = prevK !== null && prevK < prevD && lastK >= lastD;
  // 死叉：K 由上往下穿越 D
  const kDead   = prevK !== null && prevK > prevD && lastK <= lastD;
  // 超賣區（J < 20）
  const oversold   = lastJ !== null && lastJ < 20;
  // 超買區（J > 80）
  const overbought = lastJ !== null && lastJ > 80;

  return { lastK, lastD, lastJ, kGolden, kDead, oversold, overbought };
}

// ── MACD 策略面板 ─────────────────────────────────────────
function MACDStrategyPanel({ data, macdObj, rsiArr }) {
  const { dif, dea, hist } = macdObj;
  const cross = React.useMemo(() => detectMACDCross(dif, dea), [macdObj]);
  const dynLookback = React.useMemo(() => calcDynamicLookback(data), [data]);
  const div   = React.useMemo(() => detectMACDDiv(data, hist, dynLookback),  [data, macdObj, dynLookback]);
  const rsiDiv = React.useMemo(() => {
    if (!rsiArr || rsiArr.length < 20) return { bull: false, bear: false };
    return detectRSIDiv(data, rsiArr, dynLookback);
  }, [data, rsiArr, dynLookback]);

  // KDJ 輔助確認
  const kdjResult = React.useMemo(() => {
    if (!data || data.length < 15) return null;
    const { K, D, J } = calcKDJ(data);
    return interpretKDJ(K, D, J);
  }, [data]);

  // 雙重背離：MACD 背離 AND RSI 同向背離
  const dualBull = div.bull && rsiDiv.bull;
  const dualBear = div.bear && rsiDiv.bear;
  // 單一 MACD 背離（RSI 未確認）
  const macdOnlyBull = div.bull && !rsiDiv.bull;
  const macdOnlyBear = div.bear && !rsiDiv.bear;

  // ── 計算明確價位 ─────────────────────────────────────────
  const priceLevel = React.useMemo(() => {
    if (!data || data.length < 20) return null;
    var closes = data.map(function(d) { return d.close; });
    var e20arr  = emaArr(closes, 20);
    var ema20   = Math.round(e20arr[e20arr.length - 1]);

    var atr14   = calcATR14(data);
    var swing   = findRecentSwing(data, 5);

    var swingHigh = swing.swingHigh;
    var swingLow  = swing.swingLow;

    var t1  = swingHigh ? Math.round(swingHigh) : null;
    var t2  = t1 ? Math.round(t1 * 1.02) : null;
    var sl  = swingLow ? Math.round(swingLow - atr14 * 0.3) : null;

    var rr  = (t1 && sl && t1 > ema20 && sl < ema20)
      ? +((t1 - ema20) / (ema20 - sl)).toFixed(1)
      : null;

    return { ema20, t1, t2, sl, rr };
  }, [data]);

  // 綜合訊號
  const isActive  = cross.lastGolden != null && cross.lastDeath != null;
  const lastCross = isActive && cross.lastGolden > cross.lastDeath ? 'golden' : 'death';
  const barsAgo   = lastCross === 'golden' ? cross.barsAgoGolden : cross.barsAgoDeath;

  let signal = 'NEUTRAL';
  if (lastCross === 'golden' && cross.aboveZero && !dualBear && !macdOnlyBear) signal = 'BUY';
  else if (lastCross === 'golden' && !cross.aboveZero)        signal = 'WATCH';
  else if (lastCross === 'death' && !cross.aboveZero)         signal = 'SELL';
  else if (dualBull && cross.isBull)                          signal = 'BUY';
  else if (dualBear && !cross.isBull)                         signal = 'SELL';

  // KDJ 綜合確認旗標
  const kdjConfirmBull = kdjResult && signal !== 'SELL' &&
    (kdjResult.kGolden || kdjResult.oversold);
  const kdjConfirmBear = kdjResult && signal !== 'BUY' &&
    (kdjResult.kDead || kdjResult.overbought);

  const sigColor = { BUY:'var(--bull)', SELL:'var(--bear)', WATCH:'var(--gold)', NEUTRAL:'var(--ink-mute)' };
  const sigLabel = { BUY:'買進訊號', SELL:'賣出訊號', WATCH:'觀望', NEUTRAL:'中立' };

  const Chip = ({ ok, label, sub }) => (
    <div style={{
      padding: '12px 16px', border: '1px solid var(--paper-line)',
      borderLeft: `4px solid ${ok ? 'var(--bull)' : 'var(--bear)'}`,
      background: 'var(--paper)', display: 'flex', flexDirection: 'column', gap: 5,
    }}>
      <span style={{ fontSize: 13, color: ok ? 'var(--bull)' : 'var(--bear)', fontFamily: 'Times New Roman,serif', fontStyle: 'italic' }}>
        {ok ? '●' : '○'} {label}
      </span>
      <span style={{ fontSize: 14, color: 'var(--ink)', fontFamily: 'BiauKai,標楷體,serif' }}>{sub}</span>
    </div>
  );

  return (
    <div style={{ marginTop: 32, paddingTop: 24, borderTop: '1px solid var(--paper-line)' }}>

      {/* 標題列 */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 16 }}>
        <div>
          <span style={{ fontFamily: 'BiauKai,標楷體,serif', fontSize: 18, color: 'var(--ink)' }}>
            MACD 交叉策略
          </span>
          <span style={{ fontFamily: 'Times New Roman,serif', fontStyle: 'italic', fontSize: 13, color: 'var(--ink-mute)', marginLeft: 10 }}>
            Golden / Death Cross + Divergence · twopirllc/pandas-ta
          </span>
        </div>
        <div style={{
          marginLeft: 'auto', padding: '6px 18px',
          border: `1px solid ${sigColor[signal]}`,
          color: sigColor[signal], fontSize: 16, fontWeight: 700,
          fontFamily: 'Times New Roman,serif', letterSpacing: '.05em',
        }}>
          {signal} · {sigLabel[signal]}
        </div>
      </div>

      {/* 交叉狀態 */}
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr 1fr', gap: 10, marginBottom: 16 }}>
        <Chip
          ok={cross.isBull}
          label={cross.isBull ? '黃金交叉' : '死亡交叉'}
          sub={cross.isBull
            ? `DIF ${cross.difNow?.toFixed(2)} > DEA ${cross.deaNow?.toFixed(2)}`
            : `DIF ${cross.difNow?.toFixed(2)} < DEA ${cross.deaNow?.toFixed(2)}`}
        />
        <Chip
          ok={cross.aboveZero}
          label={cross.aboveZero ? '零軸上方（強勢）' : '零軸下方（弱勢）'}
          sub={`DIF ${cross.difNow?.toFixed(2)} vs 0`}
        />
        <Chip
          ok={lastCross === 'golden'}
          label={lastCross === 'golden' ? `黃金交叉 ${barsAgo}根前` : `死亡交叉 ${barsAgo}根前`}
          sub={lastCross === 'golden'
            ? barsAgo <= 3 ? '剛發生（強訊號）' : barsAgo <= 10 ? '近期有效' : '已發生一段時間'
            : barsAgo <= 3 ? '剛發生（避開）' : '持續弱勢'}
        />
        <Chip
          ok={cross.histNow > 0}
          label={cross.histNow > 0 ? '直方圖正值' : '直方圖負值'}
          sub={`OSC ${(cross.histNow / 2)?.toFixed(2)} · ${cross.histNow > 0 ? '多方動能' : '空方動能'}`}
        />
      </div>

      {/* 背離訊號 — 雙重確認或單一 MACD */}
      {(dualBull || dualBear) && (
        <div style={{
          padding: '12px 16px', marginBottom: 16,
          background: dualBull ? 'rgba(184,146,77,0.12)' : 'rgba(239,83,80,0.10)',
          border: `2px solid ${dualBull ? 'var(--gold)' : 'var(--bear)'}`,
          display: 'flex', gap: 12, alignItems: 'center',
        }}>
          <span style={{ fontSize: 22 }}>{dualBull ? '⚡' : '⚡'}</span>
          <div style={{ flex: 1 }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 3 }}>
              <span style={{ fontFamily: 'BiauKai,標楷體,serif', fontSize: 17, fontWeight: 700,
                color: dualBull ? 'var(--gold)' : 'var(--bear)' }}>
                {dualBull ? '多頭背離（底部背離）' : '空頭背離（頂部背離）'}
              </span>
              <span style={{
                fontSize: 13, fontWeight: 700, padding: '3px 10px',
                background: 'var(--gold)', color: '#fff',
                fontFamily: 'Times New Roman,serif', letterSpacing: '.04em',
              }}>⚡ 雙重背離確認</span>
            </div>
            <div style={{ fontSize: 14, color: 'var(--ink-mute)', fontFamily: 'BiauKai,標楷體,serif' }}>
              MACD 直方圖 + RSI 雙重確認 →{' '}
              {dualBull
                ? '空方動能衰竭，反轉訊號強度高'
                : '多方動能衰竭，回落訊號強度高'}
            </div>
          </div>
        </div>
      )}
      {(macdOnlyBull || macdOnlyBear) && (
        <div style={{
          padding: '12px 16px', marginBottom: 16,
          background: 'rgba(150,150,150,0.06)',
          border: '1.5px solid #aaa',
          display: 'flex', gap: 12, alignItems: 'center',
        }}>
          <span style={{ fontSize: 18 }}>{macdOnlyBull ? '📈' : '📉'}</span>
          <div>
            <div style={{ fontFamily: 'BiauKai,標楷體,serif', fontSize: 16, fontWeight: 600,
              color: '#888', marginBottom: 4 }}>
              {macdOnlyBull ? '多頭背離（底部背離）' : '空頭背離（頂部背離）'}
              <span style={{ fontSize: 13, marginLeft: 8, fontWeight: 400 }}>MACD 背離（單一）</span>
            </div>
            <div style={{ fontSize: 14, color: '#aaa', fontFamily: 'BiauKai,標楷體,serif' }}>
              {macdOnlyBull
                ? 'MACD 直方圖出現背離，但 RSI 未確認 → 降權重，謹慎參考'
                : 'MACD 直方圖出現背離，但 RSI 未確認 → 降權重，謹慎參考'}
            </div>
          </div>
        </div>
      )}

      {/* KDJ 輔助確認 */}
      {kdjResult && (
        <div style={{ marginTop: 8, padding: '8px 14px', background: 'var(--paper-deep)',
                      borderRadius: 3, fontSize: 13, fontFamily: 'Times New Roman,serif' }}>
          <span style={{ color: 'var(--ink-mute)', fontStyle: 'italic' }}>KDJ 輔助 </span>
          <span style={{ color: 'var(--ink)' }}>
            K {kdjResult.lastK.toFixed(1)} · D {kdjResult.lastD.toFixed(1)} · J {kdjResult.lastJ.toFixed(1)}
          </span>
          {kdjResult.kGolden   && <span style={{ color: 'var(--bull)', marginLeft: 8 }}>▲ KD金叉</span>}
          {kdjResult.kDead     && <span style={{ color: 'var(--bear)', marginLeft: 8 }}>▼ KD死叉</span>}
          {kdjResult.oversold  && <span style={{ color: 'var(--bull)', marginLeft: 8 }}>超賣區 J&lt;20</span>}
          {kdjResult.overbought && <span style={{ color: 'var(--bear)', marginLeft: 8 }}>超買區 J&gt;80</span>}
          {kdjConfirmBull && (
            <span style={{ color: 'var(--bull)', fontWeight: 700, marginLeft: 12 }}>⚡ KDJ 確認多頭</span>
          )}
          {kdjConfirmBear && (
            <span style={{ color: 'var(--bear)', fontWeight: 700, marginLeft: 12 }}>⚡ KDJ 確認空頭</span>
          )}
        </div>
      )}

      {/* ── 明確價位 三欄 ──────────────────────────────── */}
      {priceLevel && (
        <div style={{ marginBottom: 16 }}>
          <div style={{
            fontFamily: 'Times New Roman,serif', fontStyle: 'italic',
            fontSize: 14, color: 'var(--ink-mute)', marginBottom: 12, letterSpacing: '.05em',
          }}>
            Key Prices · 明確價位
          </div>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 10 }}>

            {/* 停損（左欄，紅色）*/}
            <div style={{
              padding: '16px 18px', border: '1px solid var(--paper-line)',
              background: 'var(--paper)', borderLeft: '4px solid var(--bear)',
            }}>
              <div style={{ fontFamily: 'Times New Roman,serif', fontStyle: 'italic', fontSize: 13, color: 'var(--ink-mute)', marginBottom: 6 }}>
                Stop Loss · 停損
              </div>
              <div style={{ fontSize: 30, fontWeight: 700, fontFamily: 'Times New Roman,serif', color: '#ef5350', lineHeight: 1 }}>
                {priceLevel.sl != null ? priceLevel.sl.toLocaleString() : '—'}
              </div>
              <div style={{ fontSize: 13, color: 'var(--ink-mute)', marginTop: 6, fontFamily: 'BiauKai,標楷體,serif' }}>
                前低防守 − ATR×0.3
              </div>
              {priceLevel.rr != null && (
                <div style={{ marginTop: 8 }}>
                  <span style={{ fontFamily: 'Times New Roman,serif', fontSize: 16, color: priceLevel.rr >= 2 ? '#4caf50' : 'var(--ink-mute)', fontWeight: 700 }}>
                    R:R = 1 : {priceLevel.rr}
                  </span>
                  <span style={{ fontSize: 13, color: 'var(--ink-mute)', marginLeft: 6 }}>
                    {priceLevel.rr >= 3 ? '優異' : priceLevel.rr >= 2 ? '良好' : priceLevel.rr >= 1.5 ? '尚可' : '偏低'}
                  </span>
                </div>
              )}
            </div>

            {/* 進場（中欄，金色）*/}
            <div style={{
              padding: '16px 18px', border: '1px solid var(--paper-line)',
              background: 'var(--paper)', borderLeft: '4px solid var(--gold)',
            }}>
              <div style={{ fontFamily: 'Times New Roman,serif', fontStyle: 'italic', fontSize: 13, color: 'var(--ink-mute)', marginBottom: 6 }}>
                Entry · 進場
              </div>
              <div style={{ fontSize: 30, fontWeight: 700, fontFamily: 'Times New Roman,serif', color: 'var(--gold-deep)', lineHeight: 1 }}>
                {priceLevel.ema20.toLocaleString()}
              </div>
              <div style={{ fontSize: 13, color: 'var(--ink-mute)', marginTop: 6, fontFamily: 'BiauKai,標楷體,serif' }}>
                EMA20 支撐進場
              </div>
            </div>

            {/* 目標（右欄，綠色）*/}
            <div style={{
              padding: '16px 18px', border: '1px solid var(--paper-line)',
              background: 'var(--paper)', borderLeft: '4px solid var(--bull)',
            }}>
              <div style={{ fontFamily: 'Times New Roman,serif', fontStyle: 'italic', fontSize: 13, color: 'var(--ink-mute)', marginBottom: 6 }}>
                Target · 目標
              </div>
              <div style={{ display: 'flex', gap: 14, alignItems: 'baseline' }}>
                <div>
                  <div style={{ fontSize: 13, color: 'var(--ink-mute)', marginBottom: 3 }}>T1 前高</div>
                  <div style={{ fontSize: 28, fontWeight: 700, fontFamily: 'Times New Roman,serif', color: '#4caf50', lineHeight: 1 }}>
                    {priceLevel.t1 != null ? priceLevel.t1.toLocaleString() : '—'}
                  </div>
                </div>
                <div>
                  <div style={{ fontSize: 13, color: 'var(--ink-mute)', marginBottom: 3 }}>T2 延伸</div>
                  <div style={{ fontSize: 20, fontWeight: 600, fontFamily: 'Times New Roman,serif', color: '#4caf50', opacity: .7, lineHeight: 1 }}>
                    {priceLevel.t2 != null ? priceLevel.t2.toLocaleString() : '—'}
                  </div>
                </div>
              </div>
              <div style={{ fontSize: 13, color: 'var(--ink-mute)', marginTop: 6, fontFamily: 'BiauKai,標楷體,serif' }}>
                近高 · T2 = T1 × 1.02
              </div>
            </div>

          </div>
        </div>
      )}

      {/* 進出場邏輯說明 */}
      <div style={{
        display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10, marginBottom: 16,
      }}>
        <div style={{ padding: '14px 18px', background: 'rgba(76,175,80,0.06)', border: '1px solid rgba(76,175,80,0.2)' }}>
          <div style={{ fontSize: 14, color: 'var(--bull)', fontFamily: 'Times New Roman,serif', fontStyle: 'italic', marginBottom: 8 }}>
            ● 進場條件（三合一確認）
          </div>
          <div style={{ fontSize: 15, color: 'var(--ink)', lineHeight: 2, fontFamily: 'BiauKai,標楷體,serif' }}>
            ① DIF 上穿 DEA（黃金交叉）<br/>
            ② 交叉發生在零軸上方（強訊號）<br/>
            ③ 多頭背離出現 或 直方圖持續擴大
          </div>
        </div>
        <div style={{ padding: '14px 18px', background: 'rgba(239,83,80,0.06)', border: '1px solid rgba(239,83,80,0.2)' }}>
          <div style={{ fontSize: 14, color: 'var(--bear)', fontFamily: 'Times New Roman,serif', fontStyle: 'italic', marginBottom: 8 }}>
            ● 出場條件（任一觸發）
          </div>
          <div style={{ fontSize: 15, color: 'var(--ink)', lineHeight: 2, fontFamily: 'BiauKai,標楷體,serif' }}>
            ① DIF 下穿 DEA（死亡交叉）<br/>
            ② 空頭背離出現（頂部背離警示）<br/>
            ③ 直方圖連續 3 根縮小（動能衰竭）
          </div>
        </div>
      </div>

      <div style={{ fontSize: 13, color: 'var(--ink-mute)', fontFamily: 'BiauKai,標楷體,serif', lineHeight: 2 }}>
        ※ MACD 參數（12, 26, 9）·{' '}
        <span style={{ color: 'var(--gold)', fontWeight: 600 }}>
          回看 {dynLookback} 根（波動率調整）
        </span>
        {dynLookback !== 40 && (
          <span>
            {dynLookback < 40 ? '（高波動：縮短回看）' : '（低波動：延長回看）'}
          </span>
        )}
        。零軸上方黃金交叉可信度 &gt; 零軸下方。
        背離需 MACD + RSI 雙重確認方為強訊號。純機械訊號，需搭配 SMC 結構確認。
        本策略由 indicator-specialist 持續監控與調整。
      </div>
    </div>
  );
}

window.SignalPanel  = SignalPanel;
window.MACDStrategyPanel = MACDStrategyPanel;
