use simd_json::OwnedValue as Value;

use super::builtins::Builtin;

/// Object key - either a static string or a dynamic filter expression
#[derive(Debug, Clone, PartialEq)]
pub enum ObjectKey {
    Static(String),       // identifier or "quoted-string"
    Dynamic(Box<Filter>), // (.expr) - evaluated at runtime
}

/// Parameterized function calls like map(filter) and select(filter)
#[derive(Debug, Clone, PartialEq)]
pub enum FunctionCall {
    Map(Box<Filter>),
    Select(Box<Filter>),
    SortBy(Box<Filter>),
    GroupBy(Box<Filter>),
    UniqueBy(Box<Filter>),
    MinBy(Box<Filter>),
    MaxBy(Box<Filter>),
    Has(String),              // has("key") - check if object has key
    Split(String),            // split("delim") - split string into array
    Join(String),             // join("sep") - join array into string
    StartsWith(String),       // startswith("prefix")
    EndsWith(String),         // endswith("suffix")
    Contains(Box<Filter>),    // contains(value) - check if contains
    WithEntries(Box<Filter>), // with_entries(f) - transform object entries
}

/// Comparison operators
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum CompareOp {
    Eq, // ==
    Ne, // !=
    Lt, // <
    Le, // <=
    Gt, // >
    Ge, // >=
}

/// Boolean operators
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum BoolOp {
    And,
    Or,
}

#[derive(Debug, Clone, PartialEq)]
pub enum Filter {
    Identity,
    Field(String),
    Index(i64),
    Slice(Option<i64>, Option<i64>), // .[2:5], .[:-1], .[2:], etc.
    Iterate,
    Optional(Box<Filter>), // .foo? - suppress errors, return null/empty
    Pipe(Box<Filter>, Box<Filter>),
    Builtin(Builtin),
    Function(FunctionCall),                       // map(f), select(f)
    Literal(Value),                               // numbers, strings, true, false, null
    Compare(Box<Filter>, CompareOp, Box<Filter>), // .x == 5, .name != "foo"
    BoolOp(Box<Filter>, BoolOp, Box<Filter>),     // .x and .y, .a or .b
    Array(Vec<Filter>),                           // [.foo, .bar]
    Object(Vec<(ObjectKey, Filter)>),             // {key: .value} with static or dynamic keys
    IfThenElse {
        // if cond then a else b end
        condition: Box<Filter>,
        then_branch: Box<Filter>,
        else_branch: Box<Filter>,
    },
    Alternative(Box<Filter>, Box<Filter>), // a // b - return b if a is null/false
}
