/* ============================================================
   DATA LAYER — factions, dice, base sizes, seed fleet
   All ship names + stats below are original / invented values
   for this personal play-aid. No AMG/Lucasfilm stat data bundled.
   ============================================================ */

const FACTIONS = {
  empire:     { id: "empire",     name: "Empire",     color: "var(--empire)" },
  rebel:      { id: "rebel",      name: "Rebel",      color: "var(--rebel)" },
  republic:   { id: "republic",   name: "Republic",   color: "var(--republic)" },
  separatist: { id: "separatist", name: "Separatist", color: "var(--separatist)" },
};
const FACTION_ORDER = ["empire", "rebel", "republic", "separatist"];

// Defense token + command dial vocab (§3)
const DEFENSE_TOKENS = ["Brace", "Redirect", "Evade", "Contain", "Scatter", "Salvo"];
// vereenvoudigde effecten zoals de engine ze toepast (eigen bewoording)
const TOKEN_EFFECT = {
  Brace: "Halveer de schade (naar boven afgerond).",
  Redirect: "Verplaats overgebleven schade naar een aangrenzend schild.",
  Evade: "Long: annuleer een die. Medium: herrol een die. Close: geen effect.",
  Contain: "Voorkom het critical-effect van de aanval.",
  Scatter: "Annuleer alle schade van de aanval.",
  Salvo: "Tegenaanval (handmatig af te handelen).",
};
const COMMAND_DIALS  = ["Navigate", "Squadron", "Repair", "Concentrate Fire"];

const ARCS = ["front", "rear", "left", "right"];
const ARC_LABEL = { front: "Front", rear: "Rear", left: "Left", right: "Right" };
// which shields border each arc (Redirect targets these)
const ARC_ADJACENT = { front: ["left", "right"], rear: ["left", "right"], left: ["front", "rear"], right: ["front", "rear"] };

// command dial catalogue with the (faithfully simplified) effects the engine applies
const DIALS = {
  navigate:   { id: "navigate",   name: "Navigate",          short: "NAV",  desc: "Pas snelheid aan met 1, en zet één extra yaw-klik bij de manoeuvre." },
  squadron:   { id: "squadron",   name: "Squadron",          short: "SQ",   desc: "Activeer tot [squadron] squadrons (beweeg + aanval)." },
  repair:     { id: "repair",     name: "Repair",            short: "REP",  desc: "Krijg engineering-punten gelijk aan de engineering-waarde." },
  cf:         { id: "cf",         name: "Concentrate Fire",  short: "CF",   desc: "Voeg 1 extra die (kleur uit de pool) toe aan elke aanval deze activatie." },
};
const DIAL_ORDER = ["navigate", "squadron", "repair", "cf"];

// default armament (attack dice) per base size — invented, editable per ship
const DEFAULT_ARMAMENT = {
  small:  { front: { red: 0, blue: 1, black: 1 }, rear: { red: 0, blue: 1, black: 0 }, left: { red: 0, blue: 1, black: 0 }, right: { red: 0, blue: 1, black: 0 } },
  medium: { front: { red: 1, blue: 1, black: 1 }, rear: { red: 0, blue: 1, black: 0 }, left: { red: 1, blue: 1, black: 0 }, right: { red: 1, blue: 1, black: 0 } },
  large:  { front: { red: 2, blue: 1, black: 1 }, rear: { red: 1, blue: 1, black: 0 }, left: { red: 1, blue: 2, black: 0 }, right: { red: 1, blue: 2, black: 0 } },
  huge:   { front: { red: 2, blue: 2, black: 1 }, rear: { red: 1, blue: 1, black: 0 }, left: { red: 2, blue: 2, black: 0 }, right: { red: 2, blue: 2, black: 0 } },
};
const DEFAULT_ENGINEERING = { small: 2, medium: 3, large: 4, huge: 6 };
const DEFAULT_SQUADRON_VAL = { small: 1, medium: 2, large: 3, huge: 4 };
const cloneArm = (a) => ARCS.reduce((o, arc) => (o[arc] = { ...a[arc] }, o), {});

// Base-size defaults (§5.6) — editable after creation
const BASE_SIZES = {
  small:  { id: "small",  name: "Small",  hull: 4,  shields: { front: 2, rear: 1, left: 1, right: 1 }, command: 1, px: 64,  defaults: ["Brace", "Evade"] },
  medium: { id: "medium", name: "Medium", hull: 5,  shields: { front: 3, rear: 1, left: 2, right: 2 }, command: 2, px: 92,  defaults: ["Brace", "Redirect", "Evade"] },
  large:  { id: "large",  name: "Large",  hull: 8,  shields: { front: 4, rear: 2, left: 3, right: 3 }, command: 3, px: 124, defaults: ["Brace", "Redirect", "Contain"] },
  huge:   { id: "huge",   name: "Huge",   hull: 11, shields: { front: 4, rear: 2, left: 4, right: 4 }, command: 4, px: 168, defaults: ["Brace", "Redirect", "Contain", "Salvo"] },
};
const BASE_SIZE_ORDER = ["small", "medium", "large", "huge"];

let _uid = 0;
const uid = (p = "id") => `${p}_${Date.now().toString(36)}_${(_uid++).toString(36)}`;

function makeShip(partial = {}) {
  const size = partial.baseSize && BASE_SIZES[partial.baseSize] ? partial.baseSize : "medium";
  const def = BASE_SIZES[size];
  const maxShields = { ...def.shields, ...(partial.maxShields || {}) };
  const maxHull = partial.maxHull != null ? partial.maxHull : def.hull;
  return {
    id: uid("ship"),
    kind: "ship",
    name: partial.name || "Unnamed Vessel",
    faction: partial.faction || "empire",
    baseSize: size,
    points: partial.points != null ? partial.points : 0,
    catalogType: partial.catalogType || null, // schip-type uit de catalogus (voor upgrade-restricties)
    unique: !!partial.unique,                  // uniek schip (voor lijst-legaliteit)
    slots: partial.slots || [],               // beschikbare upgrade-slots (catalogus)
    upgrades: partial.upgrades || [],          // gekozen upgrades: [{slot,name,points,unique,id}] of legacy {name,points}
    maxHull,
    hull: partial.hull != null ? partial.hull : maxHull,
    maxShields,
    shields: partial.shields ? { ...maxShields, ...partial.shields } : { ...maxShields },
    command: partial.command != null ? partial.command : def.command,
    engineering: partial.engineering != null ? partial.engineering : DEFAULT_ENGINEERING[size],
    squadronValue: partial.squadronValue != null ? partial.squadronValue : DEFAULT_SQUADRON_VAL[size],
    attackDice: partial.attackDice ? cloneArm(partial.attackDice) : cloneArm(DEFAULT_ARMAMENT[size]),
    antiSquadron: partial.antiSquadron || { red: 0, blue: 0, black: 0 }, // anti-squadron dice pool

    speed: partial.speed != null ? partial.speed : 2,
    facing: partial.facing != null ? partial.facing : 0, // degrees, 0 = nose up
    x: partial.x != null ? partial.x : 0,
    y: partial.y != null ? partial.y : 0,
    defenseTokens: (partial.defenseTokens || def.defaults).map((t) => ({
      id: uid("dt"), type: t, state: "ready",
    })),
    // ---- live game state (rules engine) ----
    commandQueue: [],         // planned dials, front = oldest (revealed first)
    revealedDial: null,       // dial revealed this activation, or null
    dialPending: false,       // dial onthuld maar nog niet gebruikt/bewaard (mens moet kiezen)
    commandTokens: partial.commandTokens ? partial.commandTokens.slice() : [], // bewaarde dials (strings), cap = command; overleeft rondes
    navToken: false,          // navigate-token besteed deze activatie (extra yaw, zie toggleManeuver)
    cfToken: false,           // concentrate-fire-token besteed deze activatie (zie useCF wiring)
    engineeringPts: 0,        // unspent engineering this activation
    activationStarted: false, // dial onthuld (ook bij lege queue) — activatie loopt
    squadronPtsLeft: 0,       // resterende squadron-activaties via Squadron-dial
    activated: false,         // activated this round?
    attacksMade: 0,           // attacks this activation (max 2, different arcs)
    arcsFired: [],
    attackedZones: partial.attackedZones || [], // "targetId:hitArc" al aangevallen deze activatie
    critical: false,          // has taken a standard critical
    critCards: partial.critCards ? partial.critCards.slice() : [], // face-up schadekaarten (blijven tot het schip vernietigd is)
    destroyed: false,
    statsMissing: !!partial.statsMissing,
  };
}

/* ---- DICE (§3) — verified faces ---- */
// face kinds: hit, crit, double, hitcrit, acc, blank
const DICE = {
  red:   { color: "red",   label: "Red",   ranges: ["close", "medium", "long"],
           faces: ["hit","hit","hit","crit","double","acc","blank","blank"] },
  blue:  { color: "blue",  label: "Blue",  ranges: ["close", "medium"],
           faces: ["hit","hit","hit","hit","crit","crit","acc","acc"] },
  black: { color: "black", label: "Black", ranges: ["close"],
           faces: ["hit","hit","hit","hit","hitcrit","hitcrit","blank","blank"] },
};
const DICE_ORDER = ["red", "blue", "black"];
const RANGES = ["close", "medium", "long"];
const RANGE_LABEL = { close: "Close", medium: "Medium", long: "Long" };

// face → contribution to tally
const FACE_VALUE = {
  hit:     { dmg: 1, crit: 0, acc: 0 },
  crit:    { dmg: 1, crit: 1, acc: 0 },
  double:  { dmg: 2, crit: 0, acc: 0 },
  hitcrit: { dmg: 2, crit: 1, acc: 0 },
  acc:     { dmg: 0, crit: 0, acc: 1 },
  blank:   { dmg: 0, crit: 0, acc: 0 },
};

function rollFace(color) {
  const faces = DICE[color].faces;
  return faces[Math.floor(Math.random() * faces.length)];
}
function dieAllowed(color, range) {
  return DICE[color].ranges.includes(range);
}

/* ---- SQUADRONS ---- */
function makeSquadron(partial = {}) {
  const maxHull = partial.maxHull != null ? partial.maxHull : 5;
  return {
    id: uid("sqd"),
    kind: "squadron",
    name: partial.name || "Squadron",
    faction: partial.faction || "empire",
    points: partial.points != null ? partial.points : 0,
    maxHull,
    hull: partial.hull != null ? partial.hull : maxHull,
    speed: partial.speed != null ? partial.speed : 3,
    antiSquad: partial.antiSquad || { blue: 2, black: 0 },   // dice vs squadrons
    antiShip:  partial.antiShip  || { blue: 1, black: 0 },   // dice vs ships (needs Bomber)
    keywords: partial.keywords || [],                         // e.g. ["Bomber","Escort","Swarm"]
    defenseTokens: (partial.defenseTokens || []).map((t) => ({ id: uid("dt"), type: t, state: "ready" })),
    x: partial.x != null ? partial.x : 0,
    y: partial.y != null ? partial.y : 0,
    facing: 0,
    activated: false,
    destroyed: false,
  };
}
// expand a parsed/roster squadron group (count>1) into individual board tokens
function expandSquadrons(groups) {
  const out = [];
  groups.forEach((g) => {
    const per = Math.round((g.points || 0) / Math.max(g.count || 1, 1));
    const stats = squadronStatsFor(g.name);
    for (let i = 0; i < (g.count || 1); i++) {
      out.push(makeSquadron({ name: g.name, faction: g.faction, points: per, ...stats }));
    }
  });
  return out;
}
function squadronStatsFor(name) {
  const cat = (typeof window !== "undefined" && window.CATALOG_DATA && window.CATALOG_DATA.squadrons) || [];
  const hit = cat.find((q) => (q.title || "").toLowerCase() === (name || "").toLowerCase());
  if (hit && hit.stats) {
    return { maxHull: hit.stats.hull, speed: hit.stats.speed, antiSquad: hit.stats.antiSquad,
      antiShip: hit.stats.antiShip, keywords: hit.keywords || [], defenseTokens: hit.stats.defenseTokens || [] };
  }
  const n = (name || "").toLowerCase();
  if (/bomb|y-?wing|torpedo/.test(n)) return { maxHull: 6, antiSquad: { blue: 1, black: 0 }, antiShip: { blue: 1, black: 1 }, speed: 3, keywords: ["Bomber"] };
  if (/intercept|tie|a-?wing|fight/.test(n)) return { maxHull: 3, antiSquad: { blue: 3, black: 0 }, antiShip: { blue: 0, black: 0 }, speed: 5, keywords: ["Swarm"] };
  if (/escort|x-?wing|defend/.test(n)) return { maxHull: 5, antiSquad: { blue: 2, black: 0 }, antiShip: { blue: 1, black: 0 }, speed: 4, keywords: ["Escort"], defenseTokens: ["Brace"] };
  return { maxHull: 4, antiSquad: { blue: 2, black: 0 }, antiShip: { blue: 1, black: 0 }, speed: 4, keywords: [] };
}

/* ============================================================
   SEED — small example skirmish (invented ships/stats)
   ============================================================ */
function buildSeed() {
  const ships = [
    makeShip({ name: "Vigil-Class Cruiser", faction: "empire", baseSize: "medium", points: 78,
      x: -210, y: -95, facing: 135, speed: 2,
      upgrades: [{ name: "Gunnery Officer", points: 7 }, { name: "Reinforced Bulwark", points: 6 }] }),
    makeShip({ name: "Dominator Dreadnought", faction: "empire", baseSize: "large", points: 142,
      x: -85, y: 85, facing: 65, speed: 1,
      upgrades: [{ name: "Fleet Tactician", points: 4 }, { name: "Overload Pulse", points: 8 }] }),
    makeShip({ name: "Sentinel Corvette", faction: "empire", baseSize: "small", points: 41,
      x: -255, y: 35, facing: 90, speed: 3, upgrades: [] }),

    makeShip({ name: "Liberator Frigate", faction: "rebel", baseSize: "medium", points: 81,
      x: 210, y: 90, facing: 315, speed: 2,
      upgrades: [{ name: "Defiance", points: 5 }, { name: "Engine Techs", points: 8 }] }),
    makeShip({ name: "Profundity Command Ship", faction: "rebel", baseSize: "large", points: 154,
      x: 90, y: -85, facing: 250, speed: 1,
      upgrades: [{ name: "Strategic Adviser", points: 4 }, { name: "Spinal Armament", points: 9 }] }),
    makeShip({ name: "Hammerhead Razor", faction: "rebel", baseSize: "small", points: 39,
      x: 255, y: -30, facing: 270, speed: 4, upgrades: [] }),
  ];
  const squadrons = [
    { id: uid("sq"), name: "Interceptor Wing", faction: "empire", points: 16, count: 4 },
    { id: uid("sq"), name: "Bomber Flight", faction: "empire", points: 18, count: 3 },
    { id: uid("sq"), name: "Strike Squadron", faction: "rebel", points: 19, count: 3 },
    { id: uid("sq"), name: "Escort Wing", faction: "rebel", points: 12, count: 2 },
  ];
  return { ships, squadrons };
}

/* ============================================================
   SCENARIOS — original tactical setups (not AMG card names).
   Pure tracker info: the app never enforces these (§1 non-goals).
   Fallback used when no baked objectives are available.
   ============================================================ */
const SCENARIOS_FALLBACK = [
  { id: "open",     type: "Standoff",   name: "Open Engagement",
    blurb: "Geen objectief. Twee vloten, recht tegenover elkaar. Vernietig de tegenstander.",
    setup: "Standaard deployment binnen de zones. Geen obstakels nodig.",
    tokens: 0, objective: "Meeste vlootpunten vernietigd wint." },
  { id: "outpost",  type: "Assault",    name: "Contested Outpost",
    blurb: "Een station in het midden van het veld. Wie het controleert, scoort elke ronde.",
    setup: "Plaats 1 station-token op het midden. 3 obstakels rond het centrum.",
    tokens: 1, objective: "Schip binnen afstand 1 van het station aan het einde van een ronde scoort." },
  { id: "convoy",   type: "Assault",    name: "Severed Convoy",
    blurb: "De verdediger sleept transport-tokens; de aanvaller jaagt erop.",
    setup: "Verdediger plaatst 3 cargo-tokens in eigen helft. Aanvaller deployt als laatste.",
    tokens: 3, objective: "Aanvaller scoort per vernietigd/aangeraakt cargo-token." },
  { id: "minefield",type: "Defense",    name: "Static Minefield",
    blurb: "Het slagveld ligt bezaaid met mijnen. Manoeuvreren is levensgevaarlijk.",
    setup: "Plaats 5 obstakel-tokens verspreid over het midden-derde van het veld.",
    tokens: 5, objective: "Verdediger scoort wanneer vijand een obstakel overlapt." },
  { id: "blockade", type: "Defense",    name: "Picket Line",
    blurb: "De verdediger houdt een linie; de aanvaller moet er doorheen breken.",
    setup: "Verdediger deployt in een smalle band; aanvaller krijgt extra deployment-ruimte.",
    tokens: 0, objective: "Aanvaller scoort per schip dat de centrale linie passeert." },
  { id: "corona",   type: "Navigation", name: "Solar Corona",
    blurb: "Een zonnestorm verstoort de navigatie. Posities bepalen alles.",
    setup: "Plaats 3 obstakels langs één lange rand. Wisselende deployment-hoeken.",
    tokens: 3, objective: "Scoor punten voor schepen in het verre kwadrant van de vijand." },
  { id: "lanes",    type: "Navigation", name: "Shipping Lanes",
    blurb: "Drie corridors lopen over het veld. Beheers de banen.",
    setup: "Markeer 3 lanes met token-paren. Schepen deployen aan weerszijden.",
    tokens: 6, objective: "Scoor per lane die je aan het einde van de ronde beheerst." },
];

/* ============================================================
   SCENARIOS — afgeleid van de gebakken objectives (Task 2/3),
   met de bovenstaande verzonnen lijst als fallback als er geen
   catalogusdata beschikbaar is.
   ============================================================ */
const SCENARIOS = (typeof window !== "undefined" && window.CATALOG_DATA && window.CATALOG_DATA.objectives && window.CATALOG_DATA.objectives.length)
  ? window.CATALOG_DATA.objectives.map((o) => ({
      id: o.id, type: (o.type || "").replace(/^\w/, (c) => c.toUpperCase()), name: o.name,
      blurb: o.special_rule || o.setup || "", setup: o.setup || "", tokens: 0,
      objective: o.end_of_game || o.end_of_round || o.special_rule || "",
    }))
  : SCENARIOS_FALLBACK;

const DAMAGE_DECK = (typeof window !== "undefined" && window.CATALOG_DATA && window.CATALOG_DATA.damage) || [];

// full free-text rules for a scenario id, straight from the baked dataset (verbatim, not enforced)
function findObjectiveDetail(id) {
  const cat = (typeof window !== "undefined" && window.CATALOG_DATA && window.CATALOG_DATA.objectives) || [];
  return cat.find((o) => o.id === id) || null;
}

Object.assign(window, {
  FACTIONS, FACTION_ORDER, DEFENSE_TOKENS, TOKEN_EFFECT, COMMAND_DIALS,
  ARCS, ARC_LABEL, ARC_ADJACENT, DIALS, DIAL_ORDER,
  BASE_SIZES, BASE_SIZE_ORDER, DEFAULT_ARMAMENT, DEFAULT_ENGINEERING, DEFAULT_SQUADRON_VAL, cloneArm,
  DICE, DICE_ORDER, RANGES, RANGE_LABEL, FACE_VALUE,
  uid, makeShip, makeSquadron, expandSquadrons, squadronStatsFor,
  rollFace, dieAllowed, buildSeed, SCENARIOS, DAMAGE_DECK, findObjectiveDetail,
});
