'use strict';

/**
 * De-vig engine (Model Train, step 1) — the foundation for EV + value.
 *
 * A two-sided prop price carries the book's vig (the "overround"): the two
 * sides' implied probabilities sum to MORE than 1. De-vigging strips that back
 * out to FAIR probabilities that sum to 1, and converts them back to a fair
 * (vig-free) price per side.
 *
 * METHOD: multiplicative / proportional de-vig — each side's implied prob is
 * divided by the total. It's the standard, distribution-free two-way method
 * (a.k.a. "normalized implied probability"). Simple, transparent, and correct
 * for two-way markets; we note it in the stored `method` field.
 *
 * If only one side is priced, fair values are UNAVAILABLE (null) — never faked.
 */

const round3 = (n) => Math.round(n * 1000) / 1000;

/** American odds → implied probability (WITH the vig). Null on bad input. */
function americanToImpliedProb(american) {
  const a = Number(american);
  if (!Number.isFinite(a) || a === 0) return null;
  return a > 0 ? 100 / (a + 100) : (-a) / ((-a) + 100);
}

/** American odds → decimal odds (total return multiple incl. stake). */
function americanToDecimal(american) {
  const a = Number(american);
  if (!Number.isFinite(a) || a === 0) return null;
  return a > 0 ? 1 + a / 100 : 1 + 100 / (-a);
}

/** Probability → fair American odds. Null outside (0,1). */
function impliedProbToAmerican(p) {
  if (!Number.isFinite(p) || p <= 0 || p >= 1) return null;
  // Even money (50%) is +100 by convention → favorites (p > .5) go negative.
  return p > 0.5 ? -Math.round((p / (1 - p)) * 100) : Math.round(((1 - p) / p) * 100);
}

/**
 * Two-way de-vig. Given BOTH sides' American odds, return fair prob + fair price
 * per side plus the overround. Null when a side's odds are missing/invalid —
 * the caller marks fair as unavailable rather than inventing it.
 */
function devigTwoWay(overOdds, underOdds) {
  const po = americanToImpliedProb(overOdds);
  const pu = americanToImpliedProb(underOdds);
  if (po == null || pu == null) return null;
  const sum = po + pu; // > 1 by the vig
  if (!(sum > 0)) return null;
  const fairOver = po / sum;
  const fairUnder = pu / sum;
  return {
    method: 'multiplicative',
    overround: round3(sum - 1),
    over: { fair_prob: round3(fairOver), fair_odds: impliedProbToAmerican(fairOver) },
    under: { fair_prob: round3(fairUnder), fair_odds: impliedProbToAmerican(fairUnder) },
  };
}

/**
 * Expected value (%) of staking one unit on `american` when the model gives the
 * side probability `modelProb`. EV per unit = p·decimal − 1. Positive = the bet
 * is +EV at the price you'd actually pay (the vig is already baked into the
 * actual price, so this is the true "does it pay you" measure). Null on bad input.
 */
function evPct(modelProb, american) {
  const dec = americanToDecimal(american);
  if (!Number.isFinite(modelProb) || modelProb <= 0 || dec == null) return null;
  return Math.round((modelProb * dec - 1) * 1000) / 10; // one decimal place
}

module.exports = {
  americanToImpliedProb,
  americanToDecimal,
  impliedProbToAmerican,
  devigTwoWay,
  evPct,
};
