///|
/// Evaluate an expression with input JSON, returns iterator of results
pub fn eval(expr : Expr, input : Json) -> Iter[Json] raise {
  eval_with_env(expr, input, Env::new())
}

///|
/// Evaluate with environment
fn eval_with_env(
  expr : Expr,
  input : Json,
  env : Env,
) -> Iter[Json] raise InterpreterError {
  match expr {
    // Identity returns input as-is
    Identity => Iter::singleton(input)

    // Literals ignore input and return the literal value
    Literal(lit) => Iter::singleton(eval_literal(lit))

    // Pipe: feed results of left into right
    Pipe(left, right) => {
      // Special case: if left is a function definition, update env
      if left is FunctionDef(name, params, body) {
        let new_env = env.set_function(name, body, params)
        return eval_with_env(right, input, new_env)
      }
      let left_results = eval_with_env(left, input, env).collect()
      let right_iters : Array[Iter[Json]] = []
      for v in left_results {
        right_iters.push(eval_with_env(right, v, env))
      }
      right_iters.iter().flatten()
    }

    // Comma: produce multiple outputs
    Comma(left, right) =>
      Iter::concat(
        eval_with_env(left, input, env),
        eval_with_env(right, input, env),
      )

    // Field access
    Key(key) => eval_key_access(key, input)

    // Array indexing and iteration
    Index(indices) => eval_index_access(indices, input)

    // Array slicing
    Slice(start, end) => eval_slice_access(start, end, input)

    // Optional: suppress errors and return empty on failure
    Optional(inner) => eval_optional(inner, input, env)

    // Array construction
    ArrayConstruct(expr_opt) => eval_array_construct(expr_opt, input, env)

    // Object construction
    ObjectConstruct(pairs) => eval_object_construct(pairs, input, env)

    // Binary operations
    Operation(left, op, right) =>
      eval_operation_expr(left, op, right, input, env)

    // Built-in functions
    Length => eval_length(input)
    Keys => eval_keys(input)
    Values => eval_values(input)
    Type => eval_type(input)
    Empty => eval_empty()
    Not => eval_not(input)

    // Array functions
    Map(inner) => eval_map(inner, input, env)
    Select(condition) => eval_select(condition, input, env)
    Sort => eval_sort(input)
    Reverse => eval_reverse(input)
    Flatten(depth_opt) => eval_flatten(depth_opt, input)
    Unique => eval_unique(input)

    // Numeric functions
    Add => eval_add(input)
    Floor => eval_floor(input)
    Sqrt => eval_sqrt(input)
    Min => eval_min(input)
    Max => eval_max(input)

    // Control flow
    IfThenElse(cond, then_expr, else_expr) =>
      eval_if_then_else(cond, then_expr, else_expr, input, env)
    TryCatch(try_expr, catch_opt) =>
      eval_try_catch(try_expr, catch_opt, input, env)

    // Variables
    Variable(name) => eval_variable(name, env)

    // Recursive descent
    Recurse => recurse_all(input)

    // Alternative operator: return left, or right if left produces empty/null
    Alternative(left, right) => eval_alternative(left, right, input, env)

    // Compound assignment operators: path += value
    AddAssign(path, value_expr) =>
      eval_compound_assign(path, Add, value_expr, input, env)
    SubAssign(path, value_expr) =>
      eval_compound_assign(path, Subtract, value_expr, input, env)
    MulAssign(path, value_expr) =>
      eval_compound_assign(path, Multiply, value_expr, input, env)
    DivAssign(path, value_expr) =>
      eval_compound_assign(path, Divide, value_expr, input, env)
    ModAssign(path, value_expr) =>
      eval_compound_assign(path, Modulo, value_expr, input, env)
    AltAssign(path, value_expr) => eval_alt_assign(path, value_expr, input, env)

    // Format functions: @base64, @uri, etc.
    Format(format_name) => eval_format(format_name, input)

    // String interpolation: "text \(expr) more"
    StringInterpolation(parts) => eval_string_interpolation(parts, input, env)

    // Function definition: this is tricky because we need to update env
    // We handle this specially in the Pipe case when left is FunctionDef
    FunctionDef(_name, _params, _body) =>
      // Should not be evaluated directly, only through Pipe
      Iter::singleton(input)

    // Function call: lookup and execute
    FunctionCall(name, args) => eval_function_call(name, args, input, env)

    // As pattern: bind variable and continue with body
    As(expr, var_name, body) => eval_as(expr, var_name, body, input, env)

    // Reduce: aggregate using accumulator
    Reduce(expr, var_name, init_expr, update_expr) =>
      eval_reduce(expr, var_name, init_expr, update_expr, input, env)

    // SortBy: sort array by expression result
    SortBy(expr) => eval_sort_by(expr, input, env)

    // GroupBy: group array elements by expression result
    GroupBy(expr) => eval_group_by(expr, input, env)

    // Update operator: modify value in place
    Update(path, update_expr) => eval_update(path, update_expr, input, env)

    // Assign operator: set value at path
    Assign(path, value_expr) => eval_assign(path, value_expr, input, env)

    // RecurseWith: custom recursion with function
    RecurseWith(f, cond) => eval_recurse_with(f, cond, input, env)

    // Walk: recursively apply function to all values
    Walk(f) => eval_walk(f, input, env)

    // Path expression: get path to a value
    Path(expr) => eval_path(expr)

    // Additional numeric functions
    Round => eval_round(input)
    Ceil => eval_ceil(input)
    Abs => eval_abs(input)

    // String operations
    Split(sep) => eval_split(sep, input)
    Join(sep) => eval_join(sep, input)
    StartsWith(prefix) => eval_starts_with(prefix, input)
    EndsWith(suffix) => eval_ends_with(suffix, input)
    Contains(expr) => eval_contains(expr, input, env)
    Inside(expr) => eval_inside(expr, input, env)

    // Object/Array operations
    Has(key) => eval_has(key, input)
    In(expr) => eval_in(expr, input, env)
    ToEntries => eval_to_entries(input)
    FromEntries => eval_from_entries(input)
    WithEntries(expr) => eval_with_entries(expr, input, env)

    // Iteration helpers
    Range(n) => eval_range(n)
    First => eval_first(input)
    Last => eval_last(input)
    IndicesOf(expr) => eval_indices_of(expr, input, env)
    IndexOf(expr) => eval_index_of(expr, input, env)

    // Predicates
    Any => eval_any(input)
    All => eval_all(input)

    // String trimming
    LTrimStr(prefix) => eval_ltrim_str(prefix, input)
    RTrimStr(suffix) => eval_rtrim_str(suffix, input)
    AsciiUpcase => eval_ascii_upcase(input)
    AsciiDowncase => eval_ascii_downcase(input)

    // Array functions
    Nth(n) => eval_nth(n, input)
    RIndex(search_expr) => eval_rindex(search_expr, input, env)

    // Path functions
    Paths => eval_paths(input)
    LeafPaths => eval_leaf_paths(input)
    GetPath(path_expr) => eval_get_path(path_expr, input, env)
    SetPath(path_expr, value_expr) =>
      eval_set_path(path_expr, value_expr, input, env)
    DelPaths(paths_expr) => eval_del_paths(paths_expr, input, env)

    // Control flow
    Limit(n, expr) => eval_limit(n, expr, input, env)
    Until(cond_expr, update_expr) =>
      eval_until(cond_expr, update_expr, input, env)
    While(cond_expr, update_expr) =>
      eval_while(cond_expr, update_expr, input, env)

    // Math functions
    Pow(exp_expr) => eval_pow(exp_expr, input, env)
    Log => eval_log(input)
    Exp => eval_exp(input)
    Sin => eval_sin(input)
    Cos => eval_cos(input)
    Tan => eval_tan(input)
    Asin => eval_asin(input)
    Acos => eval_acos(input)
    Atan => eval_atan(input)

    // Regex functions (simple implementations)
    Test(pattern) => eval_test(pattern, input)
    Match(pattern) => eval_match(pattern, input)
    Capture(pattern) => eval_capture(pattern, input)
    Splits(pattern) => eval_splits(pattern, input)
    Sub(pattern, replacement) => eval_sub(pattern, replacement, input)
    GSub(pattern, replacement) => eval_gsub(pattern, replacement, input)

    // Newly added features - corner case implementations
    MapValues(expr) => eval_map_values(expr, input, env)
    RangeFromTo(from_expr, to_expr) =>
      eval_range_from_to(from_expr, to_expr, input, env)
    RangeWithStep(from_expr, to_expr, step_expr) =>
      eval_range_with_step(from_expr, to_expr, step_expr, input, env)
    FirstGen(gen_expr) => eval_first_gen(gen_expr, input, env)
    LastGen(gen_expr) => eval_last_gen(gen_expr, input, env)
    Repeat(expr) => eval_repeat(expr, input, env)
    Explode => eval_explode(input)
    Implode => eval_implode(input)
    ToJsonString => eval_to_json_string(input)
    FromJsonString => eval_from_json_string(input)
    UniqueBy(expr) => eval_unique_by(expr, input, env)
    MinBy(expr) => eval_min_by(expr, input, env)
    MaxBy(expr) => eval_max_by(expr, input, env)
    Combinations => eval_combinations(input)
    Transpose => eval_transpose(input)
    AnyGen(gen_expr, cond_expr) => eval_any_gen(gen_expr, cond_expr, input, env)
    AllGen(gen_expr, cond_expr) => eval_all_gen(gen_expr, cond_expr, input, env)
    Foreach(gen_expr, var_name, init_expr, update_expr, extract_expr) =>
      eval_foreach(
        gen_expr, var_name, init_expr, update_expr, extract_expr, input, env,
      )
    PathsWithFilter(filter_expr) =>
      eval_paths_with_filter(filter_expr, input, env)
    Scan(pattern) => eval_scan(pattern, input)
  }
}