// Multi-game pricing engine (League of Legends + Valorant).
// Per-division price matrix per game; total = sum of division prices from current→target,
// × region multiplier × (1 + margin), clamped to a minimum.
// Net Wins (LoL) = per-win by current tier. Browser global + Node → globalThis.EldoradoPricing
(function (root) {
  "use strict";

  const GAMES = {
    lol: {
      key: "lol", name: "League of Legends",
      tiers: ["Iron", "Bronze", "Silver", "Gold", "Platinum", "Emerald", "Diamond"],
      apex: ["Master", "Grandmaster", "Challenger"],
      divs: ["IV", "III", "II", "I"], // low → high
      tierBase: { Iron: 1.5, Bronze: 2, Silver: 2.5, Gold: 3.5, Platinum: 5, Emerald: 7, Diamond: 12 },
      apexBase: { Master: 40, Grandmaster: 60, Challenger: 80 },
      netWin: { Iron: 2.5, Bronze: 2.5, Silver: 3, Gold: 3.5, Platinum: 5, Emerald: 7, Diamond: 11, Master: 20, Grandmaster: 25, Challenger: 30 },
      aliases: { grandmaster: "Grandmaster", challenger: "Challenger", platinum: "Platinum", plat: "Platinum", emerald: "Emerald", diamond: "Diamond", master: "Master", iron: "Iron", bronze: "Bronze", silver: "Silver", gold: "Gold", chall: "Challenger", gm: "Grandmaster", dia: "Diamond", eme: "Emerald" },
    },
    valorant: {
      key: "valorant", name: "Valorant",
      tiers: ["Iron", "Bronze", "Silver", "Gold", "Platinum", "Diamond", "Ascendant", "Immortal"],
      apex: ["Radiant"],
      divs: ["I", "II", "III"], // low → high (Valorant)
      tierBase: { Iron: 1.5, Bronze: 2, Silver: 2.5, Gold: 3.5, Platinum: 5, Diamond: 7, Ascendant: 10, Immortal: 16 },
      apexBase: { Radiant: 120 },
      netWin: { Iron: 2.5, Bronze: 2.5, Silver: 3, Gold: 3.5, Platinum: 5, Diamond: 7, Ascendant: 10, Immortal: 16, Radiant: 40 },
      aliases: { radiant: "Radiant", immortal: "Immortal", ascendant: "Ascendant", diamond: "Diamond", platinum: "Platinum", plat: "Platinum", gold: "Gold", silver: "Silver", bronze: "Bronze", iron: "Iron", imm: "Immortal", asc: "Ascendant", rad: "Radiant", dia: "Diamond" },
    },
  };

  // Build each game's full ladder (low index = lower rank)
  Object.values(GAMES).forEach((g) => {
    const L = [];
    g.tiers.forEach((t) => g.divs.forEach((d) => L.push({ tier: t, div: d, label: `${t} ${d}` })));
    g.apex.forEach((t) => L.push({ tier: t, div: "", label: t }));
    L.forEach((e, i) => (e.index = i));
    g.ladder = L;
  });

  function defaultDivisionPrices(gameKey) {
    const g = GAMES[gameKey] || GAMES.lol;
    const dp = {};
    g.tiers.forEach((t) => { dp[t] = {}; g.divs.forEach((d) => (dp[t][d] = g.tierBase[t])); });
    g.apex.forEach((t) => (dp[t] = { flat: g.apexBase[t] }));
    return dp;
  }
  function defaultNetWin(gameKey) { return Object.assign({}, (GAMES[gameKey] || GAMES.lol).netWin); }

  const DEFAULTS = {
    marginPct: 20, minOffer: 5, roundTo: 0.5, placementPrice: 5, defaultRegionMult: 1,
    regionMultipliers: {
      EUW: 1, EUNE: 1, EU: 1, NA: 1.1, AP: 1, TR: 0.85, LAN: 1, LAS: 1,
      OCE: 1.1, KR: 1.3, BR: 0.9, RU: 0.85, ME: 0.9, JP: 1.1,
    },
  };

  const REGION_CODES = [
    ["europe nordic", "EUNE"], ["europe west", "EUW"], ["north america", "NA"],
    ["latin america north", "LAN"], ["latin america south", "LAS"], ["brazil", "BR"],
    ["turkey", "TR"], ["korea", "KR"], ["japan", "JP"], ["oceania", "OCE"],
    ["russia", "RU"], ["middle east", "ME"], ["singapore", "SG"],
  ];
  function regionCode(region) {
    if (!region) return "";
    const r = String(region).toLowerCase();
    for (const [k, v] of REGION_CODES) if (r.includes(k)) return v;
    return String(region).toUpperCase(); // Valorant short codes (AP/EU/NA) pass through
  }

  function gameOf(req) { return GAMES[req && req.game] || GAMES.lol; }

  function parseRank(text, g) {
    if (!text) return -1;
    g = g || GAMES.lol;
    const t = String(text).toLowerCase();
    let tier = null;
    for (const k of Object.keys(g.aliases)) if (t.includes(k)) { tier = g.aliases[k]; break; }
    if (!tier) return -1;
    if (g.apex.includes(tier)) return g.ladder.findIndex((e) => e.tier === tier);
    let div = null;
    const m = t.match(/\b(iv|iii|ii|i)\b/);
    if (m) div = m[1].toUpperCase();
    else { const nm = t.match(/\b([1-4])\b/); if (nm) div = { "1": "I", "2": "II", "3": "III", "4": "IV" }[nm[1]]; }
    if (!div || g.divs.indexOf(div) < 0) div = g.divs[0]; // default to lowest division
    return g.ladder.findIndex((e) => e.tier === tier && e.div === div);
  }

  function mergeDivisionPrices(gameKey, custom) {
    const out = defaultDivisionPrices(gameKey);
    if (custom) for (const t of Object.keys(out)) out[t] = Object.assign({}, out[t], custom[t] || {});
    return out;
  }

  // `pricing` is one game's pricing block: { marginPct, minOffer, roundTo, placementPrice,
  // regionMultipliers, divisionPrices, netWinPrices, enabledTiers }.
  function compute(req, pricing) {
    pricing = pricing || {};
    const g = gameOf(req);
    const gk = g.key;

    const marginPct = pricing.marginPct != null ? pricing.marginPct : DEFAULTS.marginPct;
    const minOffer = pricing.minOffer != null ? pricing.minOffer : DEFAULTS.minOffer;
    const roundTo = pricing.roundTo || DEFAULTS.roundTo;
    const placementPrice = pricing.placementPrice != null ? pricing.placementPrice : DEFAULTS.placementPrice;
    const regionMultipliers = Object.assign({}, DEFAULTS.regionMultipliers, pricing.regionMultipliers || {});
    const divisionPrices = mergeDivisionPrices(gk, pricing.divisionPrices);
    const netWinPrices = Object.assign({}, defaultNetWin(gk), pricing.netWinPrices || {});
    const enabledTiers = pricing.enabledTiers || {};
    const L = g.ladder;

    const code = regionCode(req.region);
    const mult = regionMultipliers[code] != null ? regionMultipliers[code] : (pricing.defaultRegionMult != null ? pricing.defaultRegionMult : DEFAULTS.defaultRegionMult);
    const out = { final: null, base: 0, steps: 0, region: code, mult, note: "", perWin: false, game: gk };
    const round = (v) => Math.round((Math.round(v / roundTo) * roundTo) * 100) / 100;
    const type = req.type || "rank";

    if (type === "netwins") {
      const ci = parseRank(req.currentRank, g);
      if (ci < 0) { out.note = "rank?"; return out; }
      const pw = Number(netWinPrices[L[ci].tier]) || 0;
      out.base = pw; out.perWin = true; out.note = "netwins";
      out.final = round(pw * mult * (1 + (marginPct || 0) / 100));
      return out;
    }

    let base;
    if (type === "placements") {
      base = placementPrice; out.note = "placements";
    } else if (type === "rank" && req.currentRank && req.targetRank) {
      const ci = parseRank(req.currentRank, g);
      const ti = parseRank(req.targetRank, g);
      if (ci < 0 || ti < 0 || ti <= ci) { out.note = "rank?"; return out; }
      if (enabledTiers[L[ti].tier] === false) { out.note = "tier off"; return out; }
      base = 0;
      for (let k = ci; k < ti; k++) {
        const e = L[k];
        const tp = divisionPrices[e.tier] || {};
        base += e.div ? Number(tp[e.div]) || 0 : Number(tp.flat) || 0;
      }
      out.steps = ti - ci;
    } else {
      out.note = "custom";
      return out;
    }

    out.base = base;
    out.final = round(Math.max(base * mult * (1 + (marginPct || 0) / 100), minOffer));
    return out;
  }

  const api = { GAMES, DEFAULTS, gameOf, parseRank, regionCode, defaultDivisionPrices, defaultNetWin, compute };
  if (typeof module !== "undefined" && module.exports) module.exports = api;
  root.EldoradoPricing = api;
})(typeof globalThis !== "undefined" ? globalThis : this);
