///|
/// Evaluate map over array elements
fn eval_map(
  inner : Expr,
  input : Json,
  env : Env,
) -> Iter[Json] raise InterpreterError {
  guard input is Array(arr) else {
    raise TypeMismatch("array", @ast_internal.json_type_name(input))
  }
  let results : Array[Json] = arr.fold(init=[], fn(
    acc,
    elem,
  ) raise InterpreterError {
    let mapped = eval_with_env(inner, elem, env).collect()
    for v in mapped {
      acc.push(v)
    }
    acc
  })
  Iter::singleton(Json::array(results))
}

///|
/// Evaluate select filter
fn eval_select(
  condition : Expr,
  input : Json,
  env : Env,
) -> Iter[Json] raise InterpreterError {
  match eval_with_env(condition, input, env).collect() {
    [] => Iter::empty()
    [first, ..] =>
      if @ast_internal.is_truthy(first) {
        Iter::singleton(input)
      } else {
        Iter::empty()
      }
  }
}

///|
/// Evaluate array sort
fn eval_sort(input : Json) -> Iter[Json] raise InterpreterError {
  guard input is Array(arr) else {
    raise TypeMismatch("array", @ast_internal.json_type_name(input))
  }
  let sorted = arr.copy()
  sorted.sort_by(compare_json)
  Iter::singleton(Json::array(sorted))
}

///|
/// Evaluate array reverse
fn eval_reverse(input : Json) -> Iter[Json] raise InterpreterError {
  guard input is Array(arr) else {
    raise TypeMismatch("array", @ast_internal.json_type_name(input))
  }
  Iter::singleton(Json::array(arr.rev()))
}

///|
/// Evaluate array flatten
fn eval_flatten(
  depth_opt : Int?,
  input : Json,
) -> Iter[Json] raise InterpreterError {
  guard input is Array(arr) else {
    raise TypeMismatch("array", @ast_internal.json_type_name(input))
  }
  let depth = depth_opt.unwrap_or(999999) // Flatten all levels
  Iter::singleton(Json::array(flatten_array(arr, depth)))
}

///|
/// Evaluate array unique
fn eval_unique(input : Json) -> Iter[Json] raise InterpreterError {
  guard input is Array(arr) else {
    raise TypeMismatch("array", @ast_internal.json_type_name(input))
  }
  let sorted = arr.copy()
  sorted.sort_by(compare_json)
  let (unique, _prev) : (Array[Json], Json?) = sorted.fold(init=([], None), fn(
    state,
    elem,
  ) {
    let (acc, prev) = state
    match prev {
      None => {
        acc.push(elem)
        (acc, Some(elem))
      }
      Some(p) =>
        if compare_json(p, elem) != 0 {
          acc.push(elem)
          (acc, Some(elem))
        } else {
          (acc, prev)
        }
    }
  })
  Iter::singleton(Json::array(unique))
}