Coverage Summary for Class: EconomyHandlersKt (core.handlers)

Class Class, % Method, % Branch, % Line, % Instruction, %
EconomyHandlersKt 100% (1/1) 50% (1/2) 40% (4/10) 39.5% (15/38) 36.2% (54/149)


 // FILE: core/src/main/kotlin/core/handlers/EconomyHandlers.kt
 package core.handlers
 
 import core.*
 import core.pipeline.EconomySettlement
 import core.pipeline.TaxPolicy
 import core.rng.Rng
 import core.state.*
 
 /**
  * Economy command handlers.
  *
  * ## Semantic Ownership
  * Handles all economy-related commands: trophy sales, tax payments.
  *
  * ## Visibility
  * Internal to core module - only Reducer.kt should call these.
  */
 
 /**
  * Sells trophies for copper.
  *
  * Why:
  * - Trophy liquidation is the main short-term liquidity tool.
  *
  * How:
  * - Converts trophies into copper using a fixed exchange rate.
  * - Emits a single event that fully explains the transaction.
  */
 @Suppress("ReturnCount")
 internal fun handleSellTrophies(
     state: GameState,
     cmd: SellTrophies,
     _rng: Rng,
     ctx: SeqContext
 ): GameState {
     val total = state.economy.trophiesStock
     if (total <= 0) return state
 
     val amountToSell = if (cmd.amount <= 0) total else minOf(cmd.amount, total)
 
     // Settlement: Trophy sale
     val economyDelta = EconomySettlement.computeTrophySaleDelta(amountToSell)
 
     val newState = state.copy(
         economy = economyDelta.applyTo(state.economy)
     )
 
     ctx.emit(
         TrophySold(
             day = newState.meta.dayIndex,
             revision = newState.meta.revision,
             cmdId = cmd.cmdId,
             seq = 0L,
             amount = amountToSell,
             moneyGained = amountToSell  // 1:1 exchange rate
         )
     )
 
     return newState
 }
 
 /**
  * Pays tax debt.
  *
  * Why:
  * - Tax payment is a risk-reduction lever.
  * - Penalties must be paid before principal to prevent gaming.
  *
  * How:
  * - Applies payment to penalty first, then to the base due.
  * - Emits the remaining due so adapters do not infer debt.
  */
 internal fun handlePayTax(
     state: GameState,
     cmd: PayTax,
     _rng: Rng,
     ctx: SeqContext
 ): GameState {
     if (cmd.amount <= 0) return state
 
     val availableMoney = state.economy.moneyCopper
     if (availableMoney < cmd.amount) return state
 
     // Settlement: Tax payment
     val paymentResult = TaxPolicy.computePayment(
         paymentAmount = cmd.amount,
         currentTaxDue = state.meta.taxAmountDue,
         currentPenalty = state.meta.taxPenalty
     )
 
     val newState = state.copy(
         economy = state.economy.copy(moneyCopper = availableMoney - cmd.amount),
         meta = state.meta.copy(
             taxAmountDue = paymentResult.newTaxDue,
             taxPenalty = paymentResult.newPenalty,
             taxMissedCount = if (paymentResult.isComplete) 0 else state.meta.taxMissedCount
         )
     )
 
     ctx.emit(
         TaxPaid(
             day = newState.meta.dayIndex,
             revision = newState.meta.revision,
             cmdId = cmd.cmdId,
             seq = 0L,
             amountPaid = cmd.amount,
             amountDue = paymentResult.remainingDue,
             isPartialPayment = !paymentResult.isComplete
         )
     )
 
     return newState
 }