///|
/// Copy an array while preserving order.
pub fn[T] copy_array(values : Array[T]) -> Array[T] {
  let result : Array[T] = []
  for value in values {
    result.push(value)
  }
  result
}

///|
/// Whether a string occurs in an array.
pub fn string_array_contains(values : Array[String], target : String) -> Bool {
  for value in values {
    if value == target {
      return true
    }
  }
  false
}

///|
/// Insert a string only when it is not already present.
pub fn push_unique_string(values : Array[String], value : String) -> Unit {
  if !string_array_contains(values, value) {
    values.push(value)
  }
}

///|
/// Count exact string matches.
pub fn count_string(values : Array[String], target : String) -> Int {
  let mut count = 0
  for value in values {
    if value == target {
      count = count + 1
    }
  }
  count
}

///|
/// Return the smaller integer.
pub fn min_int(left : Int, right : Int) -> Int {
  if left < right {
    left
  } else {
    right
  }
}

///|
/// Return the larger integer.
pub fn max_int(left : Int, right : Int) -> Int {
  if left > right {
    left
  } else {
    right
  }
}

///|
/// Integer absolute value without relying on target-specific helpers.
pub fn abs_int(value : Int) -> Int {
  if value < 0 {
    -value
  } else {
    value
  }
}

///|
/// Clamp an integer to an inclusive range.
pub fn clamp_int(value : Int, lower : Int, upper : Int) -> Int {
  if value < lower {
    lower
  } else if value > upper {
    upper
  } else {
    value
  }
}

///|
/// Find a worker by identifier.
pub fn find_worker(workers : Array[Worker], id : String) -> Worker? {
  for worker in workers {
    if worker.id == id {
      return Some(worker)
    }
  }
  None
}

///|
/// Find a visit by identifier.
pub fn find_visit(visits : Array[Visit], id : String) -> Visit? {
  for visit in visits {
    if visit.id == id {
      return Some(visit)
    }
  }
  None
}

///|
/// Find an assignment for a visit.
pub fn find_assignment(
  assignments : Array[Assignment],
  visit_id : String,
) -> Assignment? {
  for assignment in assignments {
    if assignment.visit_id == visit_id {
      return Some(assignment)
    }
  }
  None
}

///|
/// Select all assignments belonging to a worker.
pub fn assignments_for_worker(
  assignments : Array[Assignment],
  worker_id : String,
) -> Array[Assignment] {
  let result : Array[Assignment] = []
  for assignment in assignments {
    if assignment.worker_id == worker_id {
      result.push(assignment)
    }
  }
  result
}

///|
/// Total assigned service time for a worker.
pub fn assigned_minutes(
  assignments : Array[Assignment],
  worker_id : String,
) -> Int {
  let mut total = 0
  for assignment in assignments {
    if assignment.worker_id == worker_id {
      total = total + assignment.end_minute - assignment.start_minute
    }
  }
  total
}

///|
/// Count diagnostics at one severity.
pub fn count_diagnostics(
  diagnostics : Array[Diagnostic],
  level : DiagnosticLevel,
) -> Int {
  let mut count = 0
  for diagnostic in diagnostics {
    if diagnostic.level == level {
      count = count + 1
    }
  }
  count
}

///|
/// True when any diagnostic blocks scheduling.
pub fn diagnostics_have_errors(diagnostics : Array[Diagnostic]) -> Bool {
  count_diagnostics(diagnostics, Error) > 0
}