/**
 * The intent vocabulary — every legal player choice, from docs/architecture/protocol.md §1.
 *
 * An intent is a PROPOSAL. It may be rejected. Contrast with an event (events.ts), which is a fact.
 */

import type { CarType, Direction, Hand, OfficeTier, TrackGeometry } from './content.ts';
import type { CardId, GridCoord, PlayerIndex, TrayId } from './state.ts';

// ---------------------------------------------------------------------------
// Local Operations Phase (§6) — a three-way exclusive choice
// ---------------------------------------------------------------------------

export type LocalOpsOption = 'switch' | 'draw' | 'freightAgent';

export type Intent =
  | { type: 'localOps.choose'; option: LocalOpsOption }
  // -- switch (§6.1, Appendix A)
  | { type: 'switch.move'; trayId: TrayId; to: GridCoord; reverse: boolean }
  | { type: 'switch.dropCars'; trayId: TrayId; count: number }
  /**
   * Small Yard enhancement — "a train that spends one move in the yard may sort itself in any
   * order, including cars in front of the engine". This is the designed answer to §A.3's
   * come-off-in-seated-order constraint, which is what makes facing-point work possible.
   */
  | { type: 'switch.sortConsist'; trayId: TrayId; order: number[] }
  | { type: 'switch.end' }
  // -- draw (§6.2)
  | { type: 'draw.fromHomeOffice' }
  | { type: 'draw.fromDepartment'; slot: number }
  /** `variant` indexes `variantsFor(geometry)` — Gap 11: orientation is chosen on placement. */
  | { type: 'card.play'; cardId: CardId; placement?: GridCoord; variant?: number }
  | { type: 'card.discard'; cardId: CardId; toSlot: number }
  /**
   * Lay a piece from your personal track supply (§12.2 / content.ts TRACK_SUPPLY).
   *
   * Laid during the "draw a card" option, one piece a turn — how track behaved when it WAS a card.
   * Confirmed; see implications.md §10 Q10.
   */
  | { type: 'track.lay'; geometry: TrackGeometry; hand: Hand; placement: GridCoord; variant?: number }
  | { type: 'draw.end' }
  // -- freight agent (§6.3)
  | { type: 'freightAgent.stockOutbound'; at: GridCoord; carType: CarType }
  | { type: 'freightAgent.clearInbound'; at: GridCoord; index: number }
  | { type: 'freightAgent.unjam'; at: GridCoord; from: 'outbound' | 'inbound' | 'menAtWork'; index: number }
  // -- New Train Phase (§7)
  | { type: 'newTrain.placeCar'; trayId: TrayId; carType: CarType; loaded: boolean }
  | { type: 'newTrain.passCar'; trayId: TrayId }
  /** Q9 — run a second, identical section behind a train that is due out this Stage. */
  | { type: 'newTrain.secondSection'; trainNumber: number }
  // -- Mainline Phase (§8.1) — the Superintendent's clearance ruling
  | { type: 'mainline.clearance'; allow: boolean }
  /**
   * Lay a Mainline modifier (Brakeman / Airbrakes / Helpers / Realignment) on a Mainline card.
   * `node` indexes `division.nodes`.
   */
  | { type: 'mainline.modify'; cardId: CardId; node: number }
  /**
   * Red Flags — protect a stopped train. The flagged train cannot be hit; an approaching train is
   * held instead of colliding.
   */
  | { type: 'maneuver.redFlags'; cardId: CardId; trayId: TrayId }
  /**
   * Flying Switch — cut cars off behind the engine and roll them into an adjacent industry, without
   * the engine entering it.
   */
  | { type: 'maneuver.flyingSwitch'; cardId: CardId; trayId: TrayId; count: number; to: GridCoord }
  | { type: 'redFlag.play' }
  // -- Load/Unload Phase (§9)
  | { type: 'porter.board'; at: GridCoord }
  | { type: 'porter.detrain'; at: GridCoord }
  /** §9.3 — the first Laborer step: Green Loading Slot -> MEN. */
  | { type: 'laborer.startLoad'; at: GridCoord }
  | { type: 'laborer.advanceLoad'; at: GridCoord; box: number }
  | { type: 'laborer.beginUnload'; at: GridCoord; carIndex: number }
  | { type: 'loadUnload.end' };

export type IntentType = Intent['type'];

// ---------------------------------------------------------------------------
// Rejections — protocol.md §2
// ---------------------------------------------------------------------------

export type RejectionCode =
  | 'NOT_YOUR_TURN'
  | 'WRONG_PHASE'
  | 'OPTION_ALREADY_CHOSEN'
  | 'OPTION_NOT_CHOSEN'
  | 'NO_MOVES_REMAINING'
  | 'ILLEGAL_MOVE'
  | 'NO_SUCH_TRAY'
  | 'NO_SUCH_CARD'
  | 'NO_SUCH_FACILITY'
  | 'CANNOT_DROP_HERE'
  | 'CONSIST_EMPTY'
  | 'CONSIST_FULL'
  | 'HAND_LIMIT'
  | 'CARD_NOT_IN_HAND'
  | 'NO_PLACEMENT'
  | 'NOT_CONNECTED'
  | 'DECK_EMPTY'
  | 'SLOT_EMPTY'
  | 'RESOURCE_SPENT'
  | 'BOX_FULL'
  | 'BOX_EMPTY'
  | 'NO_SUITABLE_CAR'
  | 'SUITABLE_CAR_EXISTS'
  | 'WRONG_CAR_TYPE'
  | 'NOT_SUPERINTENDENT'
  | 'NO_PENDING_DECISION'
  | 'NOT_UPGRADEABLE'
  | 'FACILITY_LOCKED'
  | 'NOT_IMPLEMENTED'
  | 'WRONG_INTENT'
  | 'NOT_A_GRADE'
  | 'TRAIN_ON_CARD'
  | 'CONSIST_ORDER'
  | 'NO_TRAIN_AT_OFFICE';

export type Rejection = { code: RejectionCode; message: string };

export function reject(code: RejectionCode, message: string): Rejection {
  return { code, message };
}

// ---------------------------------------------------------------------------
// Re-exports used by handlers
// ---------------------------------------------------------------------------

export type { CardId, Direction, GridCoord, OfficeTier, PlayerIndex, TrayId };
