/**
 * MLB matchup context helpers (Session 15).
 *
 * Two pure functions, no I/O, no state:
 *   - platoonAdvantage(pitcherHand, batterHand)
 *   - projectedPA(lineupPosition)
 *
 * Sources:
 *   - Platoon splits: the standard MLB convention — opposite-handed
 *     matchups favor the batter (LHP vs RHB, RHP vs LHB). Switch
 *     hitters get the advantage in either matchup because they bat
 *     opposite-handed by definition.
 *   - Lineup → projected plate appearances: derived from MLB-average
 *     team PA distributions per lineup slot (2024 league composite).
 *     A leadoff hitter sees ~4.7 PA in a 9-inning game; the 9-hole
 *     sees ~3.5. Numbers from baseball-reference team batting tables
 *     averaged across all 30 teams.
 *
 * Callers (computeFeatures MLB branch) attach the outputs to the
 * feature vector as `platoon_advantage` (bool|null) and
 * `projected_pa` (number|null). When inputs are absent or invalid
 * the helpers return null — engine1 treats null as a neutral signal
 * and reasoning omits the line gracefully.
 */

const HAND_VALUES = new Set(['L', 'R', 'S']);

function normalizeHand(hand) {
  if (!hand) return null;
  const h = String(hand).trim().toUpperCase().charAt(0);
  return HAND_VALUES.has(h) ? h : null;
}

/**
 * platoonAdvantage — true when the batter has the platoon edge.
 *
 *   LHP vs RHB → true     (right-handed batter sees pitches better)
 *   LHP vs LHB → false
 *   RHP vs RHB → false
 *   RHP vs LHB → true
 *   any vs Switch hitter → true (switch hits opposite of pitcher)
 *
 * Returns null when either hand is unknown. The grading engine reads
 * `null` as "no signal" — same treatment as `false` for confidence
 * arithmetic, but reasoning will skip the line.
 */
function platoonAdvantage(pitcherHand, batterHand) {
  const p = normalizeHand(pitcherHand);
  const b = normalizeHand(batterHand);
  if (!p || !b) return null;
  if (b === 'S') return true;            // switch hitter — always has edge
  if (p === b) return false;             // same-handed → pitcher edge
  return true;                           // opposite-handed → batter edge
}

// League-average plate appearances per lineup slot in 9-inning games.
// Source: 2024 MLB composite (baseball-reference team batting tables).
// Slot 1 (leadoff) leads the team; later slots see fewer PAs because
// they may not bat in the bottom of innings already wrapped up.
const PA_BY_SLOT = Object.freeze({
  1: 4.70,
  2: 4.55,
  3: 4.43,
  4: 4.31,
  5: 4.19,
  6: 4.07,
  7: 3.95,
  8: 3.83,
  9: 3.71,
});

/**
 * projectedPA — expected plate appearances by lineup position.
 *
 * @param {number|string} position 1..9
 * @returns {number|null}
 *
 * Out-of-range or invalid → null. This keeps reasoning honest when
 * the odds payload doesn't carry batting order yet (the more common
 * case today — odds-api doesn't expose lineup position in the prop
 * envelope).
 */
function projectedPA(position) {
  if (position == null) return null;
  const p = Number(position);
  if (!Number.isInteger(p) || p < 1 || p > 9) return null;
  return PA_BY_SLOT[p];
}

module.exports = {
  platoonAdvantage,
  projectedPA,
  __internals: { normalizeHand, HAND_VALUES, PA_BY_SLOT },
};
