///|
/// Evaluate string split
fn eval_split(sep : String, input : Json) -> Iter[Json] raise InterpreterError {
  guard input is String(s) else {
    raise TypeMismatch("string", @ast_internal.json_type_name(input))
  }
  let parts = s.split(sep).collect()
  let json_parts = string_views_to_json(parts)
  Iter::singleton(Json::array(json_parts))
}

///|
/// Evaluate string join
fn eval_join(sep : String, input : Json) -> Iter[Json] raise InterpreterError {
  guard input is Array(arr) else {
    raise TypeMismatch("array", @ast_internal.json_type_name(input))
  }
  let parts = json_to_strings(arr)
  Iter::singleton(Json::string(parts.join(sep)))
}

///|
/// Evaluate prefix check
fn eval_starts_with(
  prefix : String,
  input : Json,
) -> Iter[Json] raise InterpreterError {
  guard input is String(s) else {
    raise TypeMismatch("string", @ast_internal.json_type_name(input))
  }
  Iter::singleton(Json::boolean(s.has_prefix(prefix)))
}

///|
/// Evaluate suffix check
fn eval_ends_with(
  suffix : String,
  input : Json,
) -> Iter[Json] raise InterpreterError {
  guard input is String(s) else {
    raise TypeMismatch("string", @ast_internal.json_type_name(input))
  }
  Iter::singleton(Json::boolean(s.has_suffix(suffix)))
}

///|
fn string_views_to_json(parts : ArrayView[StringView]) -> Array[Json] {
  parts.map(fn(part) { Json::string(part.to_owned()) })
}

///|
fn json_to_strings(parts : ArrayView[Json]) -> Array[String] {
  parts.map(fn(part) {
    match part {
      String(s) => s
      _ => @debug.to_string(part)
    }
  })
}

///|
fn array_contains_all(
  haystack : ArrayView[Json],
  needles : ArrayView[Json],
) -> Bool {
  needles.iter().all(fn(needle) { haystack.contains(needle) })
}

///|
fn json_contains(container : Json, item : Json) -> Bool {
  match (container, item) {
    (String(haystack), String(n)) => haystack.contains(n)
    (Array(haystack), _) => haystack.contains(item)
    (Object(haystack), String(key)) => haystack.contains(key)
    _ => false
  }
}

///|
/// Evaluate contains for strings, arrays, and objects
fn eval_contains(
  expr : Expr,
  input : Json,
  env : Env,
) -> Iter[Json] raise InterpreterError {
  match eval_with_env(expr, input, env).collect() {
    [] => Iter::singleton(Json::boolean(false))
    [needle, ..] => Iter::singleton(Json::boolean(json_contains(input, needle)))
  }
}

///|
/// Evaluate inside for strings, arrays, and objects
fn eval_inside(
  expr : Expr,
  input : Json,
  env : Env,
) -> Iter[Json] raise InterpreterError {
  match eval_with_env(expr, input, env).collect() {
    [] => Iter::singleton(Json::boolean(false))
    [container, ..] =>
      match (input, container) {
        (Array(needle_arr), Array(haystack)) =>
          Iter::singleton(
            Json::boolean(array_contains_all(haystack, needle_arr)),
          )
        _ => Iter::singleton(Json::boolean(json_contains(container, input)))
      }
  }
}