/* KC MA5 結構指標 — 對齊 TradingView Pine v19「台股均線指標 v19 - Ch1 多頭買點對齊高勝率」
 *
 * window.KCStructure.compute(candles, params?) → events[]
 *
 * 與 Pine 對齊的核心邏輯：
 *   - swing：以「價格穿越 MA5」確認 swing high / low（priceAboveMa5 期間追高、breakBelowMa5 確認）
 *   - structure：頭頭高底底高 / 頭頭低底底低 / 其他（盤整）
 *   - 月線：close vs ma20、ma20 斜率
 *   - 趨勢：isBullTrend = structureBull && monthlyBull；isBearTrend = structureBear && monthlyBear
 *   - 進場：盤整突破 / 回後買上漲 / 高勝率紅 K（多）；mirror（空）
 *   - 風控過濾：短線過熱（連漲 N 日 + 累積 X%）、急漲爆量（M 日 +Y% + 量為近 N 日最大）、急漲後急跌反轉
 *   - 出場優先序：急漲爆量後急跌 → 短線過熱獲利 → 跌破前低 → 頭頭低 → 跌破月線 → 跌破 5MA
 *   - 等待標籤：waitTakeProfit / waitSurgeReversal / waitNoChase / waitLongPullback / waitRangeBreakLong
 *               / waitShortRebound / waitRangeBreakShort
 *
 * 事件 schema 維持不變（chart.jsx 直接吃）：
 *   { idx, confirmIdx, anchorIdx, endIdx, type, label, reason, price, priceLevel }
 */

const KC_DEFAULT_PARAMS = {
  maPeriods: { ma5: 5, ma10: 10, ma20: 20, ma60: 60 },
  highProb: {
    longRisePct: 2.0,           // Pine: longRisePct
    longVolumePct: 20.0,        // Pine: longVolumePct（vs 前日量）
    shortFallPct: 2.0,          // Pine: shortFallPct
    shortVolumePct: 20.0,       // Pine: shortVolumePct
    useShortVolumeFilter: false,// Pine: useShortVolumeFilter
  },
  overheat: {
    enabled: true,              // Pine: useOverheatFilter
    days: 3,                    // Pine: overheatDays
    risePct: 10.0,              // Pine: overheatRisePct
    useExit: true,              // Pine: useOverheatExit
  },
  surge: {
    enabled: true,              // Pine: useSurgeFilter
    days: 5,                    // Pine: surgeDays
    risePct: 20.0,              // Pine: surgeRisePct
    volumeLookback: 10,         // Pine: surgeVolumeLookback
    dropConfirmBars: 3,         // Pine: surgeDropConfirmBars
    dropPct: 3.0,               // Pine: surgeDropPct
  },
};

function sma(arr, key, period) {
  const out = new Array(arr.length).fill(null);
  let sum = 0;
  for (let i = 0; i < arr.length; i++) {
    sum += Number(arr[i][key]) || 0;
    if (i >= period) sum -= Number(arr[i - period][key]) || 0;
    if (i >= period - 1) out[i] = sum / period;
  }
  return out;
}

/* 「價格穿越 MA5」法偵測 swing — 對齊 Pine v19 */
function detectSwings(data, ma5) {
  const swings = [];
  let swingHigh = null, swingHighBar = -1;
  let swingLow = null, swingLowBar = -1;

  for (let i = 1; i < data.length; i++) {
    if (ma5[i] == null || ma5[i - 1] == null) continue;

    const close = data[i].c;
    const high = data[i].h;
    const low = data[i].l;
    const ma5Now = ma5[i];
    const ma5Prev = ma5[i - 1];
    const closePrev = data[i - 1].c;

    const priceAboveMa5 = close > ma5Now;
    const priceBelowMa5 = close < ma5Now;
    const breakBelowMa5 = closePrev > ma5Prev && close < ma5Now;
    const breakAboveMa5 = closePrev < ma5Prev && close > ma5Now;

    if (priceAboveMa5) {
      if (swingHigh == null || high > swingHigh) {
        swingHigh = high;
        swingHighBar = i;
      }
    }
    if (breakBelowMa5 && swingHigh != null) {
      swings.push({ type: "high", anchorIdx: swingHighBar, confirmIdx: i, price: swingHigh });
      swingHigh = null;
      swingHighBar = -1;
    }

    if (priceBelowMa5) {
      if (swingLow == null || low < swingLow) {
        swingLow = low;
        swingLowBar = i;
      }
    }
    if (breakAboveMa5 && swingLow != null) {
      swings.push({ type: "low", anchorIdx: swingLowBar, confirmIdx: i, price: swingLow });
      swingLow = null;
      swingLowBar = -1;
    }
  }
  return swings;
}

function highestVolume(data, end, lookback) {
  let m = -Infinity;
  const start = Math.max(0, end - lookback + 1);
  for (let j = start; j <= end; j++) {
    if (data[j].v > m) m = data[j].v;
  }
  return m;
}

function compute(data, userParams) {
  if (!Array.isArray(data) || data.length < 25) return [];
  const p = {
    ...KC_DEFAULT_PARAMS,
    ...(userParams || {}),
    maPeriods: { ...KC_DEFAULT_PARAMS.maPeriods, ...(userParams && userParams.maPeriods) },
    highProb:  { ...KC_DEFAULT_PARAMS.highProb,  ...(userParams && userParams.highProb)  },
    overheat:  { ...KC_DEFAULT_PARAMS.overheat,  ...(userParams && userParams.overheat)  },
    surge:     { ...KC_DEFAULT_PARAMS.surge,     ...(userParams && userParams.surge)     },
  };
  const events = [];

  const ma5 = sma(data, "c", p.maPeriods.ma5);
  const ma20 = sma(data, "c", p.maPeriods.ma20);

  /* 1. swings — 用 Pine 法 */
  const swings = detectSwings(data, ma5);
  for (const s of swings) {
    events.push({
      idx: s.anchorIdx,
      confirmIdx: s.confirmIdx,
      type: s.type,
      label: s.type === "high" ? "頭" : "底",
      price: s.price,
    });
  }

  /* 2. 主 loop — 結構、過濾、訊號 */
  let position = 0;            // 0 / 1 / -1
  let positionEntryIdx = -1;
  let lastTrendLabel = null;
  let consecUpDays = 0;
  let lastSurgeBar = -1;
  const necklines = new Set();

  /* 保存「上一根」的關鍵狀態，用於對齊 Pine 的 [1] 取值 */
  let prevState = null;

  for (let i = p.maPeriods.ma20; i < data.length; i++) {
    /* swings 已知範圍 = confirmIdx <= i */
    const known = swings.filter((s) => s.confirmIdx <= i);
    const highs = known.filter((s) => s.type === "high");
    const lows = known.filter((s) => s.type === "low");

    /* 連漲天數 — 任何 i 都要更新 */
    if (i > 0 && data[i].c > data[i - 1].c) consecUpDays++;
    else consecUpDays = 0;

    /* 短線過熱 */
    const cumRiseShort = i >= p.overheat.days && data[i - p.overheat.days].c > 0
      ? ((data[i].c - data[i - p.overheat.days].c) / data[i - p.overheat.days].c) * 100
      : 0;
    const overheatedShort = p.overheat.enabled
      && consecUpDays >= p.overheat.days
      && cumRiseShort >= p.overheat.risePct;

    /* 急漲爆量 */
    const cumRiseSurge = i >= p.surge.days && data[i - p.surge.days].c > 0
      ? ((data[i].c - data[i - p.surge.days].c) / data[i - p.surge.days].c) * 100
      : 0;
    const maxVolN = highestVolume(data, i, p.surge.volumeLookback);
    const overheatedSurge = p.surge.enabled
      && cumRiseSurge >= p.surge.risePct
      && data[i].v >= maxVolN;

    if (overheatedSurge) lastSurgeBar = i;

    /* 急漲爆量後急跌反轉 — 在 lastSurgeBar 之後 N 根內 */
    const blackBody = data[i].c < data[i].o;
    const dailyFallPct = i > 0 && data[i - 1].c > 0
      ? ((data[i - 1].c - data[i].c) / data[i - 1].c) * 100
      : 0;
    const surgeReversalConfirmed = p.surge.enabled
      && lastSurgeBar >= 0 && i > lastSurgeBar
      && (i - lastSurgeBar) <= p.surge.dropConfirmBars
      && blackBody
      && dailyFallPct >= p.surge.dropPct
      && data[i].c < data[i - 1].l;

    const overheatedAny = overheatedShort || overheatedSurge || surgeReversalConfirmed;

    /* 還沒有兩高兩低 — 只更新前置狀態，不出訊號 */
    if (highs.length < 2 || lows.length < 2) {
      prevState = null;
      continue;
    }

    const lastHigh = highs[highs.length - 1];
    const prevHigh = highs[highs.length - 2];
    const lastLow = lows[lows.length - 1];
    const prevLow = lows[lows.length - 2];

    const headHigher = lastHigh.price > prevHigh.price;
    const headLower = lastHigh.price < prevHigh.price;
    const baseHigher = lastLow.price > prevLow.price;
    const baseLower = lastLow.price < prevLow.price;

    let structure;
    if (headHigher && baseHigher) structure = "bull";
    else if (headLower && baseLower) structure = "bear";
    else structure = "range";

    const cur = data[i];
    const prev = data[i - 1];
    const open = cur.o;
    const close = cur.c;
    const prevClose = prev.c;
    const prevHighK = prev.h;
    const prevLowK = prev.l;

    const dailyRisePct = prevClose > 0 ? ((close - prevClose) / prevClose) * 100 : 0;
    const volumeIncreasePct = prev.v > 0 ? ((cur.v - prev.v) / prev.v) * 100 : 0;

    const ma5Now = ma5[i];
    const ma5Prev = ma5[i - 1];
    const ma20Now = ma20[i];
    const ma20Slope = i >= p.maPeriods.ma20 + 1 ? Math.sign(ma20[i] - ma20[i - 1]) : 0;

    const redBody = close > open;
    const breakAboveMa5 = ma5Prev != null && prevClose < ma5Prev && close > ma5Now;
    const breakBelowMa5 = ma5Prev != null && prevClose > ma5Prev && close < ma5Now;

    /* 頸線（只在 range） */
    let upperNeck = null, lowerNeck = null;
    if (structure === "range") {
      upperNeck = Math.min(lastHigh.price, prevHigh.price);
      lowerNeck = Math.max(lastLow.price, prevLow.price);

      const upKey = `up.${prevHigh.anchorIdx}.${lastHigh.anchorIdx}`;
      if (!necklines.has(upKey)) {
        necklines.add(upKey);
        events.push({
          idx: prevHigh.anchorIdx,
          endIdx: lastHigh.anchorIdx,
          priceLevel: upperNeck,
          type: "neckline-up",
          label: "上頸線",
        });
      }
      const dnKey = `dn.${prevLow.anchorIdx}.${lastLow.anchorIdx}`;
      if (!necklines.has(dnKey)) {
        necklines.add(dnKey);
        events.push({
          idx: prevLow.anchorIdx,
          endIdx: lastLow.anchorIdx,
          priceLevel: lowerNeck,
          type: "neckline-dn",
          label: "下頸線",
        });
      }
    }

    const monthlyBull = close > ma20Now && ma20Slope > 0;
    const monthlyBear = close < ma20Now && ma20Slope < 0;
    const isBullTrend = structure === "bull" && monthlyBull;
    const isBearTrend = structure === "bear" && monthlyBear;
    const isRangeState = !isBullTrend && !isBearTrend;

    const trendLabel = isBullTrend ? "多頭" : isBearTrend ? "空頭" : "盤整";
    if (trendLabel !== lastTrendLabel) {
      events.push({ idx: i, type: "trend", label: trendLabel });
      lastTrendLabel = trendLabel;
    }

    /* 是否為 swing high/low 的 confirmIdx — 對齊 Pine 的 confirmedHighThisBar */
    const confirmedHighThisBar = swings.some(s => s.type === "high" && s.confirmIdx === i);

    /* === 出場（最優先） === */
    if (position === 1 && i > positionEntryIdx) {
      let reason = null;
      /* Pine 順序：急漲爆量後急跌 > 短線過熱獲利 > 跌破前低 > 頭頭低 > 跌破月線 > 跌破 5MA */
      if (surgeReversalConfirmed) reason = "急漲爆量後急跌";
      else if (p.overheat.useExit && overheatedShort) reason = "短線過熱獲利";
      else if (close < lastLow.price) reason = "跌破前低";
      else if (confirmedHighThisBar && headLower) reason = "頭頭低";
      else if (close < ma20Now) reason = "跌破月線";
      else if (close < ma5Now) reason = "跌破 5MA";
      if (reason) {
        events.push({
          idx: i, confirmIdx: i, anchorIdx: lastLow.anchorIdx,
          type: "long-exit", label: "多單出場", reason, price: close,
        });
        position = 0;
        positionEntryIdx = -1;
      }
    } else if (position === -1 && i > positionEntryIdx) {
      let reason = null;
      const confirmedLowThisBar = swings.some(s => s.type === "low" && s.confirmIdx === i);
      if (close > lastHigh.price) reason = "突破前高";
      else if (confirmedLowThisBar && baseHigher) reason = "底底高";
      else if (close > ma20Now) reason = "突破月線";
      else if (close > ma5Now) reason = "站上 5MA";
      if (reason) {
        events.push({
          idx: i, confirmIdx: i, anchorIdx: lastHigh.anchorIdx,
          type: "short-exit", label: "空單出場", reason, price: close,
        });
        position = 0;
        positionEntryIdx = -1;
      }
    }

    /* === 進場 === */
    if (position === 0) {
      /* 多單三條件 — 用 prevState 引用上一根狀態（對齊 Pine [1]）*/
      let longReason = null;

      /* 1. 盤整突破：上一根還是 range + 紅 K + 收盤突破上頸線與前高 + 前低支撐未破 + 非過熱 */
      if (
        prevState && prevState.structure === "range" &&
        redBody &&
        prevState.upperNeck != null && prevState.lastHighPrice != null &&
        close > Math.max(prevState.upperNeck, prevState.lastHighPrice) &&
        prevState.lastLowPrice != null && prevState.prevLowPrice != null &&
        prevState.lastLowPrice >= prevState.prevLowPrice &&
        !overheatedAny
      ) {
        longReason = "盤整突破";
      }

      /* 2. 回後買上漲：站回 MA5 + 多頭結構 + 底底高 + 收盤站回月線 + 突破前一日高 + 非過熱 */
      else if (
        breakAboveMa5 && isBullTrend &&
        baseHigher && close > ma20Now && close > prevHighK &&
        !overheatedAny
      ) {
        longReason = "回後買上漲";
      }

      /* 3. 高勝率紅 K：多頭 + 紅 K + 漲幅(vs 前收) + 量增(vs 前量) + 站回 MA5 + 突破前一日高 + 非過熱 */
      else if (
        isBullTrend && redBody &&
        dailyRisePct >= p.highProb.longRisePct &&
        volumeIncreasePct >= p.highProb.longVolumePct &&
        breakAboveMa5 && close > prevHighK &&
        !overheatedAny
      ) {
        longReason = "高勝率紅 K";
      }

      if (longReason) {
        events.push({
          idx: i, confirmIdx: i, anchorIdx: lastLow.anchorIdx,
          type: "long-entry", label: "多單進場", reason: longReason, price: close,
        });
        position = 1;
        positionEntryIdx = i;
      } else {
        /* 空單三條件 */
        let shortReason = null;
        const shortVolumeOk = !p.highProb.useShortVolumeFilter
          || volumeIncreasePct >= p.highProb.shortVolumePct;

        /* 1. 盤整跌破：上一根還是 range + 黑 K + 收盤跌破下頸線與前低 + 前高壓力未破 */
        if (
          prevState && prevState.structure === "range" &&
          blackBody &&
          prevState.lowerNeck != null && prevState.lastLowPrice != null &&
          close < Math.min(prevState.lowerNeck, prevState.lastLowPrice) &&
          prevState.lastHighPrice != null && prevState.prevHighPrice != null &&
          prevState.lastHighPrice <= prevState.prevHighPrice
        ) {
          shortReason = "盤整跌破";
        }

        /* 2. 彈後破 5MA：跌破 MA5 + 空頭結構 + 頭頭低 */
        else if (breakBelowMa5 && isBearTrend && headLower) {
          shortReason = "彈後破 5MA";
        }

        /* 3. 高勝率黑 K：空頭 + 黑 K + 跌幅(vs 前收) + 量增 + 跌破 MA5 + 跌破前低 */
        else if (
          isBearTrend && blackBody &&
          dailyFallPct >= p.highProb.shortFallPct &&
          shortVolumeOk &&
          ma5Prev != null && prevClose >= ma5Prev && close < ma5Now &&
          close < prevLowK
        ) {
          shortReason = "高勝率黑 K";
        }

        if (shortReason) {
          events.push({
            idx: i, confirmIdx: i, anchorIdx: lastHigh.anchorIdx,
            type: "short-entry", label: "空單進場", reason: shortReason, price: close,
          });
          position = -1;
          positionEntryIdx = i;
        }
      }
    }

    /* 更新 prevState 給下一根用 */
    prevState = {
      structure,
      upperNeck, lowerNeck,
      lastHighPrice: lastHigh.price, prevHighPrice: prevHigh.price,
      lastLowPrice: lastLow.price, prevLowPrice: prevLow.price,
      isBullTrend, isBearTrend, isRangeState,
      overheatedAny, overheatedShort, overheatedSurge, surgeReversalConfirmed,
    };
  }

  /* 3. 等待訊號 — 在最後一根判斷，多種狀態（對齊 Pine waitText 階梯） */
  const lastIdx = data.length - 1;
  const last = data[lastIdx];
  const lastClose = last.c;
  const lastMa5 = ma5[lastIdx];
  const lastMa20 = ma20[lastIdx];
  const ps = prevState;

  if (ps && lastMa5 != null && lastMa20 != null) {
    const lastHighPrice = ps.lastHighPrice;
    const lastLowPrice = ps.lastLowPrice;

    let waitType = null;
    let waitReason = null;

    if (position === 1 && ps.surgeReversalConfirmed) {
      waitType = "wait-long";
      waitReason = "急漲爆量後急跌 — 多單出場慎防反轉";
    } else if (position === 1 && ps.overheatedShort) {
      waitType = "wait-long";
      waitReason = "短線過熱 — 持股可獲利一趟等回檔";
    } else if (position === 0 && ps.isBullTrend && ps.surgeReversalConfirmed) {
      waitType = "wait-long";
      waitReason = "急漲爆量後急跌 — 暫停追多等回檔";
    } else if (position === 0 && ps.isBullTrend && ps.overheatedSurge) {
      waitType = "wait-long";
      waitReason = "急漲爆量 — 暫停追多等回檔";
    } else if (position === 0 && ps.isBullTrend && ps.overheatedAny) {
      waitType = "wait-long";
      waitReason = "短線過熱 — 暫停追多等回檔";
    } else if (
      position === 0 && ps.isBullTrend &&
      lastClose < lastMa5 && lastLowPrice != null && lastClose >= lastLowPrice
    ) {
      waitType = "wait-long";
      waitReason = "回檔不破前低 — 等收盤站回 5MA";
    } else if (
      position === 0 && ps.structure === "range" &&
      ps.upperNeck != null && lastClose <= ps.upperNeck
    ) {
      waitType = "wait-long";
      waitReason = "等紅 K 收盤突破上頸線";
    } else if (
      position === 0 && ps.isBearTrend &&
      lastClose > lastMa5 && lastHighPrice != null && lastClose <= lastHighPrice
    ) {
      waitType = "wait-short";
      waitReason = "反彈不過前高 — 等再跌破 5MA";
    } else if (
      position === 0 && ps.structure === "range" &&
      ps.lowerNeck != null && lastClose >= ps.lowerNeck
    ) {
      waitType = "wait-short";
      waitReason = "等黑 K 收盤跌破下頸線";
    }

    if (waitType) {
      events.push({ idx: lastIdx, type: waitType, label: waitType === "wait-long" ? "等待多單" : "等待空單", reason: waitReason });
    }
  }

  return events;
}

window.KCStructure = { compute, sma, KC_DEFAULT_PARAMS };
