/* ============================================================
   TRADE MATCHING ENGINE  (CBA Art VII §6(j) — see V1 TRADE_RULES.md)
   Ported verbatim from "Version 1 of Full Roster/index.html".

   v1 "solid core": Expanded/Room TPE matching, apron + hard-cap
   tripwires, 2nd-apron no-aggregation, NTC. BYC / S&T /
   TPE-absorption / combinatorial splitting are deferred.

   V1 read thresholds from DATA.meta as {cap, firstApron,
   secondApron}. Here we use the shared CAP_2026 constants
   (cap / apron1 / apron2) — verified byte-equal to the real
   teams-trade-data.json `cba` block.
   ============================================================ */

const CAP_2023_24 = 136021000; // fixed CBA anchor for the scaled cushion

function dollars(n) { return "$" + Math.round(n).toLocaleString(); }

// Expanded TPE max incoming for outgoing aggregate O (TRADE_RULES.md §2).
// allowance = 250000, or 0 when post-trade Apron Team Salary > 1st apron.
function tradeMaxIncoming(O, allowance) {
  const C75 = 7500000 * (CAP_2026.cap / CAP_2023_24);
  const branchY = Math.min(2.0 * O + allowance, 1.0 * O + C75);
  const branchZ = 1.25 * O + allowance;
  return Math.max(branchY, branchZ);
}

// side = { code, out:[{name,salary,noTrade}], in:[...], preSalary }
function evaluateTeamSide(side) {
  const O = side.out.reduce((a, p) => a + (p.salary || 0), 0);
  const I = side.in.reduce((a, p) => a + (p.salary || 0), 0);
  const post = side.preSalary - O + I;
  const cap = CAP_2026.cap;
  const firstApron = CAP_2026.apron1;
  const secondApron = CAP_2026.apron2;
  const allowance = post > firstApron ? 0 : 250000;
  const r = { code: side.code, legal: true, reasons: [], flags: [], O, I, post };

  if (O === 0 && I === 0) {
    r.legal = false;
    r.reasons.push(`${side.code}: no players selected on this side.`);
    return r;
  }

  // No-trade clause
  for (const p of [...side.out, ...side.in]) {
    if (p.noTrade) {
      r.legal = false;
      r.reasons.push(`${p.name} has a no-trade clause — must consent.`);
    }
  }

  // 2nd-apron team cannot aggregate outgoing salaries (TRADE_RULES.md §3b)
  if (side.preSalary > secondApron && side.out.length > 1) {
    r.legal = false;
    r.reasons.push(`${side.code} is above the 2nd apron and cannot aggregate ${side.out.length} outgoing salaries.`);
  }

  // Salary matching. A below-cap team may match either via cap room OR via
  // the Traded Player Exception (CBA §6(j)(2)) — it gets whichever is more
  // favorable. An over-cap team only has the Expanded TPE.
  const roomMax = side.preSalary < cap
    ? (cap - side.preSalary) + allowance : null;
  const tpeMax = tradeMaxIncoming(O, allowance);
  let maxIn, basis;
  if (roomMax !== null && roomMax >= tpeMax) {
    maxIn = roomMax;
    basis = `room ${dollars(cap - side.preSalary)} + ${dollars(allowance)} (Room TPE)`;
  } else {
    maxIn = tpeMax;
    basis = `Expanded TPE on ${dollars(O)} outgoing` +
            (allowance === 0 ? " ($250k→$0 above 1st apron)" : "");
  }
  maxIn = Math.round(maxIn);
  if (I > maxIn) {
    r.legal = false;
    r.reasons.push(`${side.code} takes in ${dollars(I)} but max is ${dollars(maxIn)} — ${basis}.`);
  } else {
    r.reasons.push(`${side.code} salary match OK: ${dollars(I)} ≤ ${dollars(maxIn)} — ${basis}.`);
  }

  // Hard-cap tripwires (TRADE_RULES.md §3a). Room TPE (under-cap) teams
  // absorb into cap space and do not trip the Expanded-TPE hard caps.
  const usedExpandedTPE = side.preSalary >= cap;
  if (usedExpandedTPE && side.out.length > 1)
    r.flags.push(`${side.code}: aggregating outgoing salaries → hard-capped at the 2nd apron.`);
  if (usedExpandedTPE && I > O)
    r.flags.push(`${side.code}: takes back more than it sends (Expanded TPE) → hard-capped at the 1st apron (${dollars(firstApron)}).`);
  if (post > secondApron)
    r.flags.push(`${side.code} post-trade ${dollars(post)} is ABOVE the 2nd apron.`);
  else if (post > firstApron)
    r.flags.push(`${side.code} post-trade ${dollars(post)} is between the aprons.`);

  return r;
}

function getToolTier(salary) {
  if (salary >= CAP_2026.apron2) return "above2nd";
  if (salary >= CAP_2026.apron1) return "between";
  return "below1st";
}

Object.assign(window, {
  CAP_2023_24, dollars, tradeMaxIncoming, evaluateTeamSide, getToolTier,
});
