mod accessor;
mod construction;
mod primitives;

use nom::{
    Parser,
    branch::alt,
    character::complete::{char, multispace0},
    combinator::all_consuming,
    multi::many0,
    sequence::{delimited, preceded},
};

use simd_json::OwnedValue as Value;
use simd_json::StaticNode;

use super::ast::{Filter, FunctionCall};
use super::builtins::Builtin;
use crate::error::ParseError;

pub fn parse(input: &str) -> Result<Filter, ParseError> {
    match all_consuming(parse_filter).parse(input) {
        Ok((_, filter)) => Ok(filter),
        Err(e) => {
            let (position, message) = match &e {
                nom::Err::Error(e) | nom::Err::Failure(e) => {
                    let pos = input.len() - e.input.len();
                    let msg = if e.input.is_empty() {
                        "unexpected end of input".to_string()
                    } else {
                        format!("unexpected '{}'", e.input.chars().next().unwrap_or('?'))
                    };
                    (pos, msg)
                }
                nom::Err::Incomplete(_) => (input.len(), "incomplete input".to_string()),
            };
            Err(ParseError { message, position })
        }
    }
}

fn parse_filter(input: &str) -> nom::IResult<&str, Filter> {
    delimited(multispace0, parse_pipe_chain, multispace0).parse(input)
}

pub(crate) fn parse_pipe_chain(input: &str) -> nom::IResult<&str, Filter> {
    let (input, first) = parse_alternative_expr(input)?;
    let (input, rest) = many0(preceded(
        delimited(multispace0, char('|'), multispace0),
        parse_alternative_expr,
    ))
    .parse(input)?;

    let filter = rest
        .into_iter()
        .fold(first, |acc, f| Filter::Pipe(Box::new(acc), Box::new(f)));

    Ok((input, filter))
}

fn parse_alternative_expr(input: &str) -> nom::IResult<&str, Filter> {
    use nom::bytes::complete::tag;

    let (input, first) = parse_or_expr(input)?;
    let (input, rest) = many0(preceded(
        delimited(multispace0, tag("//"), multispace0),
        parse_or_expr,
    ))
    .parse(input)?;

    let filter = rest.into_iter().fold(first, |acc, f| {
        Filter::Alternative(Box::new(acc), Box::new(f))
    });

    Ok((input, filter))
}

fn parse_or_expr(input: &str) -> nom::IResult<&str, Filter> {
    use super::ast::BoolOp;
    use nom::bytes::complete::tag;

    let (input, first) = parse_and_expr(input)?;
    let (input, rest) = many0(preceded(
        delimited(multispace0, tag("or"), multispace0),
        parse_and_expr,
    ))
    .parse(input)?;

    let filter = rest.into_iter().fold(first, |acc, f| {
        Filter::BoolOp(Box::new(acc), BoolOp::Or, Box::new(f))
    });

    Ok((input, filter))
}

fn parse_and_expr(input: &str) -> nom::IResult<&str, Filter> {
    use super::ast::BoolOp;
    use nom::bytes::complete::tag;

    let (input, first) = parse_comparison_expr(input)?;
    let (input, rest) = many0(preceded(
        delimited(multispace0, tag("and"), multispace0),
        parse_comparison_expr,
    ))
    .parse(input)?;

    let filter = rest.into_iter().fold(first, |acc, f| {
        Filter::BoolOp(Box::new(acc), BoolOp::And, Box::new(f))
    });

    Ok((input, filter))
}

fn parse_comparison_expr(input: &str) -> nom::IResult<&str, Filter> {
    let (input, left) = parse_filter_term(input)?;

    // Try to parse a comparison operator
    let (input, _) = multispace0(input)?;
    if let Ok((input, op)) = primitives::parse_compare_op(input) {
        let (input, _) = multispace0(input)?;
        let (input, right) = parse_filter_term(input)?;
        Ok((input, Filter::Compare(Box::new(left), op, Box::new(right))))
    } else {
        Ok((input, left))
    }
}

fn parse_filter_term(input: &str) -> nom::IResult<&str, Filter> {
    alt((
        construction::parse_array_construction,
        construction::parse_object_construction,
        parse_if_then_else,
        parse_function_call,
        parse_literal,
        accessor::parse_accessor_chain,
        parse_builtin,
    ))
    .parse(input)
}

fn parse_if_then_else(input: &str) -> nom::IResult<&str, Filter> {
    use nom::bytes::complete::tag;

    let (rest, _) = tag("if").parse(input)?;
    // Ensure "if" is not part of a longer identifier
    if rest
        .chars()
        .next()
        .is_some_and(|c| c.is_alphanumeric() || c == '_')
    {
        return Err(nom::Err::Error(nom::error::Error::new(
            input,
            nom::error::ErrorKind::Tag,
        )));
    }

    let (rest, _) = multispace0(rest)?;
    let (rest, condition) = parse_pipe_chain(rest)?;
    let (rest, _) = multispace0(rest)?;

    let (rest, _) = tag("then").parse(rest)?;
    let (rest, _) = multispace0(rest)?;
    let (rest, then_branch) = parse_pipe_chain(rest)?;
    let (rest, _) = multispace0(rest)?;

    let (rest, _) = tag("else").parse(rest)?;
    let (rest, _) = multispace0(rest)?;
    let (rest, else_branch) = parse_pipe_chain(rest)?;
    let (rest, _) = multispace0(rest)?;

    let (rest, _) = tag("end").parse(rest)?;

    Ok((
        rest,
        Filter::IfThenElse {
            condition: Box::new(condition),
            then_branch: Box::new(then_branch),
            else_branch: Box::new(else_branch),
        },
    ))
}

fn parse_literal(input: &str) -> nom::IResult<&str, Filter> {
    alt((
        parse_null_literal,
        parse_bool_literal,
        parse_string_literal,
        parse_number_literal,
    ))
    .parse(input)
}

fn parse_null_literal(input: &str) -> nom::IResult<&str, Filter> {
    use nom::bytes::complete::tag;
    let (rest, _) = tag("null").parse(input)?;
    // Make sure it's not followed by alphanumeric (e.g., "nullify")
    if rest
        .chars()
        .next()
        .is_some_and(|c| c.is_alphanumeric() || c == '_')
    {
        return Err(nom::Err::Error(nom::error::Error::new(
            input,
            nom::error::ErrorKind::Tag,
        )));
    }
    Ok((rest, Filter::Literal(Value::Static(StaticNode::Null))))
}

fn parse_bool_literal(input: &str) -> nom::IResult<&str, Filter> {
    use nom::bytes::complete::tag;
    let true_parser = tag("true").map(|_| Filter::Literal(Value::Static(StaticNode::Bool(true))));
    let false_parser =
        tag("false").map(|_| Filter::Literal(Value::Static(StaticNode::Bool(false))));

    let (rest, filter) = alt((true_parser, false_parser)).parse(input)?;
    // Make sure it's not followed by alphanumeric
    if rest
        .chars()
        .next()
        .is_some_and(|c| c.is_alphanumeric() || c == '_')
    {
        return Err(nom::Err::Error(nom::error::Error::new(
            input,
            nom::error::ErrorKind::Tag,
        )));
    }
    Ok((rest, filter))
}

fn parse_string_literal(input: &str) -> nom::IResult<&str, Filter> {
    let (rest, s) = primitives::parse_string_literal(input)?;
    Ok((rest, Filter::Literal(Value::String(s))))
}

fn parse_number_literal(input: &str) -> nom::IResult<&str, Filter> {
    let (rest, n) = primitives::parse_number(input)?;
    Ok((rest, Filter::Literal(Value::Static(n))))
}

fn parse_function_call(input: &str) -> nom::IResult<&str, Filter> {
    let (rest, name) = primitives::identifier(input)?;

    // Only handle known parameterized functions
    if !matches!(
        name.as_str(),
        "map"
            | "select"
            | "sort_by"
            | "group_by"
            | "unique_by"
            | "min_by"
            | "max_by"
            | "has"
            | "split"
            | "join"
            | "startswith"
            | "endswith"
            | "contains"
            | "with_entries"
    ) {
        return Err(nom::Err::Error(nom::error::Error::new(
            input,
            nom::error::ErrorKind::Tag,
        )));
    }

    // Parse opening paren
    let (rest, _) = multispace0(rest)?;
    let (rest, _) = char('(').parse(rest)?;
    let (rest, _) = multispace0(rest)?;

    // Functions that take a string argument
    if matches!(
        name.as_str(),
        "has" | "split" | "join" | "startswith" | "endswith"
    ) {
        let (rest, arg) = primitives::parse_string_literal(rest)?;
        let (rest, _) = multispace0(rest)?;
        let (rest, _) = char(')').parse(rest)?;
        let func = match name.as_str() {
            "has" => FunctionCall::Has(arg),
            "split" => FunctionCall::Split(arg),
            "join" => FunctionCall::Join(arg),
            "startswith" => FunctionCall::StartsWith(arg),
            "endswith" => FunctionCall::EndsWith(arg),
            _ => unreachable!(),
        };
        return Ok((rest, Filter::Function(func)));
    }

    // Parse filter argument
    let (rest, inner) = parse_pipe_chain(rest)?;
    let (rest, _) = multispace0(rest)?;
    let (rest, _) = char(')').parse(rest)?;

    let func = match name.as_str() {
        "map" => FunctionCall::Map(Box::new(inner)),
        "select" => FunctionCall::Select(Box::new(inner)),
        "sort_by" => FunctionCall::SortBy(Box::new(inner)),
        "group_by" => FunctionCall::GroupBy(Box::new(inner)),
        "unique_by" => FunctionCall::UniqueBy(Box::new(inner)),
        "min_by" => FunctionCall::MinBy(Box::new(inner)),
        "max_by" => FunctionCall::MaxBy(Box::new(inner)),
        "contains" => FunctionCall::Contains(Box::new(inner)),
        "with_entries" => FunctionCall::WithEntries(Box::new(inner)),
        _ => unreachable!(),
    };

    Ok((rest, Filter::Function(func)))
}

fn parse_builtin(input: &str) -> nom::IResult<&str, Filter> {
    let (rest, name) = primitives::identifier(input)?;
    match Builtin::from_name(&name) {
        Some(b) => Ok((rest, Filter::Builtin(b))),
        None => Err(nom::Err::Error(nom::error::Error::new(
            input,
            nom::error::ErrorKind::Tag,
        ))),
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::filter::ast::ObjectKey;

    // Identity tests
    #[test]
    fn test_identity() {
        assert_eq!(parse("."), Ok(Filter::Identity));
    }

    #[test]
    fn test_identity_with_whitespace() {
        assert_eq!(parse("  .  "), Ok(Filter::Identity));
    }

    // Field access tests
    #[test]
    fn test_field() {
        assert_eq!(parse(".foo"), Ok(Filter::Field("foo".to_string())));
        assert_eq!(parse(".name"), Ok(Filter::Field("name".to_string())));
    }

    #[test]
    fn test_field_with_whitespace() {
        assert_eq!(parse("  .foo  "), Ok(Filter::Field("foo".to_string())));
    }

    #[test]
    fn test_field_chain() {
        assert_eq!(
            parse(".foo.bar"),
            Ok(Filter::Pipe(
                Box::new(Filter::Field("foo".to_string())),
                Box::new(Filter::Field("bar".to_string()))
            ))
        );
    }

    #[test]
    fn test_field_chain_triple() {
        assert_eq!(
            parse(".foo.bar.baz"),
            Ok(Filter::Pipe(
                Box::new(Filter::Pipe(
                    Box::new(Filter::Field("foo".to_string())),
                    Box::new(Filter::Field("bar".to_string()))
                )),
                Box::new(Filter::Field("baz".to_string()))
            ))
        );
    }

    #[test]
    fn test_underscore_field() {
        assert_eq!(
            parse("._private"),
            Ok(Filter::Field("_private".to_string()))
        );
    }

    #[test]
    fn test_field_with_numbers() {
        assert_eq!(
            parse(".field123"),
            Ok(Filter::Field("field123".to_string()))
        );
    }

    #[test]
    fn test_unicode_field() {
        assert_eq!(parse(".名前"), Ok(Filter::Field("名前".to_string())));
    }

    #[test]
    fn test_keyword_as_field() {
        assert_eq!(parse(".null"), Ok(Filter::Field("null".to_string())));
        assert_eq!(parse(".true"), Ok(Filter::Field("true".to_string())));
        assert_eq!(parse(".false"), Ok(Filter::Field("false".to_string())));
    }

    // Array index tests
    #[test]
    fn test_index() {
        assert_eq!(parse(".[0]"), Ok(Filter::Index(0)));
        assert_eq!(parse(".[1]"), Ok(Filter::Index(1)));
        assert_eq!(parse(".[42]"), Ok(Filter::Index(42)));
    }

    #[test]
    fn test_negative_index() {
        assert_eq!(parse(".[-1]"), Ok(Filter::Index(-1)));
        assert_eq!(parse(".[-10]"), Ok(Filter::Index(-10)));
    }

    #[test]
    fn test_index_with_whitespace() {
        assert_eq!(parse(".[ 0 ]"), Ok(Filter::Index(0)));
        assert_eq!(parse(".[  -1  ]"), Ok(Filter::Index(-1)));
    }

    // Iterate tests
    #[test]
    fn test_iterate() {
        assert_eq!(parse(".[]"), Ok(Filter::Iterate));
    }

    #[test]
    fn test_iterate_with_whitespace() {
        assert_eq!(parse(".[  ]"), Ok(Filter::Iterate));
    }

    // Quoted field tests
    #[test]
    fn test_quoted_field() {
        assert_eq!(parse(".[\"foo\"]"), Ok(Filter::Field("foo".to_string())));
    }

    #[test]
    fn test_quoted_field_special_chars() {
        assert_eq!(
            parse(".[\"foo-bar\"]"),
            Ok(Filter::Field("foo-bar".to_string()))
        );
        assert_eq!(
            parse(".[\"with space\"]"),
            Ok(Filter::Field("with space".to_string()))
        );
        assert_eq!(parse(".[\"a.b\"]"), Ok(Filter::Field("a.b".to_string())));
    }

    #[test]
    fn test_quoted_field_empty() {
        assert_eq!(parse(".[\"\"]"), Ok(Filter::Field("".to_string())));
    }

    #[test]
    fn test_quoted_field_unicode() {
        assert_eq!(
            parse(".[\"日本語\"]"),
            Ok(Filter::Field("日本語".to_string()))
        );
    }

    #[test]
    fn test_quoted_field_with_whitespace() {
        assert_eq!(parse(".[ \"foo\" ]"), Ok(Filter::Field("foo".to_string())));
    }

    #[test]
    fn test_quoted_field_escapes() {
        assert_eq!(parse(".[\"a\\nb\"]"), Ok(Filter::Field("a\nb".to_string())));
        assert_eq!(parse(".[\"a\\tb\"]"), Ok(Filter::Field("a\tb".to_string())));
        assert_eq!(
            parse(".[\"a\\\"b\"]"),
            Ok(Filter::Field("a\"b".to_string()))
        );
        assert_eq!(
            parse(".[\"a\\\\b\"]"),
            Ok(Filter::Field("a\\b".to_string()))
        );
    }

    #[test]
    fn test_quoted_field_unicode_escape() {
        assert_eq!(parse(".[\"\\u0041\"]"), Ok(Filter::Field("A".to_string())));
    }

    // Chaining tests
    #[test]
    fn test_chain_field_index() {
        assert_eq!(
            parse(".foo[0]"),
            Ok(Filter::Pipe(
                Box::new(Filter::Field("foo".to_string())),
                Box::new(Filter::Index(0))
            ))
        );
    }

    #[test]
    fn test_chain_index_field() {
        assert_eq!(
            parse(".[0].foo"),
            Ok(Filter::Pipe(
                Box::new(Filter::Index(0)),
                Box::new(Filter::Field("foo".to_string()))
            ))
        );
    }

    #[test]
    fn test_chain_index_index() {
        assert_eq!(
            parse(".[0][1]"),
            Ok(Filter::Pipe(
                Box::new(Filter::Index(0)),
                Box::new(Filter::Index(1))
            ))
        );
    }

    #[test]
    fn test_chain_iterate_index() {
        assert_eq!(
            parse(".[][0]"),
            Ok(Filter::Pipe(
                Box::new(Filter::Iterate),
                Box::new(Filter::Index(0))
            ))
        );
    }

    #[test]
    fn test_chain_quoted_quoted() {
        assert_eq!(
            parse(".[\"a\"][\"b\"]"),
            Ok(Filter::Pipe(
                Box::new(Filter::Field("a".to_string())),
                Box::new(Filter::Field("b".to_string()))
            ))
        );
    }

    #[test]
    fn test_chain_mixed() {
        assert_eq!(
            parse(".items[0].id"),
            Ok(Filter::Pipe(
                Box::new(Filter::Pipe(
                    Box::new(Filter::Field("items".to_string())),
                    Box::new(Filter::Index(0))
                )),
                Box::new(Filter::Field("id".to_string()))
            ))
        );
    }

    #[test]
    fn test_chain_iterate_field() {
        assert_eq!(
            parse(".items[].id"),
            Ok(Filter::Pipe(
                Box::new(Filter::Pipe(
                    Box::new(Filter::Field("items".to_string())),
                    Box::new(Filter::Iterate)
                )),
                Box::new(Filter::Field("id".to_string()))
            ))
        );
    }

    #[test]
    fn test_chain_mixed_dot_bracket() {
        assert_eq!(
            parse(".foo[\"bar\"].baz"),
            Ok(Filter::Pipe(
                Box::new(Filter::Pipe(
                    Box::new(Filter::Field("foo".to_string())),
                    Box::new(Filter::Field("bar".to_string()))
                )),
                Box::new(Filter::Field("baz".to_string()))
            ))
        );
    }

    // Error tests
    #[test]
    fn test_trailing_dot_error() {
        assert!(parse(".foo.").is_err());
    }

    #[test]
    fn test_digit_start_error() {
        assert!(parse(".123").is_err());
    }

    #[test]
    fn test_empty() {
        assert!(parse("").is_err());
    }

    #[test]
    fn test_invalid() {
        assert!(parse("..").is_err());
        assert!(parse("foo").is_err());
    }

    #[test]
    fn test_unclosed_bracket() {
        assert!(parse(".[").is_err());
        assert!(parse(".[0").is_err());
    }

    #[test]
    fn test_unclosed_string() {
        assert!(parse(".[\"unclosed").is_err());
    }

    #[test]
    fn test_single_quotes_error() {
        assert!(parse(".['field']").is_err());
    }

    #[test]
    fn test_float_error() {
        assert!(parse(".[0.5]").is_err());
    }

    #[test]
    fn test_integer_overflow() {
        assert!(parse(".[99999999999999999999]").is_err());
    }

    // Explicit pipe tests
    #[test]
    fn test_pipe_simple() {
        assert_eq!(
            parse(".foo | .bar"),
            Ok(Filter::Pipe(
                Box::new(Filter::Field("foo".to_string())),
                Box::new(Filter::Field("bar".to_string()))
            ))
        );
    }

    #[test]
    fn test_pipe_with_whitespace() {
        assert_eq!(
            parse(".foo|.bar"),
            Ok(Filter::Pipe(
                Box::new(Filter::Field("foo".to_string())),
                Box::new(Filter::Field("bar".to_string()))
            ))
        );
        assert_eq!(
            parse(".foo  |  .bar"),
            Ok(Filter::Pipe(
                Box::new(Filter::Field("foo".to_string())),
                Box::new(Filter::Field("bar".to_string()))
            ))
        );
    }

    #[test]
    fn test_pipe_triple() {
        // .a | .b | .c  →  Pipe(Pipe(a, b), c)
        assert_eq!(
            parse(".a | .b | .c"),
            Ok(Filter::Pipe(
                Box::new(Filter::Pipe(
                    Box::new(Filter::Field("a".to_string())),
                    Box::new(Filter::Field("b".to_string()))
                )),
                Box::new(Filter::Field("c".to_string()))
            ))
        );
    }

    #[test]
    fn test_pipe_with_iterate() {
        // .users | .[]
        assert_eq!(
            parse(".users | .[]"),
            Ok(Filter::Pipe(
                Box::new(Filter::Field("users".to_string())),
                Box::new(Filter::Iterate)
            ))
        );
    }

    #[test]
    fn test_pipe_with_index() {
        // .items | .[0]
        assert_eq!(
            parse(".items | .[0]"),
            Ok(Filter::Pipe(
                Box::new(Filter::Field("items".to_string())),
                Box::new(Filter::Index(0))
            ))
        );
    }

    #[test]
    fn test_pipe_mixed_implicit_explicit() {
        // .users[].name | .foo combines implicit and explicit pipes
        assert_eq!(
            parse(".users[].name | .foo"),
            Ok(Filter::Pipe(
                Box::new(Filter::Pipe(
                    Box::new(Filter::Pipe(
                        Box::new(Filter::Field("users".to_string())),
                        Box::new(Filter::Iterate)
                    )),
                    Box::new(Filter::Field("name".to_string()))
                )),
                Box::new(Filter::Field("foo".to_string()))
            ))
        );
    }

    #[test]
    fn test_pipe_identity() {
        // . | .foo
        assert_eq!(
            parse(". | .foo"),
            Ok(Filter::Pipe(
                Box::new(Filter::Identity),
                Box::new(Filter::Field("foo".to_string()))
            ))
        );
    }

    // Pipe error tests
    #[test]
    fn test_pipe_trailing_error() {
        assert!(parse(".foo |").is_err());
    }

    #[test]
    fn test_pipe_leading_error() {
        assert!(parse("| .foo").is_err());
    }

    #[test]
    fn test_pipe_double_error() {
        assert!(parse(".foo || .bar").is_err());
    }

    #[test]
    fn test_pipe_empty_segment_error() {
        assert!(parse(".foo | | .bar").is_err());
    }

    // Builtin parser tests
    #[test]
    fn test_builtin_standalone() {
        assert_eq!(parse("length"), Ok(Filter::Builtin(Builtin::Length)));
        assert_eq!(parse("keys"), Ok(Filter::Builtin(Builtin::Keys)));
        assert_eq!(parse("type"), Ok(Filter::Builtin(Builtin::Type)));
    }

    #[test]
    fn test_builtin_with_whitespace() {
        assert_eq!(parse("  length  "), Ok(Filter::Builtin(Builtin::Length)));
    }

    #[test]
    fn test_builtin_after_pipe() {
        // .foo | length
        assert_eq!(
            parse(".foo | length"),
            Ok(Filter::Pipe(
                Box::new(Filter::Field("foo".to_string())),
                Box::new(Filter::Builtin(Builtin::Length))
            ))
        );
    }

    #[test]
    fn test_builtin_before_pipe() {
        // length | .foo - unusual but valid
        assert_eq!(
            parse("length | .foo"),
            Ok(Filter::Pipe(
                Box::new(Filter::Builtin(Builtin::Length)),
                Box::new(Filter::Field("foo".to_string()))
            ))
        );
    }

    #[test]
    fn test_builtin_chain() {
        // .items | length
        assert_eq!(
            parse(".items | length"),
            Ok(Filter::Pipe(
                Box::new(Filter::Field("items".to_string())),
                Box::new(Filter::Builtin(Builtin::Length))
            ))
        );
    }

    #[test]
    fn test_iterate_then_builtin() {
        // .[] | length
        assert_eq!(
            parse(".[] | length"),
            Ok(Filter::Pipe(
                Box::new(Filter::Iterate),
                Box::new(Filter::Builtin(Builtin::Length))
            ))
        );
    }

    #[test]
    fn test_builtin_then_iterate() {
        // keys | .[]
        assert_eq!(
            parse("keys | .[]"),
            Ok(Filter::Pipe(
                Box::new(Filter::Builtin(Builtin::Keys)),
                Box::new(Filter::Iterate)
            ))
        );
    }

    #[test]
    fn test_multiple_builtins() {
        // sort | reverse
        assert_eq!(
            parse("sort | reverse"),
            Ok(Filter::Pipe(
                Box::new(Filter::Builtin(Builtin::Sort)),
                Box::new(Filter::Builtin(Builtin::Reverse))
            ))
        );
    }

    #[test]
    fn test_all_builtins_parse() {
        assert_eq!(parse("length"), Ok(Filter::Builtin(Builtin::Length)));
        assert_eq!(parse("keys"), Ok(Filter::Builtin(Builtin::Keys)));
        assert_eq!(parse("values"), Ok(Filter::Builtin(Builtin::Values)));
        assert_eq!(parse("type"), Ok(Filter::Builtin(Builtin::Type)));
        assert_eq!(parse("first"), Ok(Filter::Builtin(Builtin::First)));
        assert_eq!(parse("last"), Ok(Filter::Builtin(Builtin::Last)));
        assert_eq!(parse("reverse"), Ok(Filter::Builtin(Builtin::Reverse)));
        assert_eq!(parse("sort"), Ok(Filter::Builtin(Builtin::Sort)));
        assert_eq!(parse("min"), Ok(Filter::Builtin(Builtin::Min)));
        assert_eq!(parse("max"), Ok(Filter::Builtin(Builtin::Max)));
        assert_eq!(parse("unique"), Ok(Filter::Builtin(Builtin::Unique)));
        assert_eq!(parse("flatten"), Ok(Filter::Builtin(Builtin::Flatten)));
        assert_eq!(parse("add"), Ok(Filter::Builtin(Builtin::Add)));
        assert_eq!(parse("empty"), Ok(Filter::Builtin(Builtin::Empty)));
        assert_eq!(parse("not"), Ok(Filter::Builtin(Builtin::Not)));
    }

    #[test]
    fn test_unknown_identifier_error() {
        // "notabuiltin" should fail
        assert!(parse("notabuiltin").is_err());
    }

    #[test]
    fn test_builtin_as_field_name() {
        // .length should be a field access, not a builtin
        assert_eq!(parse(".length"), Ok(Filter::Field("length".to_string())));
    }

    // Array construction tests
    #[test]
    fn test_array_construction_simple() {
        assert_eq!(
            parse("[.foo, .bar]"),
            Ok(Filter::Array(vec![
                Filter::Field("foo".to_string()),
                Filter::Field("bar".to_string()),
            ]))
        );
    }

    #[test]
    fn test_array_construction_empty() {
        assert_eq!(parse("[]"), Ok(Filter::Array(vec![])));
    }

    #[test]
    fn test_array_construction_single() {
        assert_eq!(
            parse("[.foo]"),
            Ok(Filter::Array(vec![Filter::Field("foo".to_string())]))
        );
    }

    #[test]
    fn test_array_construction_with_pipe() {
        // [.foo | length]
        assert_eq!(
            parse("[.foo | length]"),
            Ok(Filter::Array(vec![Filter::Pipe(
                Box::new(Filter::Field("foo".to_string())),
                Box::new(Filter::Builtin(Builtin::Length))
            )]))
        );
    }

    #[test]
    fn test_array_construction_with_iterate() {
        // [.[]]
        assert_eq!(parse("[.[]]"), Ok(Filter::Array(vec![Filter::Iterate])));
    }

    #[test]
    fn test_array_construction_nested() {
        // [[.x], [.y]]
        assert_eq!(
            parse("[[.x], [.y]]"),
            Ok(Filter::Array(vec![
                Filter::Array(vec![Filter::Field("x".to_string())]),
                Filter::Array(vec![Filter::Field("y".to_string())]),
            ]))
        );
    }

    #[test]
    fn test_array_construction_with_whitespace() {
        assert_eq!(
            parse("[ .foo , .bar ]"),
            Ok(Filter::Array(vec![
                Filter::Field("foo".to_string()),
                Filter::Field("bar".to_string()),
            ]))
        );
    }

    // Object construction tests
    #[test]
    fn test_object_construction_simple() {
        assert_eq!(
            parse("{name: .foo}"),
            Ok(Filter::Object(vec![(
                ObjectKey::Static("name".to_string()),
                Filter::Field("foo".to_string())
            )]))
        );
    }

    #[test]
    fn test_object_construction_empty() {
        assert_eq!(parse("{}"), Ok(Filter::Object(vec![])));
    }

    #[test]
    fn test_object_construction_multiple() {
        assert_eq!(
            parse("{name: .foo, age: .bar}"),
            Ok(Filter::Object(vec![
                (
                    ObjectKey::Static("name".to_string()),
                    Filter::Field("foo".to_string())
                ),
                (
                    ObjectKey::Static("age".to_string()),
                    Filter::Field("bar".to_string())
                ),
            ]))
        );
    }

    #[test]
    fn test_object_construction_quoted_key() {
        assert_eq!(
            parse("{\"my-key\": .foo}"),
            Ok(Filter::Object(vec![(
                ObjectKey::Static("my-key".to_string()),
                Filter::Field("foo".to_string())
            )]))
        );
    }

    #[test]
    fn test_object_construction_dynamic_key() {
        // {(.key): .value}
        assert_eq!(
            parse("{(.key): .value}"),
            Ok(Filter::Object(vec![(
                ObjectKey::Dynamic(Box::new(Filter::Field("key".to_string()))),
                Filter::Field("value".to_string())
            )]))
        );
    }

    #[test]
    fn test_object_construction_with_pipe() {
        // {count: .items | length}
        assert_eq!(
            parse("{count: .items | length}"),
            Ok(Filter::Object(vec![(
                ObjectKey::Static("count".to_string()),
                Filter::Pipe(
                    Box::new(Filter::Field("items".to_string())),
                    Box::new(Filter::Builtin(Builtin::Length))
                )
            )]))
        );
    }

    #[test]
    fn test_object_construction_with_whitespace() {
        assert_eq!(
            parse("{ name : .foo , age : .bar }"),
            Ok(Filter::Object(vec![
                (
                    ObjectKey::Static("name".to_string()),
                    Filter::Field("foo".to_string())
                ),
                (
                    ObjectKey::Static("age".to_string()),
                    Filter::Field("bar".to_string())
                ),
            ]))
        );
    }

    #[test]
    fn test_nested_object_in_array() {
        // [{a: .x}]
        assert_eq!(
            parse("[{a: .x}]"),
            Ok(Filter::Array(vec![Filter::Object(vec![(
                ObjectKey::Static("a".to_string()),
                Filter::Field("x".to_string())
            )])]))
        );
    }

    #[test]
    fn test_array_in_object() {
        // {point: [.x, .y]}
        assert_eq!(
            parse("{point: [.x, .y]}"),
            Ok(Filter::Object(vec![(
                ObjectKey::Static("point".to_string()),
                Filter::Array(vec![
                    Filter::Field("x".to_string()),
                    Filter::Field("y".to_string()),
                ])
            )]))
        );
    }

    // Optional access tests
    #[test]
    fn test_optional_field() {
        assert_eq!(
            parse(".foo?"),
            Ok(Filter::Optional(Box::new(Filter::Field("foo".to_string()))))
        );
    }

    #[test]
    fn test_optional_index() {
        assert_eq!(
            parse(".[0]?"),
            Ok(Filter::Optional(Box::new(Filter::Index(0))))
        );
    }

    #[test]
    fn test_optional_iterate() {
        assert_eq!(
            parse(".[]?"),
            Ok(Filter::Optional(Box::new(Filter::Iterate)))
        );
    }

    #[test]
    fn test_optional_chained() {
        // .foo?.bar - optional on foo, then bar
        assert_eq!(
            parse(".foo?.bar"),
            Ok(Filter::Pipe(
                Box::new(Filter::Optional(Box::new(Filter::Field("foo".to_string())))),
                Box::new(Filter::Field("bar".to_string()))
            ))
        );
    }

    #[test]
    fn test_optional_both() {
        // .foo?.bar?
        assert_eq!(
            parse(".foo?.bar?"),
            Ok(Filter::Pipe(
                Box::new(Filter::Optional(Box::new(Filter::Field("foo".to_string())))),
                Box::new(Filter::Optional(Box::new(Filter::Field("bar".to_string()))))
            ))
        );
    }

    #[test]
    fn test_optional_quoted_field() {
        assert_eq!(
            parse(".[\"foo\"]?"),
            Ok(Filter::Optional(Box::new(Filter::Field("foo".to_string()))))
        );
    }

    // Array slice tests
    #[test]
    fn test_slice_both() {
        assert_eq!(parse(".[2:5]"), Ok(Filter::Slice(Some(2), Some(5))));
    }

    #[test]
    fn test_slice_start_only() {
        assert_eq!(parse(".[2:]"), Ok(Filter::Slice(Some(2), None)));
    }

    #[test]
    fn test_slice_end_only() {
        assert_eq!(parse(".[:5]"), Ok(Filter::Slice(None, Some(5))));
    }

    #[test]
    fn test_slice_neither() {
        assert_eq!(parse(".[:]"), Ok(Filter::Slice(None, None)));
    }

    #[test]
    fn test_slice_negative_start() {
        assert_eq!(parse(".[-2:]"), Ok(Filter::Slice(Some(-2), None)));
    }

    #[test]
    fn test_slice_negative_end() {
        assert_eq!(parse(".[:-1]"), Ok(Filter::Slice(None, Some(-1))));
    }

    #[test]
    fn test_slice_both_negative() {
        assert_eq!(parse(".[-3:-1]"), Ok(Filter::Slice(Some(-3), Some(-1))));
    }

    #[test]
    fn test_slice_with_whitespace() {
        assert_eq!(parse(".[ 2 : 5 ]"), Ok(Filter::Slice(Some(2), Some(5))));
    }

    #[test]
    fn test_slice_chained() {
        assert_eq!(
            parse(".[2:5][0]"),
            Ok(Filter::Pipe(
                Box::new(Filter::Slice(Some(2), Some(5))),
                Box::new(Filter::Index(0))
            ))
        );
    }

    // Function call tests (map, select)
    #[test]
    fn test_map_basic() {
        assert_eq!(
            parse("map(.foo)"),
            Ok(Filter::Function(FunctionCall::Map(Box::new(
                Filter::Field("foo".to_string())
            ))))
        );
    }

    #[test]
    fn test_map_with_pipe() {
        assert_eq!(
            parse("map(.a | .b)"),
            Ok(Filter::Function(FunctionCall::Map(Box::new(Filter::Pipe(
                Box::new(Filter::Field("a".to_string())),
                Box::new(Filter::Field("b".to_string()))
            )))))
        );
    }

    #[test]
    fn test_map_identity() {
        assert_eq!(
            parse("map(.)"),
            Ok(Filter::Function(FunctionCall::Map(Box::new(
                Filter::Identity
            ))))
        );
    }

    #[test]
    fn test_map_with_whitespace() {
        assert_eq!(
            parse("map( .foo )"),
            Ok(Filter::Function(FunctionCall::Map(Box::new(
                Filter::Field("foo".to_string())
            ))))
        );
    }

    #[test]
    fn test_select_basic() {
        assert_eq!(
            parse("select(.active)"),
            Ok(Filter::Function(FunctionCall::Select(Box::new(
                Filter::Field("active".to_string())
            ))))
        );
    }

    #[test]
    fn test_select_with_pipe() {
        assert_eq!(
            parse("select(.a | .b)"),
            Ok(Filter::Function(FunctionCall::Select(Box::new(
                Filter::Pipe(
                    Box::new(Filter::Field("a".to_string())),
                    Box::new(Filter::Field("b".to_string()))
                )
            ))))
        );
    }

    #[test]
    fn test_function_in_pipe_chain() {
        // .items | map(.name) | select(.)
        assert_eq!(
            parse(".items | map(.name)"),
            Ok(Filter::Pipe(
                Box::new(Filter::Field("items".to_string())),
                Box::new(Filter::Function(FunctionCall::Map(Box::new(
                    Filter::Field("name".to_string())
                ))))
            ))
        );
    }

    #[test]
    fn test_map_without_parens_fails() {
        // "map" without parentheses should fail (not a builtin)
        assert!(parse("map").is_err());
    }

    #[test]
    fn test_select_without_parens_fails() {
        // "select" without parentheses should fail
        assert!(parse("select").is_err());
    }

    #[test]
    fn test_map_nested_in_array() {
        assert_eq!(
            parse("[map(.x)]"),
            Ok(Filter::Array(vec![Filter::Function(FunctionCall::Map(
                Box::new(Filter::Field("x".to_string()))
            ))]))
        );
    }

    #[test]
    fn test_sort_by_basic() {
        assert_eq!(
            parse("sort_by(.age)"),
            Ok(Filter::Function(FunctionCall::SortBy(Box::new(
                Filter::Field("age".to_string())
            ))))
        );
    }

    #[test]
    fn test_group_by_basic() {
        assert_eq!(
            parse("group_by(.type)"),
            Ok(Filter::Function(FunctionCall::GroupBy(Box::new(
                Filter::Field("type".to_string())
            ))))
        );
    }

    #[test]
    fn test_unique_by_basic() {
        assert_eq!(
            parse("unique_by(.id)"),
            Ok(Filter::Function(FunctionCall::UniqueBy(Box::new(
                Filter::Field("id".to_string())
            ))))
        );
    }

    #[test]
    fn test_min_by_basic() {
        assert_eq!(
            parse("min_by(.score)"),
            Ok(Filter::Function(FunctionCall::MinBy(Box::new(
                Filter::Field("score".to_string())
            ))))
        );
    }

    #[test]
    fn test_max_by_basic() {
        assert_eq!(
            parse("max_by(.score)"),
            Ok(Filter::Function(FunctionCall::MaxBy(Box::new(
                Filter::Field("score".to_string())
            ))))
        );
    }

    #[test]
    fn test_sort_by_in_pipe() {
        assert_eq!(
            parse(".items | sort_by(.name)"),
            Ok(Filter::Pipe(
                Box::new(Filter::Field("items".to_string())),
                Box::new(Filter::Function(FunctionCall::SortBy(Box::new(
                    Filter::Field("name".to_string())
                ))))
            ))
        );
    }

    // if-then-else tests
    #[test]
    fn test_if_then_else_basic() {
        assert_eq!(
            parse("if .x then .a else .b end"),
            Ok(Filter::IfThenElse {
                condition: Box::new(Filter::Field("x".to_string())),
                then_branch: Box::new(Filter::Field("a".to_string())),
                else_branch: Box::new(Filter::Field("b".to_string())),
            })
        );
    }

    #[test]
    fn test_if_then_else_with_comparison() {
        use super::super::ast::CompareOp;
        assert_eq!(
            parse("if . > 0 then \"pos\" else \"neg\" end"),
            Ok(Filter::IfThenElse {
                condition: Box::new(Filter::Compare(
                    Box::new(Filter::Identity),
                    CompareOp::Gt,
                    Box::new(Filter::Literal(simd_json::json!(0)))
                )),
                then_branch: Box::new(Filter::Literal(simd_json::json!("pos"))),
                else_branch: Box::new(Filter::Literal(simd_json::json!("neg"))),
            })
        );
    }

    #[test]
    fn test_if_then_else_with_whitespace() {
        assert!(parse("if . then . else . end").is_ok());
        assert!(parse("if  .  then  .  else  .  end").is_ok());
    }

    // Alternative operator tests
    #[test]
    fn test_alternative_basic() {
        assert_eq!(
            parse(".x // \"default\""),
            Ok(Filter::Alternative(
                Box::new(Filter::Field("x".to_string())),
                Box::new(Filter::Literal(simd_json::json!("default")))
            ))
        );
    }

    #[test]
    fn test_alternative_chain() {
        assert_eq!(
            parse(".a // .b // .c"),
            Ok(Filter::Alternative(
                Box::new(Filter::Alternative(
                    Box::new(Filter::Field("a".to_string())),
                    Box::new(Filter::Field("b".to_string()))
                )),
                Box::new(Filter::Field("c".to_string()))
            ))
        );
    }

    #[test]
    fn test_alternative_in_pipe() {
        assert_eq!(
            parse(".x | . // 0"),
            Ok(Filter::Pipe(
                Box::new(Filter::Field("x".to_string())),
                Box::new(Filter::Alternative(
                    Box::new(Filter::Identity),
                    Box::new(Filter::Literal(simd_json::json!(0)))
                ))
            ))
        );
    }

    // has() tests
    #[test]
    fn test_has_basic() {
        assert_eq!(
            parse("has(\"name\")"),
            Ok(Filter::Function(FunctionCall::Has("name".to_string())))
        );
    }

    #[test]
    fn test_has_in_select() {
        assert_eq!(
            parse("select(has(\"x\"))"),
            Ok(Filter::Function(FunctionCall::Select(Box::new(
                Filter::Function(FunctionCall::Has("x".to_string()))
            ))))
        );
    }

    // Literal tests
    #[test]
    fn test_literal_number_int() {
        assert_eq!(parse("5"), Ok(Filter::Literal(simd_json::json!(5))));
    }

    #[test]
    fn test_literal_number_negative() {
        assert_eq!(parse("-42"), Ok(Filter::Literal(simd_json::json!(-42))));
    }

    #[test]
    fn test_literal_number_float() {
        assert_eq!(parse("3.15"), Ok(Filter::Literal(simd_json::json!(3.15))));
    }

    #[test]
    fn test_literal_string() {
        assert_eq!(
            parse("\"hello\""),
            Ok(Filter::Literal(simd_json::json!("hello")))
        );
    }

    #[test]
    fn test_literal_true() {
        assert_eq!(parse("true"), Ok(Filter::Literal(simd_json::json!(true))));
    }

    #[test]
    fn test_literal_false() {
        assert_eq!(parse("false"), Ok(Filter::Literal(simd_json::json!(false))));
    }

    #[test]
    fn test_literal_null() {
        assert_eq!(parse("null"), Ok(Filter::Literal(simd_json::json!(null))));
    }

    // Comparison tests
    #[test]
    fn test_compare_eq() {
        use super::super::ast::CompareOp;
        assert_eq!(
            parse(". == 5"),
            Ok(Filter::Compare(
                Box::new(Filter::Identity),
                CompareOp::Eq,
                Box::new(Filter::Literal(simd_json::json!(5)))
            ))
        );
    }

    #[test]
    fn test_compare_ne() {
        use super::super::ast::CompareOp;
        assert_eq!(
            parse(".x != \"foo\""),
            Ok(Filter::Compare(
                Box::new(Filter::Field("x".to_string())),
                CompareOp::Ne,
                Box::new(Filter::Literal(simd_json::json!("foo")))
            ))
        );
    }

    #[test]
    fn test_compare_lt() {
        use super::super::ast::CompareOp;
        assert_eq!(
            parse(".age < 18"),
            Ok(Filter::Compare(
                Box::new(Filter::Field("age".to_string())),
                CompareOp::Lt,
                Box::new(Filter::Literal(simd_json::json!(18)))
            ))
        );
    }

    #[test]
    fn test_compare_le() {
        use super::super::ast::CompareOp;
        assert_eq!(
            parse(". <= 100"),
            Ok(Filter::Compare(
                Box::new(Filter::Identity),
                CompareOp::Le,
                Box::new(Filter::Literal(simd_json::json!(100)))
            ))
        );
    }

    #[test]
    fn test_compare_gt() {
        use super::super::ast::CompareOp;
        assert_eq!(
            parse(". > 0"),
            Ok(Filter::Compare(
                Box::new(Filter::Identity),
                CompareOp::Gt,
                Box::new(Filter::Literal(simd_json::json!(0)))
            ))
        );
    }

    #[test]
    fn test_compare_ge() {
        use super::super::ast::CompareOp;
        assert_eq!(
            parse(". >= -5"),
            Ok(Filter::Compare(
                Box::new(Filter::Identity),
                CompareOp::Ge,
                Box::new(Filter::Literal(simd_json::json!(-5)))
            ))
        );
    }

    #[test]
    fn test_compare_in_pipe() {
        use super::super::ast::CompareOp;
        // .[] | . > 2
        assert_eq!(
            parse(".[] | . > 2"),
            Ok(Filter::Pipe(
                Box::new(Filter::Iterate),
                Box::new(Filter::Compare(
                    Box::new(Filter::Identity),
                    CompareOp::Gt,
                    Box::new(Filter::Literal(simd_json::json!(2)))
                ))
            ))
        );
    }

    #[test]
    fn test_compare_field_to_field() {
        use super::super::ast::CompareOp;
        assert_eq!(
            parse(".a == .b"),
            Ok(Filter::Compare(
                Box::new(Filter::Field("a".to_string())),
                CompareOp::Eq,
                Box::new(Filter::Field("b".to_string()))
            ))
        );
    }

    // Phase 5: String functions
    #[test]
    fn test_split() {
        assert_eq!(
            parse("split(\"/\")"),
            Ok(Filter::Function(FunctionCall::Split("/".to_string())))
        );
    }

    #[test]
    fn test_split_empty_delim() {
        assert_eq!(
            parse("split(\"\")"),
            Ok(Filter::Function(FunctionCall::Split("".to_string())))
        );
    }

    #[test]
    fn test_join() {
        assert_eq!(
            parse("join(\"-\")"),
            Ok(Filter::Function(FunctionCall::Join("-".to_string())))
        );
    }

    #[test]
    fn test_startswith() {
        assert_eq!(
            parse("startswith(\"hello\")"),
            Ok(Filter::Function(FunctionCall::StartsWith(
                "hello".to_string()
            )))
        );
    }

    #[test]
    fn test_endswith() {
        assert_eq!(
            parse("endswith(\".json\")"),
            Ok(Filter::Function(FunctionCall::EndsWith(
                ".json".to_string()
            )))
        );
    }

    #[test]
    fn test_contains_string() {
        assert_eq!(
            parse("contains(\"foo\")"),
            Ok(Filter::Function(FunctionCall::Contains(Box::new(
                Filter::Literal(simd_json::json!("foo"))
            ))))
        );
    }

    #[test]
    fn test_contains_array() {
        assert_eq!(
            parse("contains([1, 2])"),
            Ok(Filter::Function(FunctionCall::Contains(Box::new(
                Filter::Array(vec![
                    Filter::Literal(simd_json::json!(1)),
                    Filter::Literal(simd_json::json!(2)),
                ])
            ))))
        );
    }

    #[test]
    fn test_contains_object() {
        assert_eq!(
            parse("contains({\"a\": 1})"),
            Ok(Filter::Function(FunctionCall::Contains(Box::new(
                Filter::Object(vec![(
                    ObjectKey::Static("a".to_string()),
                    Filter::Literal(simd_json::json!(1))
                )])
            ))))
        );
    }

    #[test]
    fn test_string_func_in_pipe() {
        assert_eq!(
            parse(". | split(\"/\")"),
            Ok(Filter::Pipe(
                Box::new(Filter::Identity),
                Box::new(Filter::Function(FunctionCall::Split("/".to_string())))
            ))
        );
    }

    // Phase 6: Object manipulation
    #[test]
    fn test_to_entries() {
        assert_eq!(parse("to_entries"), Ok(Filter::Builtin(Builtin::ToEntries)));
    }

    #[test]
    fn test_from_entries() {
        assert_eq!(
            parse("from_entries"),
            Ok(Filter::Builtin(Builtin::FromEntries))
        );
    }

    #[test]
    fn test_with_entries() {
        assert_eq!(
            parse("with_entries(.)"),
            Ok(Filter::Function(FunctionCall::WithEntries(Box::new(
                Filter::Identity
            ))))
        );
    }

    #[test]
    fn test_with_entries_select() {
        use super::super::ast::CompareOp;
        assert_eq!(
            parse("with_entries(select(.value > 1))"),
            Ok(Filter::Function(FunctionCall::WithEntries(Box::new(
                Filter::Function(FunctionCall::Select(Box::new(Filter::Compare(
                    Box::new(Filter::Field("value".to_string())),
                    CompareOp::Gt,
                    Box::new(Filter::Literal(simd_json::json!(1)))
                ))))
            ))))
        );
    }

    #[test]
    fn test_to_from_entries_pipe() {
        assert_eq!(
            parse("to_entries | from_entries"),
            Ok(Filter::Pipe(
                Box::new(Filter::Builtin(Builtin::ToEntries)),
                Box::new(Filter::Builtin(Builtin::FromEntries))
            ))
        );
    }
}
