import { atrPercent, estimateRevisionsNet, calculateRvol } from './featureService.js';

/**
 * Scoring Engine
 * Generates testable signals.
 */

function gradeLabel(score) {
  if (score >= 80) return { label: 'A', color: 'emerald' };
  if (score >= 60) return { label: 'B', color: 'blue' };
  if (score >= 40) return { label: 'C', color: 'yellow' };
  if (score >= 20) return { label: 'D', color: 'orange' };
  return { label: 'F', color: 'red' };
}

/**
 * Score a single stock entry
 * @returns {Object} { daytrade, shortTerm, longTerm } where each is { score, grade, drivers, dataAgeSec, stale, ... }
 */
export function scoreSuitability({ quote, technicals, fundamentals, regime, dataAgeSec }) {
  const q = quote || {};
  const t = technicals || {};
  const f = fundamentals || {};
  const stale = dataAgeSec > 90; // stale if older than 90s

  // Compute raw features
  const rvol = calculateRvol(q.volume, f.avg_volume_10d, f.avg_volume_3m);
  const atrPct = t.candles ? atrPercent(t.candles, 14) : null;
  const revisionsNet = estimateRevisionsNet(f);
  
  const rsi = t.rsi_14 ?? null;
  const macdH = t.macd_histogram ?? null;
  const pctSma50 = t.pct_from_sma50 ?? null;
  const above200 = t.above_sma200 ?? null;
  const pctSma200 = t.pct_from_sma200 ?? null;
  const eg = f.earnings_growth ?? null;
  const pm = f.profit_margins ?? null;
  
  // ─── Daytrade Score (Logistic Regression) ───────────────────────────────────
  // Weights from Phase 2 training (features: rsi/100, atrPct/10, rvol/5, macd/2, regime)
  const dtParts = [];
  
  // Safe values for dot product
  const safeRsi = rsi ?? 50;
  const safeAtr = atrPct ?? 1.0;
  const safeRvol = rvol ?? 1.0;
  const safeMacd = macdH ?? 0.0;
  const regimeFlag = regime?.trend === 'risk_on' ? 1 : 0;

  // Log-odds calculation (z)
  const intercept = -84.54;
  const wRsi = -50.36 * (safeRsi / 100);
  const wAtr = 13.90 * (safeAtr / 10);
  const wRvol = 37.27 * (safeRvol / 5);
  const wMacd = -395.0 * (safeMacd / 2);
  const wRegime = 41.80 * regimeFlag;

  const z = intercept + wRsi + wAtr + wRvol + wMacd + wRegime;
  
  // Sigmoid
  const pWin = 1 / (1 + Math.exp(-z));
  
  // We use P(win) * 100 as the "score" for sorting, but we'll export it cleanly
  const dtScore = Math.round(pWin * 100);

  // Expose feature importance in the drivers for the UI
  dtParts.push({ key: 'rsi', label: 'RSI Impact', value: safeRsi, points: Math.round(wRsi), desc: `Oversold effect (w: ${wRsi.toFixed(1)})` });
  dtParts.push({ key: 'atrPct', label: 'Volatility Impact', value: safeAtr, points: Math.round(wAtr), desc: `ATR push (w: ${wAtr.toFixed(1)})` });
  dtParts.push({ key: 'rvol', label: 'Volume Impact', value: safeRvol, points: Math.round(wRvol), desc: `Relative Vol (w: ${wRvol.toFixed(1)})` });
  dtParts.push({ key: 'macd', label: 'Trend Impact', value: safeMacd, points: Math.round(wMacd), desc: `MACD (w: ${wMacd.toFixed(1)})` });
  if (regimeFlag) dtParts.push({ key: 'regime', label: 'Regime Impact', value: 1, points: Math.round(wRegime), desc: `Risk-On Boost` });


  // ─── Short Term (Swing) Score ─────────────────────────────────────────────
  const stParts = [];

  let stMacdPts = 0, stMacdLbl = 'MACD neutral';
  if (macdH != null) {
    if (macdH > 0.5)       { stMacdPts = 30; stMacdLbl = `Strong MACD bullish`; }
    else if (macdH > 0)    { stMacdPts = 22; stMacdLbl = `MACD bullish`; }
    else if (macdH > -0.2) { stMacdPts = 10; stMacdLbl = `MACD near crossover`; }
  }
  stParts.push({ key: 'macd', label: 'MACD setup', value: macdH, points: stMacdPts, desc: stMacdLbl });

  let stRsiPts = 0, stRsiLbl = 'RSI extended';
  if (rsi != null) {
    if (rsi >= 40 && rsi <= 60)   { stRsiPts = 25; stRsiLbl = `RSI ${rsi.toFixed(0)} (swing zone)`; }
    else if (rsi >= 30 && rsi < 40) { stRsiPts = 20; stRsiLbl = `RSI ${rsi.toFixed(0)} (reset/dip)`; }
    else if (rsi > 60 && rsi <= 70) { stRsiPts = 15; stRsiLbl = `RSI ${rsi.toFixed(0)} (momentum)`; }
    else if (rsi <= 30)             { stRsiPts = 10; stRsiLbl = `RSI oversold`; }
    else                            { stRsiPts = 2; }
  }
  stParts.push({ key: 'rsi', label: 'RSI sweet spot', value: rsi, points: stRsiPts, desc: stRsiLbl });

  let stSmaPts = 0, stSmaLbl = 'Extended from SMA50';
  if (pctSma50 != null) {
    const abs = Math.abs(pctSma50);
    if (abs <= 3)       { stSmaPts = 25; stSmaLbl = `Near SMA50 (${pctSma50.toFixed(1)}%) - coiled`; }
    else if (abs <= 7)  { stSmaPts = 18; stSmaLbl = `${pctSma50 > 0 ? 'Above' : 'Below'} SMA50 by ${abs.toFixed(1)}%`; }
    else if (abs <= 15) { stSmaPts = 8;  stSmaLbl = `${abs.toFixed(1)}% from SMA50`; }
  }
  stParts.push({ key: 'dist50', label: 'SMA50 proximity', value: pctSma50, points: stSmaPts, desc: stSmaLbl });

  // Revisions (Replacing Analyst Targets)
  let revPts = 0, revLbl = 'Revisions neutral';
  if (revisionsNet > 0) {
    if (revisionsNet >= 5)  { revPts = 20; revLbl = `Strong upward revisions`; }
    else if (revisionsNet >= 2) { revPts = 14; revLbl = `Net positive revisions`; }
    else { revPts = 8; revLbl = `Slight upward revisions`; }
  } else if (revisionsNet < 0) {
    revLbl = `Negative revisions`;
  }
  stParts.push({ key: 'revisions', label: 'Estimate revisions', value: revisionsNet, points: revPts, desc: revLbl });

  const stScore = Math.min(100, Math.round(stParts.reduce((s, p) => s + p.points, 0)));

  // ─── Long Term Score ──────────────────────────────────────────────────────
  const ltParts = [];

  let ltSmaPts = 0, ltSmaLbl = 'Below SMA200';
  if (above200 != null) {
    if (above200 && pctSma200 != null && pctSma200 > 10) { ltSmaPts = 30; ltSmaLbl = `${pctSma200.toFixed(0)}% above SMA200`; }
    else if (above200)                                   { ltSmaPts = 22; ltSmaLbl = `Above SMA200`; }
    else if (pctSma200 != null && pctSma200 > -5)        { ltSmaPts = 10; ltSmaLbl = `Near SMA200 support`; }
  }
  ltParts.push({ key: 'dist200', label: 'Macro trend', value: pctSma200, points: ltSmaPts, desc: ltSmaLbl });

  let egPts = 0, egLbl = 'Negative growth';
  if (eg != null) {
    if (eg >= 0.30)      { egPts = 25; egLbl = `${(eg * 100).toFixed(0)}% earnings growth`; }
    else if (eg >= 0.15) { egPts = 18; egLbl = `${(eg * 100).toFixed(0)}% earnings growth`; }
    else if (eg >= 0.05) { egPts = 11; egLbl = `${(eg * 100).toFixed(0)}% growth`; }
    else if (eg >= 0)    { egPts = 5;  egLbl = `Flat growth`; }
  }
  ltParts.push({ key: 'epsGrowth', label: 'Earnings power', value: eg, points: egPts, desc: egLbl });

  const totalRec = (f.rec_strong_buy || 0) + (f.rec_buy || 0) + (f.rec_hold || 0) + (f.rec_sell || 0) + (f.rec_strong_sell || 0);
  const bullishRec = (f.rec_strong_buy || 0) + (f.rec_buy || 0);
  let recPts = 0, recLbl = 'Bearish consensus';
  if (totalRec > 0) {
    const bullPct = bullishRec / totalRec;
    if (bullPct >= 0.75)      { recPts = 25; recLbl = `${(bullPct * 100).toFixed(0)}% bullish`; }
    else if (bullPct >= 0.55) { recPts = 16; recLbl = `${(bullPct * 100).toFixed(0)}% bullish`; }
    else if (bullPct >= 0.40) { recPts = 8;  recLbl = `Mixed consensus`; }
  }
  ltParts.push({ key: 'conviction', label: 'Analyst conviction', value: bullishRec/totalRec, points: recPts, desc: recLbl });

  let pmPts = 0, pmLbl = 'Unprofitable';
  if (pm != null) {
    if (pm >= 0.25)      { pmPts = 20; pmLbl = `${(pm * 100).toFixed(0)}% margin`; }
    else if (pm >= 0.15) { pmPts = 14; pmLbl = `${(pm * 100).toFixed(0)}% margin`; }
    else if (pm >= 0.05) { pmPts = 7;  pmLbl = `${(pm * 100).toFixed(0)}% margin`; }
    else if (pm >= 0)    { pmPts = 2;  pmLbl = `Thin margin`; }
  }
  ltParts.push({ key: 'margin', label: 'Profit durability', value: pm, points: pmPts, desc: pmLbl });

  const ltScore = Math.min(100, Math.round(ltParts.reduce((s, p) => s + p.points, 0)));

  // Raw features map for logging
  const featuresLog = {
    rvol, atrPct, rsi, macdHist: macdH, dist50: pctSma50, dist200: pctSma200,
    revisions: revisionsNet, epsGrowth: eg, margin: pm,
    beta: q.beta
  };

  return {
    daytrade: {
      score: dtScore,
      grade: gradeLabel(dtScore),
      probability: pWin,
      drivers: dtParts.sort((a, b) => b.points - a.points),
      dataAgeSec,
      stale,
      features: featuresLog
    },
    shortTerm: {
      score: stScore,
      grade: gradeLabel(stScore),
      drivers: stParts.sort((a, b) => b.points - a.points),
      dataAgeSec,
      stale,
      features: featuresLog
    },
    longTerm: {
      score: ltScore,
      grade: gradeLabel(ltScore),
      drivers: ltParts.sort((a, b) => b.points - a.points),
      dataAgeSec,
      stale,
      features: featuresLog
    },
  };
}

export function calculateRocketScore(row) {
  let score = 0;

  // Premium tier (0-3 points)
  if (row.premium_num >= 2000000) score += 3.0;
  else if (row.premium_num >= 800000) score += 2.0;
  else if (row.premium_num >= 200000) score += 1.0;

  // Net premium imbalance (-1.5 to +1.5 points)
  const totalPrem = row.bull_total + row.bear_total;
  if (totalPrem > 0) {
    score += 1.5 * ((row.bull_total - row.bear_total) / totalPrem);
  }

  // Vol > OI (1.2 points)
  if (row.vol_num > row.oi_num) score += 1.2;

  // Session weight (0-1 point)
  const sessionWeights = { RTH: 1.0, POST: 0.5, PRE: 0.3, OFF: 0 };
  score += sessionWeights[row.session_bucket] || 0;

  // Catalyst flag (1 point)
  if (row.near_alert_type) score += 1.0;

  // OTM bias (0.8 points)
  const isOTM = 
    (row.cp_norm === 'CALL' && row.strike_num > row.spot_num) ||
    (row.cp_norm === 'PUT' && row.strike_num < row.spot_num);
  if (isOTM) score += 0.8;

  // Tape alignment (0.5 points)
  if (row.tape_alignment === 1) score += 0.5;

  return parseFloat(score.toFixed(2));
}
