// live.jsx — fetch real market data and run pattern detection over it
//
// Sources:
//   • "yahoo": self-hosted Yahoo proxy at /api/yahoo (batched, server-side
//     cookie+crumb auth, disk-cached, market-hours-aware refresh)
//   • "proxy": Finnhub proxy at /api/quote + /api/candles (requires paid
//     tier for candles — kept as fallback)
//
// Produces a stocks[] array shaped like buildStocks() returns, plus
// metadata { dataAge, stale, nextPollMs } from the server.

const LIVE_BATCH_SIZE = 12;     // tickers per parallel wave (proxy mode)
const LIVE_BATCH_DELAY = 800;   // ms between waves (proxy mode)

// ── Yahoo batch fetcher (preferred) ───────────────────────────

async function fetchAllYahoo(tickers, onProgress) {
  const symbols = tickers.map(t => t.sym).join(',');
  const res = await fetch(`/api/yahoo?symbols=${encodeURIComponent(symbols)}`);
  if (!res.ok) throw new Error(`/api/yahoo ${res.status}`);
  const json = await res.json();

  const results = [];
  const errors = [];

  for (const ticker of tickers) {
    const d = json.data?.[ticker.sym];
    if (!d || !d.series || d.series.length < 30) {
      errors.push({ sym: ticker.sym, err: 'insufficient data' });
      continue;
    }

    const stock = buildStockFromData(ticker, d);
    results.push(stock);
    onProgress?.({ done: results.length + errors.length, total: tickers.length });
  }

  const withSignal = results.filter(s => s.patternKey !== 'NO_SIGNAL');
  return {
    stocks: withSignal.length ? withSignal : results,
    errors,
    source: 'yahoo',
    dataAge: json.dataAge,
    stale: json.stale || false,
    nextPollMs: json.nextPollMs || 60000,
  };
}

// ── Finnhub proxy fetcher (fallback) ──────────────────────────

async function fetchProxy(sym) {
  const [q, c] = await Promise.all([
    fetch(`/api/quote?symbol=${sym}`).then(r => r.ok ? r.json() : Promise.reject(r.status)),
    fetch(`/api/candles?symbol=${sym}&days=90`).then(r => r.ok ? r.json() : Promise.reject(r.status)),
  ]);
  if (c.s !== 'ok') throw new Error('candle status ' + c.s);
  return {
    series: c.c, volume: c.v,
    px: q.c, prevClose: q.pc,
  };
}

// ── shared: build a stock entry from { series, volume, px, prevClose } ──

function buildStockFromData(ticker, data) {
  const ohlc = (data.open && data.high && data.low)
    ? { open: data.open, high: data.high, low: data.low, weekly: data.weekly || null }
    : null;
  const detected = window.detectPattern(data.series, data.volume, ohlc);
  const KEY_MAP = { TRIANGLE: 'SYM_TRIANGLE' };
  const rawKey = detected?.key || 'NO_SIGNAL';
  const patternKey = KEY_MAP[rawKey] || rawKey;
  const pattern = window.PATTERNS[patternKey] || {
    name: 'No Signal', dir: 'WATCH', success: 0, target: 0,
    desc: 'No high-confidence pattern detected on current window.',
  };

  const last = data.px ?? data.series.at(-1);
  const change = ((last - data.prevClose) / data.prevClose) * 100;
  const avgVol30 = data.volume.slice(-30).reduce((a,b)=>a+b,0) / Math.min(30, data.volume.length);
  const lastVol = data.volume.at(-1) || avgVol30;
  const volRatio = lastVol / (avgVol30 || 1);

  const entry = last;
  const stop = pattern.dir === 'BUY' ? entry * 0.97 : pattern.dir === 'SELL' ? entry * 1.03 : entry;
  const target = pattern.dir === 'BUY' ? entry * 1.06 : pattern.dir === 'SELL' ? entry * 0.94 : entry;

  return {
    ...ticker,
    px: last,
    change,
    series: data.series,
    volume: data.volume,
    avgVol30, volRatio,
    patternKey,
    pattern,
    confidence: detected?.confidence ?? 0,
    evidence: detected?.evidence ?? [],
    entry, stop, target,
    rr: 2.0,
    live: true,
  };
}

// ── single-symbol fetch (proxy mode, kept for Finnhub fallback) ──

async function fetchOne(ticker, source) {
  const data = await fetchProxy(ticker.sym);
  if (!data.series || data.series.length < 30) throw new Error('insufficient series');
  return buildStockFromData(ticker, data);
}

// ── orchestrator ──────────────────────────────────────────────

async function buildStocksLive(source = 'yahoo', onProgress) {
  const tickers = window.TICKERS;

  if (source === 'yahoo') {
    return fetchAllYahoo(tickers, onProgress);
  }

  // proxy (Finnhub) path — per-symbol batching
  const results = [];
  const errors = [];

  for (let i = 0; i < tickers.length; i += LIVE_BATCH_SIZE) {
    const batch = tickers.slice(i, i + LIVE_BATCH_SIZE);
    const settled = await Promise.allSettled(batch.map(t => fetchOne(t, source)));
    settled.forEach((r, j) => {
      if (r.status === 'fulfilled') results.push(r.value);
      else errors.push({ sym: batch[j].sym, err: String(r.reason) });
    });
    onProgress?.({ done: results.length + errors.length, total: tickers.length });
    if (i + LIVE_BATCH_SIZE < tickers.length) {
      await new Promise(res => setTimeout(res, LIVE_BATCH_DELAY));
    }
  }

  const withSignal = results.filter(s => s.patternKey !== 'NO_SIGNAL');
  return {
    stocks: withSignal.length ? withSignal : results,
    errors,
    source,
    dataAge: 0,
    stale: false,
    nextPollMs: 60000,
  };
}

window.buildStocksLive = buildStocksLive;
window.fetchOne = fetchOne;
