'use strict';

/**
 * Rare-event markets (betting-logic audit, 2026-07-19).
 *
 * On a 0.5-line rare counting stat — doubles, triples, home runs, stolen bases —
 * the UNDER is the juiced side the book wants action on: the event just usually
 * doesn't happen, so a real projection of (say) 0.2 always "favors" under 0.5,
 * but there's no takeable edge (the book prices it -250+). Surfacing those
 * unders floods the board with fake B-grade "under 0.5" reads and violates the
 * standing no-unders-default doctrine.
 *
 * So we SUPPRESS the under side on these markets at/below the line threshold —
 * it is not graded. The OVER side still grades normally and only surfaces when
 * the model genuinely projects the event above the line.
 *
 * Config-driven (a stat list + a line threshold) so it's tunable without
 * touching grade logic. CommonJS so it's unit-testable + requireable everywhere.
 */

// Low-frequency counting stats (across sports) where a 0.5 under is
// structurally a bad bet. This list is only the BACKUP for props with no odds —
// the primary, stat-agnostic guard is the juice floor below.
const RARE_EVENT_STATS = [
  'doubles', 'triples', 'home_runs', 'stolen_bases', // MLB
  'steals', 'blocks',                                 // NBA / WNBA
];

// The under is suppressed only at/below this line (0.5 is the juiced rare line;
// a 1.5+ line is a different market where an under can be a real read).
const RARE_EVENT_LINE_MAX = 0.5;

function isRareEventStat(statType) {
  return RARE_EVENT_STATS.includes(String(statType || '').toLowerCase());
}

/** True when this prop is a rare-event UNDER at/below the line threshold —
 *  the juiced side, always refused. Needs no projection. */
function isSuppressedRareUnder(statType, line, direction) {
  const dir = String(direction || '').toLowerCase();
  const ln = Number(line);
  return dir === 'under'
    && isRareEventStat(statType)
    && Number.isFinite(ln)
    && ln <= RARE_EVENT_LINE_MAX;
}

/** True when this prop is a rare-event OVER at/below the threshold that the
 *  model does NOT genuinely project (projection <= line). Refusing it is what
 *  keeps the board clean — a 0.2-projection over 0.5 carries the SAME |edge| as
 *  the suppressed under, so if we let it grade it just takes the under's place.
 *  The over is a real read only when the model projects ABOVE the line. */
function isSuppressedRareOver(statType, line, direction, projection) {
  const dir = String(direction || '').toLowerCase();
  if (dir !== 'over') return false;
  const ln = Number(line);
  const proj = Number(projection);
  return isRareEventStat(statType)
    && Number.isFinite(ln)
    && ln <= RARE_EVENT_LINE_MAX
    && !(Number.isFinite(proj) && proj > ln);
}

// ── The juice floor (the GENERAL, stat-agnostic rule) ───────────────────────
// The book's own price is the ground truth of "no takeable edge": a side priced
// worse than this is a heavy favorite where the vig has already eaten any edge,
// regardless of stat or sport. The rare-event doubles unders that started this
// were priced -625 to -1100 — laying 6-11x to win 1x on an ~82% event, with no
// value the model could recover. This catches EVERY version of the problem
// (steals, blocks, any market) without a per-stat whitelist. Tunable via env.
const JUICE_ODDS_FLOOR = Number(process.env.JUICE_ODDS_FLOOR || -400);

function parseAmerican(odds) {
  if (odds == null) return null;
  const n = Number(String(odds).replace('+', ''));
  return Number.isFinite(n) ? n : null;
}

/** American odds on the SIDE being graded (under_odds for under, else over). */
function gradedSideOdds(prop) {
  const dir = String((prop && prop.direction) || '').toLowerCase();
  const raw = dir === 'under'
    ? (prop && (prop.under_odds ?? prop.under)) ?? null
    : (prop && (prop.over_odds ?? prop.over)) ?? null;
  return parseAmerican(raw);
}

/** True when the graded side is too juiced to carry a takeable edge. Odds
 *  absent → false (can't judge from price; the rare-event rule is the backup). */
function isTooJuiced(prop) {
  const o = gradedSideOdds(prop);
  return o != null && o <= JUICE_ODDS_FLOOR;
}

module.exports = {
  RARE_EVENT_STATS,
  RARE_EVENT_LINE_MAX,
  isRareEventStat,
  isSuppressedRareUnder,
  isSuppressedRareOver,
  JUICE_ODDS_FLOOR,
  parseAmerican,
  gradedSideOdds,
  isTooJuiced,
};
