/* 技術指標計算 — 純函式
 *
 * window.Indicators = {
 *   ema(values, period)                 → array(null|number)
 *   macd(candles, fast=12, slow=26, sig=9) → { macd, signal, hist }
 *   rsi(candles, period=14)             → array(null|number)
 *   kd(candles, k=9, d=3, smoothK=3)    → { k, d }
 *   bbands(candles, period=20, mult=2)  → { upper, middle, lower }
 * }
 */

function ema(values, period) {
  const out = new Array(values.length).fill(null);
  if (values.length < period) return out;
  const k = 2 / (period + 1);
  let sum = 0;
  for (let i = 0; i < period; i++) sum += values[i];
  out[period - 1] = sum / period;
  for (let i = period; i < values.length; i++) {
    out[i] = values[i] * k + out[i - 1] * (1 - k);
  }
  return out;
}

function macd(candles, fast = 12, slow = 26, sig = 9) {
  const closes = candles.map((c) => c.c);
  const emaFast = ema(closes, fast);
  const emaSlow = ema(closes, slow);
  const macdLine = closes.map((_, i) =>
    emaFast[i] != null && emaSlow[i] != null ? emaFast[i] - emaSlow[i] : null
  );
  /* signal = ema of macdLine (跳過 null 起點) */
  const macdNonNull = macdLine.map((v) => (v == null ? 0 : v));
  const signalRaw = ema(macdNonNull, sig);
  const signal = macdLine.map((v, i) => (v == null ? null : signalRaw[i]));
  const hist = macdLine.map((v, i) =>
    v != null && signal[i] != null ? v - signal[i] : null
  );
  return { macd: macdLine, signal, hist };
}

function rsi(candles, period = 14) {
  const out = new Array(candles.length).fill(null);
  if (candles.length < period + 1) return out;
  let gains = 0, losses = 0;
  for (let i = 1; i <= period; i++) {
    const d = candles[i].c - candles[i - 1].c;
    if (d >= 0) gains += d; else losses -= d;
  }
  let avgG = gains / period;
  let avgL = losses / period;
  out[period] = avgL === 0 ? 100 : 100 - 100 / (1 + avgG / avgL);
  for (let i = period + 1; i < candles.length; i++) {
    const d = candles[i].c - candles[i - 1].c;
    if (d >= 0) {
      avgG = (avgG * (period - 1) + d) / period;
      avgL = (avgL * (period - 1)) / period;
    } else {
      avgG = (avgG * (period - 1)) / period;
      avgL = (avgL * (period - 1) + (-d)) / period;
    }
    out[i] = avgL === 0 ? 100 : 100 - 100 / (1 + avgG / avgL);
  }
  return out;
}

function kd(candles, period = 9, dPeriod = 3, smoothK = 3) {
  const len = candles.length;
  const rsv = new Array(len).fill(null);
  for (let i = period - 1; i < len; i++) {
    let hh = -Infinity, ll = Infinity;
    for (let j = i - period + 1; j <= i; j++) {
      if (candles[j].h > hh) hh = candles[j].h;
      if (candles[j].l < ll) ll = candles[j].l;
    }
    const denom = hh - ll;
    rsv[i] = denom === 0 ? 50 : ((candles[i].c - ll) / denom) * 100;
  }
  /* %K = SMA(rsv, smoothK)；%D = SMA(%K, dPeriod) — 台股慣用 SMA 而非 EMA */
  const sma = (arr, p) => {
    const o = new Array(arr.length).fill(null);
    let sum = 0, cnt = 0;
    const buf = [];
    for (let i = 0; i < arr.length; i++) {
      const v = arr[i];
      if (v == null) { buf.push(null); continue; }
      buf.push(v);
      sum += v; cnt++;
      if (cnt > p) { sum -= buf[buf.length - p - 1]; cnt = p; }
      if (cnt === p) o[i] = sum / p;
    }
    return o;
  };
  const k = sma(rsv, smoothK);
  const d = sma(k, dPeriod);
  return { k, d };
}

function bbands(candles, period = 20, mult = 2) {
  const len = candles.length;
  const middle = new Array(len).fill(null);
  const upper = new Array(len).fill(null);
  const lower = new Array(len).fill(null);
  for (let i = period - 1; i < len; i++) {
    let sum = 0;
    for (let j = i - period + 1; j <= i; j++) sum += candles[j].c;
    const m = sum / period;
    let v = 0;
    for (let j = i - period + 1; j <= i; j++) {
      const d = candles[j].c - m;
      v += d * d;
    }
    const sd = Math.sqrt(v / period);
    middle[i] = m;
    upper[i] = m + mult * sd;
    lower[i] = m - mult * sd;
  }
  return { upper, middle, lower };
}

window.Indicators = { ema, macd, rsi, kd, bbands };
