///|
fn strategy_error(
  code : StrategyErrorCode,
  lap : Int?,
  message : String,
) -> StrategyError {
  { code, lap, message, }
}

///|
fn driver_exists(analysis : RaceAnalysis, driver : String) -> Bool {
  for candidate in analysis.drivers {
    if candidate == driver {
      return true
    }
  }
  false
}

///|
/// Validate driver selection and return a copied, lap-sorted pit plan.
fn validate_and_normalize_request(
  analysis : RaceAnalysis,
  request : StrategyRequest,
) -> Result[StrategyRequest, StrategyError] {
  if !driver_exists(analysis, request.target_driver) {
    return Err(
      strategy_error(
        UnknownTargetDriver,
        None,
        "target driver does not exist in this race",
      ),
    )
  }
  if !driver_exists(analysis, request.opponent_driver) {
    return Err(
      strategy_error(
        UnknownOpponentDriver,
        None,
        "opponent driver does not exist in this race",
      ),
    )
  }
  if request.target_driver == request.opponent_driver {
    return Err(
      strategy_error(
        SameDriver,
        None,
        "target driver and opponent driver must be different",
      ),
    )
  }
  let stops : Array[PlannedPitStop] = []
  for stop in request.stops {
    stops.push(stop)
  }
  stops.sort_by((left, right) => left.pit_lap - right.pit_lap)
  for index = 0; index < stops.length(); index = index + 1 {
    let stop = stops[index]
    if stop.pit_lap < 1 || stop.pit_lap >= analysis.lap_count {
      return Err(
        strategy_error(
          InvalidPitLap,
          Some(stop.pit_lap),
          "planned pit lap must be within the race and before the final lap",
        ),
      )
    }
    if index > 0 && stops[index - 1].pit_lap == stop.pit_lap {
      return Err(
        strategy_error(
          DuplicatePitLap,
          Some(stop.pit_lap),
          "only one planned pit stop is allowed per lap",
        ),
      )
    }
  }
  Ok({
    target_driver: request.target_driver,
    opponent_driver: request.opponent_driver,
    stops,
  })
}