/**
 * Normal CDF using rational approximation (Abramowitz & Stegun).
 */
function normalCDF(x, mean = 0, stddev = 1) {
  if (stddev <= 0) return x >= mean ? 1 : 0;
  const z = (x - mean) / stddev;
  const t = 1 / (1 + 0.2316419 * Math.abs(z));
  const d = 0.3989422804014327; // 1/sqrt(2*pi)
  const p = d * Math.exp(-z * z / 2) *
    (t * (0.3193815 + t * (-0.3565638 + t * (1.781478 + t * (-1.8212560 + t * 1.3302744)))));
  return z > 0 ? 1 - p : p;
}

/**
 * Calculate model probability for a prop line using normal distribution.
 * @param {number} mean - Projected mean
 * @param {number} stddev - Standard deviation
 * @param {number} line - The prop line
 * @param {string} direction - 'over' or 'under'
 * @returns {number} Probability 0-1
 */
function calculateModelProbability(mean, stddev, line, direction) {
  if (stddev <= 0) {
    if (direction === 'over') return mean > line ? 1 : 0;
    return mean < line ? 1 : 0;
  }

  const cdf = normalCDF(line, mean, stddev);
  return direction === 'over' ? 1 - cdf : cdf;
}

/**
 * Convert American odds to implied probability.
 * @param {number} odds - American odds (e.g. -110, +150)
 * @returns {number} Implied probability 0-1
 */
function americanToImplied(odds) {
  if (odds < 0) return Math.abs(odds) / (Math.abs(odds) + 100);
  return 100 / (odds + 100);
}

/**
 * Compare model probability to book implied probability.
 * @param {number} modelProb - Model-calculated probability
 * @param {number} bookOdds - American odds from the book
 * @returns {object} { model_prob, book_implied, edge, value_detected }
 */
function compareToBookImplied(modelProb, bookOdds) {
  const bookImplied = americanToImplied(bookOdds);
  const edge = modelProb - bookImplied;

  return {
    model_prob: Math.round(modelProb * 1000) / 1000,
    book_implied: Math.round(bookImplied * 1000) / 1000,
    edge: Math.round(edge * 1000) / 1000,
    value_detected: edge > 0,
  };
}

/**
 * Scan alternate lines for A-grade props to find optimal value.
 * @param {object} prop - { player, stat, projected_mean, projected_stddev, grade }
 * @param {Array} oddsData - Array of { line, odds, book } from alt markets
 * @returns {object|null} Best alt line with edge, or null
 */
function scanAltLines(prop, oddsData) {
  if (!prop || !oddsData || oddsData.length === 0) return null;

  const { projected_mean, projected_stddev } = prop;
  const direction = prop.direction || 'over';

  const evaluated = oddsData.map(alt => {
    const modelProb = calculateModelProbability(projected_mean, projected_stddev, alt.line, direction);
    const comparison = compareToBookImplied(modelProb, alt.odds);

    return {
      line: alt.line,
      odds: alt.odds,
      book: alt.book,
      model_probability: comparison.model_prob,
      book_implied: comparison.book_implied,
      edge: comparison.edge,
      value_detected: comparison.value_detected,
    };
  });

  const withValue = evaluated.filter(e => e.value_detected);
  if (withValue.length === 0) return null;

  withValue.sort((a, b) => b.edge - a.edge);
  const optimal = withValue[0];

  return {
    optimal_line: optimal.line,
    odds: optimal.odds,
    book: optimal.book,
    model_probability: optimal.model_probability,
    book_implied: optimal.book_implied,
    edge: optimal.edge,
    all_value_lines: withValue,
  };
}

module.exports = {
  scanAltLines,
  calculateModelProbability,
  compareToBookImplied,
  normalCDF,
  americanToImplied,
};
