/* ============================================================
   RULES ENGINE — pure logic (no React)
   Geometry (range/arc), dice gathering, damage resolution,
   round/phase flow, command queue, scoring.
   Mechanics implemented from general knowledge of the game;
   no rulebook text or copyrighted data is reproduced.
   ============================================================ */

const PHASES = ["command", "ship", "squadron", "status"];
const PHASE_LABEL = { command: "Command", ship: "Ship", squadron: "Squadron", status: "Status" };
const PHASE_DESC = {
  command:  "Plan in het geheim de command dials voor elk schip.",
  ship:     "Spelers wisselen activaties af: onthul dial, vuur, manoeuvreer.",
  squadron: "Activeer squadrons die nog niet door een Squadron-command bewogen.",
  status:   "Ready tokens & dials, verhoog de ronde, geef initiatief door.",
};

// center distance → range band (world units, tuned to the visual rings)
const RANGE_DIST = { close: 178, medium: 300, long: 424 };
function worldDist(a, b) { return Math.hypot(a.x - b.x, a.y - b.y); }
function rangeBand(d) {
  if (d <= RANGE_DIST.close) return "close";
  if (d <= RANGE_DIST.medium) return "medium";
  if (d <= RANGE_DIST.long) return "long";
  return null; // out of range
}
function rangeBetween(a, b) { return rangeBand(worldDist(a, b)); }

// bearing from `from` to `to`, relative to from.facing (0=front, clockwise)
function relBearing(from, to) {
  const ang = Math.atan2(to.x - from.x, -(to.y - from.y)) * 180 / Math.PI; // 0 = up
  return ((ang - (from.facing || 0)) % 360 + 360) % 360;
}
function arcFromBearing(b) {
  if (b >= 315 || b < 45) return "front";
  if (b < 135) return "right";
  if (b < 225) return "rear";
  return "left";
}
function defenderHitArc(defender, attacker) { return arcFromBearing(relBearing(defender, attacker)); }
function attackerFireArc(attacker, defender) { return arcFromBearing(relBearing(attacker, defender)); }

// dice available from an arc at a range (range gates colours)
function gatherArcDice(ship, arc, range) {
  const base = (ship.attackDice && ship.attackDice[arc]) || { red: 0, blue: 0, black: 0 };
  const out = { red: base.red || 0, blue: base.blue || 0, black: base.black || 0 };
  if (range === "long") { out.blue = 0; out.black = 0; }
  else if (range === "medium") { out.black = 0; }
  return out;
}
function poolSize(p) { return (p.red || 0) + (p.blue || 0) + (p.black || 0); }

function rollPool(pool) {
  const dice = [];
  ["red", "blue", "black"].forEach((c) => { for (let i = 0; i < (pool[c] || 0); i++) dice.push({ id: uid("ad"), color: c, face: rollFace(c), rerolled: false }); });
  return dice;
}

// damage to a ship: hit arc shields first, redirect overflow to one adjacent arc, rest to hull
function resolveDamage(defender, hitArc, damage, redirect) {
  const shields = { ...defender.shields };
  let remaining = damage;
  const hit = Math.min(shields[hitArc], remaining);
  shields[hitArc] -= hit; remaining -= hit;
  let redirected = 0;
  if (redirect && redirect.arc && remaining > 0) {
    redirected = Math.min(redirect.amount || 0, remaining, shields[redirect.arc]);
    shields[redirect.arc] -= redirected; remaining -= redirected;
  }
  const hullLoss = remaining;
  const hull = Math.max(0, defender.hull - hullLoss);
  return { shields, hull, hullLoss, destroyed: hull <= 0 };
}

/* ---- round / phase flow ---- */
// reset per-round ship flags at the start of a command phase (incl. exhausted tokens → ready)
// let op: commandTokens wordt hier bewust NIET gereset — die overleven rondes (§ command tokens)
function resetForRound(ship) {
  return { ...ship, activated: false, revealedDial: null, attacksMade: 0, arcsFired: [], attackedZones: [], engineeringPts: 0,
    activationStarted: false, squadronPtsLeft: 0, dialPending: false, navToken: false, cfToken: false,
    defenseTokens: (ship.defenseTokens || []).map((dt) => dt.state === "exhausted" ? { ...dt, state: "ready" } : dt),
    upgrades: withUpgradesReadied(ship.upgrades) };
}
// dials a ship must plan this command phase
function dialsToPlan(ship, round) { return round === 1 ? ship.command : 1; }

// initiative-aware activation: returns faction that should activate next, or null if ship phase done
function nextToActivate(ships, sides, lastFaction, firstFaction) {
  const unact = (f) => ships.some((s) => s.faction === f && !s.activated && !s.destroyed);
  if (!sides.length) return null;
  // alternate, but skip a side with nothing left
  const other = sides.find((f) => f !== lastFaction) || lastFaction;
  if (lastFaction == null) return sides.find((f) => f === firstFaction && unact(f)) || sides.find(unact) || null;
  if (unact(other)) return other;
  if (unact(lastFaction)) return lastFaction;
  return null;
}

// squadron phase: alternate factions (2 activations per turn); next faction with squadrons left, or null
function nextSquadronTurn(squadrons, sides, lastFaction, firstFaction) {
  const unact = (f) => squadrons.some((q) => q.faction === f && !q.activated && !q.destroyed);
  if (!sides.length) return null;
  if (lastFaction == null) return sides.find((f) => f === firstFaction && unact(f)) || sides.find(unact) || null;
  const other = sides.find((f) => f !== lastFaction) || lastFaction;
  if (unact(other)) return other;
  if (unact(lastFaction)) return lastFaction;
  return null;
}

// squadron engagement distance (world units) — dogfight bereik, ruim binnen close
const SQD_RANGE = 100;
// squadron-bewegingsafstand per snelheidspunt (world units), voor de sleep-limiet
const SQD_MOVE = 55;

Object.assign(window, {
  PHASES, PHASE_LABEL, PHASE_DESC, RANGE_DIST, SQD_RANGE, SQD_MOVE,
  worldDist, rangeBand, rangeBetween, relBearing, arcFromBearing,
  defenderHitArc, attackerFireArc, gatherArcDice, poolSize, rollPool,
  resolveDamage, resetForRound, dialsToPlan, nextToActivate, nextSquadronTurn,
});
