/**
 * Fundamentals Service
 * Fetches deep trader metrics from Yahoo Finance using authenticated crumb requests
 * Calculates RSI-14, MACD (12,26,9), SMAs from OHLCV data
 */

import NodeCache from 'node-cache';
import { classifyCapTier } from './stockUniverseService.js';
import { yfFetchAuth } from './yahooCrumb.js';

const fundamentalsCache = new NodeCache({ stdTTL: 60 });
const technicalCache = new NodeCache({ stdTTL: 300 });
const quoteSummaryCache = new NodeCache({ stdTTL: 120 });

const YF_BASE = 'https://query1.finance.yahoo.com';

/**
 * Fetch basic quote (price, volume, change, market cap)
 */
export async function fetchBasicQuote(symbol) {
  const cacheKey = `quote_${symbol}`;
  const cached = fundamentalsCache.get(cacheKey);
  if (cached) return cached;

  const url = `${YF_BASE}/v8/finance/chart/${symbol}?interval=1d&range=5d`;

  try {
    const data = await yfFetchAuth(url);
    const result = data?.chart?.result?.[0];
    if (!result) return null;

    const meta = result.meta;
    const quotes = result.indicators?.quote?.[0] || {};
    const timestamps = result.timestamp || [];

    const currentPrice = meta.regularMarketPrice ?? meta.previousClose ?? null;
    const prevClose = meta.previousClose ?? null;
    const change = currentPrice && prevClose ? currentPrice - prevClose : null;
    const changePct = change && prevClose ? (change / prevClose) * 100 : null;

    const history = [];
    const startIdx = Math.max(0, timestamps.length - 5);
    for (let i = startIdx; i < timestamps.length; i++) {
      const v = quotes.volume?.[i];
      const c = quotes.close?.[i];
      if (c != null) {
        history.push({
          date: new Date(timestamps[i] * 1000).toISOString().split('T')[0],
          open: quotes.open?.[i] ?? null,
          high: quotes.high?.[i] ?? null,
          low: quotes.low?.[i] ?? null,
          close: c,
          volume: v ?? null,
        });
      }
    }
    history.sort((a, b) => new Date(b.date) - new Date(a.date));

    const q = {
      symbol: symbol.toUpperCase(),
      price: currentPrice,
      prev_close: prevClose,
      change,
      change_pct: changePct ? Math.round(changePct * 100) / 100 : null,
      open: meta.regularMarketOpen ?? null,
      high: meta.regularMarketDayHigh ?? null,
      low: meta.regularMarketDayLow ?? null,
      volume: meta.regularMarketVolume ?? null,
      market_cap: meta.marketCap ?? null,
      currency: meta.currency ?? 'USD',
      exchange: meta.exchangeName ?? null,
      market_state: meta.marketState ?? null,
      history,
    };

    fundamentalsCache.set(cacheKey, q);
    return q;
  } catch (err) {
    console.warn(`[fundamentalsService] quote failed for ${symbol}:`, err.message);
    return null;
  }
}

/**
 * Fetch macro performance (1D, 1W, 1M, 1Y)
 */
export async function fetchMacroPerformance(symbol) {
  const cacheKey = `macro_perf_${symbol}`;
  const cached = fundamentalsCache.get(cacheKey);
  if (cached) return cached;

  const url = `${YF_BASE}/v8/finance/chart/${symbol}?interval=1d&range=1y`;

  try {
    const data = await yfFetchAuth(url);
    const result = data?.chart?.result?.[0];
    if (!result) return null;

    const closes = result.indicators?.quote?.[0]?.close?.filter(v => v != null) || [];
    if (closes.length === 0) return null;

    const currentPrice = closes[closes.length - 1];
    
    // Calculate returns safely based on available data length
    const getRet = (daysBack) => {
      if (closes.length <= daysBack) return null;
      const oldPrice = closes[closes.length - 1 - daysBack];
      if (!oldPrice) return null;
      return ((currentPrice - oldPrice) / oldPrice) * 100;
    };

    const q = {
      symbol: symbol.toUpperCase(),
      price: currentPrice,
      change_pct: getRet(1),
      change_1w: getRet(5),
      change_1m: getRet(21),
      change_1y: getRet(closes.length - 1),
    };

    fundamentalsCache.set(cacheKey, q);
    return q;
  } catch (err) {
    console.warn(`[fundamentalsService] macro perf failed for ${symbol}:`, err.message);
    return null;
  }
}

/**
 * Fetch full fundamentals via quoteSummary v10
 */
export async function fetchQuoteSummary(symbol) {
  const cacheKey = `qs_${symbol}`;
  const cached = quoteSummaryCache.get(cacheKey);
  if (cached) return cached;

  const modules = [
    'summaryDetail',
    'defaultKeyStatistics',
    'financialData',
    'recommendationTrend',
    'earningsTrend'
  ].join(',');

  const url = `${YF_BASE}/v10/finance/quoteSummary/${symbol}?modules=${modules}`;
  
  try {
    const data = await yfFetchAuth(url);
    const result = data?.quoteSummary?.result?.[0];
    if (!result) return null;

    const sd = result.summaryDetail || {};
    const dks = result.defaultKeyStatistics || {};
    const fd = result.financialData || {};
    const rt = result.recommendationTrend?.trend?.[0] || {};

    const parsed = {
      pe_ttm: sd.trailingPE?.raw ?? null,
      pe_forward: sd.forwardPE?.raw ?? null,
      price_to_book: dks.priceToBook?.raw ?? null,
      price_to_sales: dks.priceToSalesTrailing12Months?.raw ?? null,
      ev_ebitda: dks.enterpriseToEbitda?.raw ?? null,
      eps_ttm: dks.trailingEps?.raw ?? null,
      eps_forward: dks.forwardEps?.raw ?? null,
      earnings_growth: fd.earningsGrowth?.raw ?? null,
      revenue_growth: fd.revenueGrowth?.raw ?? null,
      gross_margins: fd.grossMargins?.raw ?? null,
      ebitda_margins: fd.ebitdaMargins?.raw ?? null,
      operating_margins: fd.operatingMargins?.raw ?? null,
      profit_margins: dks.profitMargins?.raw ?? null,
      debt_to_equity: fd.debtToEquity?.raw ?? null,
      current_ratio: fd.currentRatio?.raw ?? null,
      quick_ratio: fd.quickRatio?.raw ?? null,
      return_on_equity: fd.returnOnEquity?.raw ?? null,
      return_on_assets: fd.returnOnAssets?.raw ?? null,
      free_cash_flow: fd.freeCashflow?.raw ?? null,
      total_revenue: fd.totalRevenue?.raw ?? null,
      total_debt: fd.totalDebt?.raw ?? null,
      beta: sd.beta?.raw ?? null,
      market_cap: sd.marketCap?.raw ?? null,
      shares_outstanding: dks.sharesOutstanding?.raw ?? null,
      float_shares: dks.floatShares?.raw ?? null,
      short_pct_float: dks.shortPercentOfFloat?.raw ?? null,
      shares_short: dks.sharesShort?.raw ?? null,
      held_pct_institutions: dks.heldPercentInstitutions?.raw ?? null,
      held_pct_insiders: dks.heldPercentInsiders?.raw ?? null,
      book_value: dks.bookValue?.raw ?? null,
      week52_high: sd.fiftyTwoWeekHigh?.raw ?? null,
      week52_low: sd.fiftyTwoWeekLow?.raw ?? null,
      week52_change: dks.fiftyTwoWeekChange?.raw ?? null,
      avg_volume_10d: sd.averageVolume10days?.raw ?? null,
      avg_volume_3m: sd.averageVolume?.raw ?? null,
      dividend_yield: sd.dividendYield?.raw ?? null,
      payout_ratio: sd.payoutRatio?.raw ?? null,
      target_high: fd.targetHighPrice?.raw ?? null,
      target_low: fd.targetLowPrice?.raw ?? null,
      target_mean: fd.targetMeanPrice?.raw ?? null,
      target_median: fd.targetMedianPrice?.raw ?? null,
      recommendation: fd.recommendationKey ?? null,
      analyst_count: fd.numberOfAnalystOpinions?.raw ?? null,
      rec_strong_buy: rt.strongBuy ?? 0,
      rec_buy: rt.buy ?? 0,
      rec_hold: rt.hold ?? 0,
      rec_sell: rt.sell ?? 0,
      rec_strong_sell: rt.strongSell ?? 0,
      capTier: classifyCapTier(sd.marketCap?.raw ?? null),
    };

    quoteSummaryCache.set(cacheKey, parsed);
    return parsed;
  } catch (err) {
    console.warn(`[fundamentalsService] quoteSummary failed for ${symbol}:`, err.message);
    return null;
  }
}

/**
 * Fetch OHLCV history and calculate technical indicators
 */
export async function fetchTechnicals(symbol) {
  const cacheKey = `tech_${symbol}`;
  const cached = technicalCache.get(cacheKey);
  if (cached) return cached;

  const url = `${YF_BASE}/v8/finance/chart/${symbol}?interval=1d&range=1y`;

  try {
    const data = await yfFetchAuth(url);
    const result = data?.chart?.result?.[0];
    if (!result) return null;

    const closes = result.indicators?.quote?.[0]?.close?.filter(v => v != null) || [];
    if (closes.length < 26) return null;
    
    // Extract candles for ATR
    const quote = result.indicators?.quote?.[0] || {};
    const candles = [];
    for (let i = 0; i < closes.length; i++) {
      if (quote.high?.[i] != null && quote.low?.[i] != null && quote.close?.[i] != null) {
        candles.push({
          high: quote.high[i],
          low: quote.low[i],
          close: quote.close[i]
        });
      }
    }

    const rsi = calculateRSI(closes, 14);
    const macd = calculateMACD(closes, 12, 26, 9);
    const sma20 = closes.length >= 20 ? avg(closes.slice(-20)) : null;
    const sma50 = closes.length >= 50 ? avg(closes.slice(-50)) : null;
    const sma200 = closes.length >= 200 ? avg(closes.slice(-200)) : null;
    const currentPrice = closes[closes.length - 1];

    const result_data = {
      candles, // Pass candles to featureService
      rsi_14: rsi !== null ? Math.round(rsi * 100) / 100 : null,
      macd_line: macd?.line ?? null,
      macd_signal: macd?.signal ?? null,
      macd_histogram: macd?.histogram ?? null,
      sma_20: sma20 ? Math.round(sma20 * 100) / 100 : null,
      sma_50: sma50 ? Math.round(sma50 * 100) / 100 : null,
      sma_200: sma200 ? Math.round(sma200 * 100) / 100 : null,
      above_sma50: sma50 ? currentPrice > sma50 : null,
      above_sma200: sma200 ? currentPrice > sma200 : null,
      pct_from_sma50: sma50 ? ((currentPrice - sma50) / sma50 * 100) : null,
      pct_from_sma200: sma200 ? ((currentPrice - sma200) / sma200 * 100) : null,
    };

    technicalCache.set(cacheKey, result_data);
    return result_data;
  } catch (err) {
    console.warn(`[fundamentalsService] technicals failed for ${symbol}:`, err.message);
    return null;
  }
}

/**
 * Full deep analysis for a single symbol
 */
export async function fetchFullAnalysis(symbol) {
  const [quote, fundamentals, technicals] = await Promise.allSettled([
    fetchBasicQuote(symbol),
    fetchQuoteSummary(symbol),
    fetchTechnicals(symbol),
  ]);

  return {
    symbol: symbol.toUpperCase(),
    quote: quote.status === 'fulfilled' ? quote.value : null,
    fundamentals: fundamentals.status === 'fulfilled' ? fundamentals.value : null,
    technicals: technicals.status === 'fulfilled' ? technicals.value : null,
    fetchedAt: new Date().toISOString(),
  };
}

// ─── Technical Indicator Calculations ───────────────────────────────────────

function avg(arr) {
  return arr.reduce((a, b) => a + b, 0) / arr.length;
}

function calculateRSI(closes, period = 14) {
  if (closes.length < period + 1) return null;
  const recent = closes.slice(-period - 1);
  let gains = 0, losses = 0;

  for (let i = 1; i <= period; i++) {
    const diff = recent[i] - recent[i - 1];
    if (diff >= 0) gains += diff;
    else losses += Math.abs(diff);
  }

  const avgGain = gains / period;
  const avgLoss = losses / period;

  if (avgLoss === 0) return 100;
  const rs = avgGain / avgLoss;
  return 100 - (100 / (1 + rs));
}

function ema(data, period) {
  if (data.length < period) return [];
  const k = 2 / (period + 1);
  const result = [];
  let emaVal = avg(data.slice(0, period));
  result.push(emaVal);
  for (let i = period; i < data.length; i++) {
    emaVal = data[i] * k + emaVal * (1 - k);
    result.push(emaVal);
  }
  return result;
}

function calculateMACD(closes, fast = 12, slow = 26, signal = 9) {
  if (closes.length < slow + signal) return null;
  const emaFast = ema(closes, fast);
  const emaSlow = ema(closes, slow);

  const diff = emaSlow.length - emaFast.length;
  const macdLine = emaFast.slice(diff).map((v, i) => v - emaSlow[i]);
  const signalLine = ema(macdLine, signal);

  const lastMacd = macdLine[macdLine.length - 1];
  const lastSignal = signalLine[signalLine.length - 1];

  return {
    line: Math.round(lastMacd * 10000) / 10000,
    signal: Math.round(lastSignal * 10000) / 10000,
    histogram: Math.round((lastMacd - lastSignal) * 10000) / 10000,
  };
}
