///|
fn find_record(
  records : Array[ParsedRecord],
  driver : String,
  lap : Int,
) -> ParsedRecord? {
  for item in records {
    if item.record.driver == driver && item.record.lap == lap {
      return Some(item)
    }
  }
  None
}

///|
fn has_driver(drivers : Array[String], driver : String) -> Bool {
  for candidate in drivers {
    if candidate == driver {
      return true
    }
  }
  false
}

///|
fn validate_records(
  parsed : Array[ParsedRecord],
) -> Result[Array[LapRecord], RaceError] {
  for index = 1; index < parsed.length(); index = index + 1 {
    let previous = parsed[index - 1]
    let current = parsed[index]
    if previous.record.lap == current.record.lap &&
      previous.record.driver == current.record.driver {
      return Err(
        csv_error(
          DuplicateDriverLap,
          current.line,
          "driver,lap",
          "each driver may appear only once per lap",
        ),
      )
    }
  }
  let drivers = []
  for item in parsed {
    if item.record.lap == 1 && !has_driver(drivers, item.record.driver) {
      drivers.push(item.record.driver)
    }
  }
  if drivers.length() == 0 {
    return Err(
      csv_error(MissingLap, 0, "lap", "every driver must start at lap 1"),
    )
  }
  let max_lap = parsed[parsed.length() - 1].record.lap
  for item in parsed {
    if !has_driver(drivers, item.record.driver) {
      return Err(
        csv_error(
          InconsistentLaps,
          item.line,
          "driver",
          "every driver must start at lap 1",
        ),
      )
    }
  }
  for lap = 1; lap <= max_lap; lap = lap + 1 {
    let first = match find_record(parsed, drivers[0], lap) {
      Some(item) => item
      None =>
        return Err(
          csv_error(
            MissingLap,
            0,
            "lap",
            "all drivers must have every consecutive lap",
          ),
        )
    }
    for driver in drivers {
      let current = match find_record(parsed, driver, lap) {
        Some(item) => item
        None =>
          return Err(
            csv_error(
              InconsistentLaps,
              0,
              "lap",
              "all drivers must have the same consecutive laps",
            ),
          )
      }
      if current.record.track_status != first.record.track_status {
        return Err(
          csv_error(
            InconsistentTrackStatus,
            current.line,
            "track_status",
            "all drivers must share track_status on the same lap",
          ),
        )
      }
      if current.record.weather != first.record.weather {
        return Err(
          csv_error(
            InconsistentWeather,
            current.line,
            "weather",
            "all drivers must share weather on the same lap",
          ),
        )
      }
      if lap > 1 {
        let previous = match find_record(parsed, driver, lap - 1) {
          Some(item) => item
          None =>
            return Err(
              csv_error(
                MissingLap,
                current.line,
                "lap",
                "laps must be continuous for each driver",
              ),
            )
        }
        if previous.record.pit {
          if current.record.tyre_age_laps != 1 {
            return Err(
              csv_error(
                InvalidTyreTransition,
                current.line,
                "tyre_age_laps",
                "tyre_age_laps must be 1 after a pit stop",
              ),
            )
          }
        } else {
          if current.record.compound != previous.record.compound {
            return Err(
              csv_error(
                InvalidTyreTransition,
                current.line,
                "compound",
                "compound may change only after a pit stop",
              ),
            )
          }
          if current.record.tyre_age_laps != previous.record.tyre_age_laps + 1 {
            return Err(
              csv_error(
                InvalidTyreTransition,
                current.line,
                "tyre_age_laps",
                "tyre_age_laps must increase by 1 without a pit stop",
              ),
            )
          }
        }
      }
    }
  }
  let records = []
  for item in parsed {
    records.push(item.record)
  }
  Ok(records)
}