'use strict';

/**
 * Player/market grouping (P0-3) — the phone audit found the same player listed
 * as separate flat entries everywhere (Bohm's strikeout ladder = 3 ledger cards,
 * a leaderboard flooded with 9 identical stolen_bases rows). Props must present
 * as ONE row per player per market family, with alt lines nested as a ladder,
 * and no single market may flood a ranked list.
 *
 * CommonJS so it's unit-testable by the plain-JS Jest suite AND importable by
 * the .tsx surfaces (allowJs) — same doctrine as vyndrTokens / playerName.
 */

const { nameKey } = require('./playerName');

/** A stat's MARKET FAMILY — alt lines of the same market collapse together.
 *  (Side/line are stripped; only the stat type identifies the family.) */
function marketFamily(stat) {
  return String(stat || '').trim().toLowerCase().replace(/\s+/g, '_');
}

function playerMarketKey(player, stat) {
  return `${nameKey(player)}|${marketFamily(stat)}`;
}

/**
 * Dedupe ranked leaderboard rows: ONE row per (player, market family) — keeping
 * the FIRST (best-ranked) occurrence — then a per-market cap so one prop type
 * can't flood the board. Input order is preserved (callers pass pre-ranked).
 *
 * @param {Array<{player?:string, stat?:string}>} rows
 * @param {number} [perMarketCap=4] max rows of one market family on the board
 * @returns {Array} deduped, capped, order-preserving
 */
function dedupeLeaders(rows, perMarketCap = 4) {
  const seen = new Set();
  const marketCount = new Map();
  const out = [];
  for (const r of Array.isArray(rows) ? rows : []) {
    const who = r.player || r.player_name;
    if (!r || !who) continue;
    const key = playerMarketKey(who, r.stat);
    if (seen.has(key)) continue;                 // one row per player+market
    const fam = marketFamily(r.stat);
    const n = marketCount.get(fam) || 0;
    if (perMarketCap > 0 && n >= perMarketCap) continue; // don't let one market flood
    seen.add(key);
    marketCount.set(fam, n + 1);
    out.push(r);
  }
  return out;
}

/**
 * Group flat prop rows into ONE entry per (player, market family) with the alt
 * lines nested as a ladder — the Alt Line Ladder shape (the /pricing Desk demo).
 * `betterOf(a, b)` picks the primary/base line (default: keep the first seen).
 * Order of first appearance is preserved.
 *
 * @param {Array} rows  each { player, stat, line, ... }
 * @param {(a,b)=>any} [betterOf] choose the representative row of a ladder
 * @returns {Array<{ player, stat, market, primary, ladder }>}
 */
function groupIntoLadders(rows, betterOf) {
  const byKey = new Map();
  const order = [];
  for (const r of Array.isArray(rows) ? rows : []) {
    const who = r.player || r.player_name;
    if (!r || !who) continue;
    const key = playerMarketKey(who, r.stat);
    if (!byKey.has(key)) {
      byKey.set(key, { player: who, stat: r.stat, market: marketFamily(r.stat), primary: r, ladder: [r] });
      order.push(key);
    } else {
      const g = byKey.get(key);
      g.ladder.push(r);
      if (typeof betterOf === 'function') g.primary = betterOf(g.primary, r);
    }
  }
  // Sort each ladder by line ascending for a stable rungs display.
  for (const key of order) {
    const g = byKey.get(key);
    g.ladder.sort((a, b) => (Number(a.line) || 0) - (Number(b.line) || 0));
  }
  return order.map((k) => byKey.get(k));
}

module.exports = { marketFamily, playerMarketKey, dedupeLeaders, groupIntoLadders };
