use crate::Program;

use super::node::{AccessTarget, Args, Expr, Literal, Node, Params, Pattern, StringSegment};
use std::fmt::Write;

impl Node {
    /// Converts the AST node back to mq code code.
    pub fn to_code(&self) -> String {
        let mut output = String::new();
        self.format_to_code(&mut output, 0);
        output
    }

    fn format_to_code(&self, buf: &mut String, indent: usize) {
        match &*self.expr {
            Expr::Literal(lit) => {
                format_literal(lit, buf);
            }
            Expr::Ident(ident) => {
                write!(buf, "{}", ident).unwrap();
            }
            Expr::Self_ => {
                buf.push_str("self");
            }
            Expr::Nodes => {
                buf.push_str("nodes");
            }
            Expr::Selector(selector) => {
                write!(buf, "{}", selector).unwrap();
            }
            Expr::SelectorChain(selectors) => {
                for (i, sel) in selectors.iter().enumerate() {
                    if i > 0 {
                        buf.push_str(" | ");
                    }
                    write!(buf, "{}", sel).unwrap();
                }
            }
            Expr::SelectorCall(selector, args) => {
                write!(buf, "{}", selector).unwrap();
                buf.push('(');
                format_args(args, buf, indent);
                buf.push(')');
            }
            Expr::Break(None) => {
                buf.push_str("break");
            }
            Expr::Break(Some(value)) => {
                buf.push_str("break: ");
                value.format_to_code(buf, indent);
            }
            Expr::Continue => {
                buf.push_str("continue");
            }
            Expr::Paren(node) => {
                buf.push('(');
                node.format_to_code(buf, indent);
                buf.push(')');
            }
            Expr::And(operands) => {
                for (i, op) in operands.iter().enumerate() {
                    if i > 0 {
                        buf.push_str(" && ");
                    }
                    op.format_to_code(buf, indent);
                }
            }
            Expr::Or(operands) => {
                for (i, op) in operands.iter().enumerate() {
                    if i > 0 {
                        buf.push_str(" || ");
                    }
                    op.format_to_code(buf, indent);
                }
            }
            Expr::Call(func, args) => {
                write!(buf, "{}", func).unwrap();
                buf.push('(');
                format_args(args, buf, indent);
                buf.push(')');
            }
            Expr::CallDynamic(func, args) => {
                func.format_to_code(buf, indent);
                buf.push('(');
                format_args(args, buf, indent);
                buf.push(')');
            }
            Expr::As(ident, value) => {
                value.format_to_code(buf, indent);
                write!(buf, " as {}", ident).unwrap();
            }
            Expr::Let(pattern, value) => {
                buf.push_str("let ");
                format_pattern(pattern, buf);
                buf.push_str(" = ");
                value.format_to_code(buf, indent);
            }
            Expr::Var(pattern, value) => {
                buf.push_str("var ");
                format_pattern(pattern, buf);
                buf.push_str(" = ");
                value.format_to_code(buf, indent);
            }
            Expr::Assign(ident, value) => {
                write!(buf, "{} = ", ident).unwrap();
                value.format_to_code(buf, indent);
            }
            Expr::If(branches) => {
                for (i, (cond_opt, body)) in branches.iter().enumerate() {
                    if i == 0 {
                        buf.push_str("if ");
                        if let Some(cond) = cond_opt {
                            buf.push('(');
                            cond.format_to_code(buf, indent);
                            buf.push(')');
                        }
                        buf.push_str(": ");
                        body.format_to_code(buf, indent);
                    } else if let Some(cond) = cond_opt {
                        buf.push_str(" elif (");
                        cond.format_to_code(buf, indent);
                        buf.push_str("): ");
                        body.format_to_code(buf, indent);
                    } else {
                        buf.push_str(" else: ");
                        body.format_to_code(buf, indent);
                    }
                }
            }
            Expr::While(cond, program) => {
                buf.push_str("while (");
                cond.format_to_code(buf, indent);
                buf.push(')');
                if needs_block_syntax(program) {
                    format_program_block(program, buf, indent);
                } else if let Some(stmt) = program.first() {
                    buf.push_str(": ");
                    stmt.format_to_code(buf, indent);
                }
            }
            Expr::Loop(program) => {
                buf.push_str("loop");
                format_program_block(program, buf, indent);
            }
            Expr::Foreach(item, iter, program) => {
                write!(buf, "foreach({}, ", item).unwrap();
                iter.format_to_code(buf, indent);
                buf.push(')');
                if needs_block_syntax(program) {
                    format_program_block(program, buf, indent);
                } else if let Some(stmt) = program.first() {
                    buf.push_str(": ");
                    stmt.format_to_code(buf, indent);
                }
            }
            Expr::Block(program) => {
                // Remove leading space from format_program_block output
                let start_len = buf.len();
                format_program_block(program, buf, indent);
                // format_program_block adds " do", but Block doesn't need leading space
                if buf[start_len..].starts_with(' ') {
                    buf.replace_range(start_len..start_len + 1, "");
                }
            }
            Expr::Def(name, params, program) => {
                write!(buf, "def {}(", name).unwrap();
                format_params(params, buf, indent);
                buf.push(')');
                if needs_block_syntax(program) {
                    format_program_block(program, buf, indent);
                } else if let Some(stmt) = program.first() {
                    buf.push_str(": ");
                    stmt.format_to_code(buf, indent);
                }
            }
            Expr::Fn(params, program) => {
                buf.push_str("fn(");
                format_params(params, buf, indent);
                buf.push(')');
                if needs_block_syntax(program) {
                    format_program_block(program, buf, indent);
                } else if let Some(stmt) = program.first() {
                    buf.push_str(": ");
                    stmt.format_to_code(buf, indent);
                }
            }
            Expr::Match(value, arms) => {
                buf.push_str("match (");
                value.format_to_code(buf, indent);
                buf.push_str(") do");
                for arm in arms.iter() {
                    buf.push('\n');
                    buf.push_str(&"  ".repeat(indent + 1));
                    buf.push_str("| ");
                    format_pattern(&arm.pattern, buf);
                    if let Some(guard) = &arm.guard {
                        buf.push_str(" if ");
                        guard.format_to_code(buf, indent + 1);
                    }
                    buf.push_str(": ");
                    arm.body.format_to_code(buf, indent + 1);
                }
                buf.push('\n');
                buf.push_str(&"  ".repeat(indent));
                buf.push_str("end");
            }
            Expr::InterpolatedString(segments) => {
                buf.push_str("s\"");
                for segment in segments.iter() {
                    match segment {
                        StringSegment::Text(text) => {
                            buf.push_str(&escape_string(text));
                        }
                        StringSegment::Expr(expr) => {
                            buf.push_str("${");
                            expr.format_to_code(buf, indent);
                            buf.push('}');
                        }
                        StringSegment::Env(name) => {
                            write!(buf, "${{{}}}", name).unwrap();
                        }
                        StringSegment::Self_ => {
                            buf.push_str("${self}");
                        }
                    }
                }
                buf.push('"');
            }
            Expr::Macro(name, params, body) => {
                write!(buf, "macro {}(", name).unwrap();
                format_params(params, buf, indent);
                buf.push_str("): ");
                body.format_to_code(buf, indent);
            }
            Expr::Quote(node) => {
                buf.push_str("quote: ");
                node.format_to_code(buf, indent);
            }
            Expr::Unquote(node) => {
                buf.push_str("unquote(");
                node.format_to_code(buf, indent);
                buf.push(')');
            }
            Expr::Try(try_expr, catch_expr) => {
                buf.push_str("try ");
                try_expr.format_to_code(buf, indent);
                buf.push_str(" catch: ");
                catch_expr.format_to_code(buf, indent);
            }
            Expr::Module(name, program) => {
                write!(buf, "module {}", name).unwrap();
                format_program_block(program, buf, indent);
            }
            Expr::QualifiedAccess(path, target) => {
                for (i, part) in path.iter().enumerate() {
                    if i > 0 {
                        buf.push_str("::");
                    }
                    write!(buf, "{}", part).unwrap();
                }
                match target {
                    AccessTarget::Call(func, args) => {
                        write!(buf, "::{}", func).unwrap();
                        buf.push('(');
                        format_args(args, buf, indent);
                        buf.push(')');
                    }
                    AccessTarget::Ident(ident) => {
                        write!(buf, "::{}", ident).unwrap();
                    }
                }
            }
            Expr::Include(path) => {
                buf.push_str("include ");
                format_literal(path, buf);
            }
            Expr::Import(path) => {
                buf.push_str("import ");
                format_literal(path, buf);
            }
        }
    }
}

fn escape_string(s: &str) -> String {
    let mut result = String::with_capacity(s.len());
    for ch in s.chars() {
        match ch {
            '\\' => result.push_str("\\\\"),
            '"' => result.push_str("\\\""),
            '\n' => result.push_str("\\n"),
            '\t' => result.push_str("\\t"),
            '\r' => result.push_str("\\r"),
            _ => result.push(ch),
        }
    }
    result
}

fn format_literal(literal: &Literal, buf: &mut String) {
    match literal {
        Literal::String(s) => {
            buf.push('"');
            buf.push_str(&escape_string(s));
            buf.push('"');
        }
        Literal::Bytes(b) => {
            buf.push_str("b\"");
            for byte in b {
                if byte.is_ascii_graphic() && *byte != b'"' && *byte != b'\\' {
                    buf.push(*byte as char);
                } else {
                    write!(buf, "\\x{:02x}", byte).unwrap();
                }
            }
            buf.push('"');
        }
        Literal::Number(n) => {
            write!(buf, "{}", n).unwrap();
        }
        Literal::Symbol(ident) => {
            write!(buf, ":{}", ident).unwrap();
        }
        Literal::Bool(b) => {
            buf.push_str(if *b { "true" } else { "false" });
        }
        Literal::None => {
            buf.push_str("none");
        }
    }
}

fn format_args(args: &Args, buf: &mut String, indent: usize) {
    for (i, arg) in args.iter().enumerate() {
        if i > 0 {
            buf.push_str(", ");
        }
        arg.format_to_code(buf, indent);
    }
}

fn format_params(params: &Params, buf: &mut String, indent: usize) {
    for (i, param) in params.iter().enumerate() {
        if i > 0 {
            buf.push_str(", ");
        }

        write!(buf, "{}", param.ident).unwrap();
        if let Some(default) = &param.default {
            buf.push_str(" = ");
            default.format_to_code(buf, indent);
        }
    }
}

fn format_program_block(program: &Program, buf: &mut String, indent: usize) {
    buf.push_str(" do\n");
    buf.push_str(&"  ".repeat(indent + 1));
    for (i, stmt) in program.iter().enumerate() {
        if i > 0 {
            buf.push_str(" | ");
        }
        stmt.format_to_code(buf, indent + 1);
    }
    buf.push('\n');
    buf.push_str(&"  ".repeat(indent));
    buf.push_str("end");
}

fn format_pattern(pattern: &Pattern, buf: &mut String) {
    match pattern {
        Pattern::Literal(lit) => {
            format_literal(lit, buf);
        }
        Pattern::Ident(ident) => {
            write!(buf, "{}", ident).unwrap();
        }
        Pattern::Wildcard => {
            buf.push('_');
        }
        Pattern::Array(patterns) => {
            buf.push('[');
            for (i, p) in patterns.iter().enumerate() {
                if i > 0 {
                    buf.push_str(", ");
                }
                format_pattern(p, buf);
            }
            buf.push(']');
        }
        Pattern::ArrayRest(patterns, rest) => {
            buf.push('[');
            for (i, p) in patterns.iter().enumerate() {
                if i > 0 {
                    buf.push_str(", ");
                }
                format_pattern(p, buf);
            }
            if !patterns.is_empty() {
                buf.push_str(", ");
            }
            write!(buf, "..{}", rest).unwrap();
            buf.push(']');
        }
        Pattern::Dict(entries) => {
            buf.push('{');
            for (i, (key, value)) in entries.iter().enumerate() {
                if i > 0 {
                    buf.push_str(", ");
                }
                write!(buf, "{}: ", key).unwrap();
                format_pattern(value, buf);
            }
            buf.push('}');
        }
        Pattern::Type(type_name) => {
            write!(buf, ":{}", type_name).unwrap();
        }
        Pattern::Or(patterns) => {
            for (i, p) in patterns.iter().enumerate() {
                if i > 0 {
                    buf.push_str(" || ");
                }
                format_pattern(p, buf);
            }
        }
    }
}

fn needs_block_syntax(program: &Program) -> bool {
    program.len() > 1
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{
        Ident, IdentWithToken, Shared,
        arena::ArenaId,
        ast::node::{MatchArm, Param, Pattern},
        number::Number,
    };
    use rstest::rstest;
    use smallvec::smallvec;

    // Helper function to create a Node from an Expr
    fn create_node(expr: Expr) -> Node {
        Node {
            token_id: ArenaId::new(0),
            expr: Shared::new(expr),
        }
    }

    #[rstest]
    #[case::string(Literal::String("hello".to_string()), r#""hello""#)]
    #[case::string_with_quote(Literal::String(r#"hello"world"#.to_string()), r#""hello\"world""#)]
    #[case::string_with_backslash(Literal::String(r"hello\world".to_string()), r#""hello\\world""#)]
    #[case::string_with_newline(Literal::String("hello\nworld".to_string()), r#""hello\nworld""#)]
    #[case::string_with_tab(Literal::String("hello\tworld".to_string()), r#""hello\tworld""#)]
    #[case::number_int(Literal::Number(Number::new(42.0)), "42")]
    #[case::number_float(Literal::Number(Number::new(42.5)), "42.5")]
    #[case::symbol(Literal::Symbol(Ident::new("test")), ":test")]
    #[case::bool_true(Literal::Bool(true), "true")]
    #[case::bool_false(Literal::Bool(false), "false")]
    #[case::none(Literal::None, "none")]
    fn test_to_code_literals(#[case] literal: Literal, #[case] expected: &str) {
        let node = create_node(Expr::Literal(literal));
        assert_eq!(node.to_code(), expected);
    }

    #[rstest]
    #[case::ident(Expr::Ident(IdentWithToken::new("foo")), "foo")]
    #[case::self_(Expr::Self_, "self")]
    #[case::nodes(Expr::Nodes, "nodes")]
    #[case::break_(Expr::Break(None), "break")]
    #[case::continue_(Expr::Continue, "continue")]
    fn test_to_code_simple_expressions(#[case] expr: Expr, #[case] expected: &str) {
        let node = create_node(expr);
        assert_eq!(node.to_code(), expected);
    }

    #[rstest]
    #[case::simple(Expr::Literal(Literal::Number(Number::new(42.0))), "(42)")]
    #[case::ident(Expr::Ident(IdentWithToken::new("x")), "(x)")]
    fn test_to_code_paren(#[case] inner_expr: Expr, #[case] expected: &str) {
        let inner_node = Shared::new(create_node(inner_expr));
        let node = create_node(Expr::Paren(inner_node));
        assert_eq!(node.to_code(), expected);
    }

    #[rstest]
    #[case::and(
        Expr::And(vec![
            Shared::new(create_node(Expr::Ident(IdentWithToken::new("x")))),
            Shared::new(create_node(Expr::Ident(IdentWithToken::new("y")))),
        ]),
        "x && y"
    )]
    #[case::or(
        Expr::Or(vec![
            Shared::new(create_node(Expr::Ident(IdentWithToken::new("a")))),
            Shared::new(create_node(Expr::Ident(IdentWithToken::new("b")))),
        ]),
        "a || b"
    )]
    #[case::and_three_operands(
        Expr::And(vec![
            Shared::new(create_node(Expr::Ident(IdentWithToken::new("x")))),
            Shared::new(create_node(Expr::Ident(IdentWithToken::new("y")))),
            Shared::new(create_node(Expr::Ident(IdentWithToken::new("z")))),
        ]),
        "x && y && z"
    )]
    #[case::or_three_operands(
        Expr::Or(vec![
            Shared::new(create_node(Expr::Ident(IdentWithToken::new("a")))),
            Shared::new(create_node(Expr::Ident(IdentWithToken::new("b")))),
            Shared::new(create_node(Expr::Ident(IdentWithToken::new("c")))),
        ]),
        "a || b || c"
    )]
    #[case::or_with_nested_and(
        // Or([And([a, b]), c])
        Expr::Or(vec![
            Shared::new(create_node(Expr::And(vec![
                Shared::new(create_node(Expr::Ident(IdentWithToken::new("a")))),
                Shared::new(create_node(Expr::Ident(IdentWithToken::new("b")))),
            ]))),
            Shared::new(create_node(Expr::Ident(IdentWithToken::new("c")))),
        ]),
        "a && b || c"
    )]
    #[case::or_with_and_in_middle(
        // Or([a, And([b, c]), d])
        Expr::Or(vec![
            Shared::new(create_node(Expr::Ident(IdentWithToken::new("a")))),
            Shared::new(create_node(Expr::And(vec![
                Shared::new(create_node(Expr::Ident(IdentWithToken::new("b")))),
                Shared::new(create_node(Expr::Ident(IdentWithToken::new("c")))),
            ]))),
            Shared::new(create_node(Expr::Ident(IdentWithToken::new("d")))),
        ]),
        "a || b && c || d"
    )]
    fn test_to_code_operators(#[case] expr: Expr, #[case] expected: &str) {
        let node = create_node(expr);
        assert_eq!(node.to_code(), expected);
    }

    #[rstest]
    #[case::no_args(
        Expr::Call(IdentWithToken::new("test"), smallvec![]),
        "test()"
    )]
    #[case::one_arg(
        Expr::Call(
            IdentWithToken::new("add"),
            smallvec![Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(1.0)))))]
        ),
        "add(1)"
    )]
    #[case::two_args(
        Expr::Call(
            IdentWithToken::new("add"),
            smallvec![
                Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(1.0))))),
                Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(2.0)))))
            ]
        ),
        "add(1, 2)"
    )]
    fn test_to_code_call(#[case] expr: Expr, #[case] expected: &str) {
        let node = create_node(expr);
        assert_eq!(node.to_code(), expected);
    }

    #[rstest]
    #[case::simple(
        Expr::CallDynamic(
            Shared::new(create_node(Expr::Ident(IdentWithToken::new("func")))),
            smallvec![Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(42.0)))))]
        ),
        "func(42)"
    )]
    fn test_to_code_call_dynamic(#[case] expr: Expr, #[case] expected: &str) {
        let node = create_node(expr);
        assert_eq!(node.to_code(), expected);
    }

    #[rstest]
    #[case::let_simple(
        Expr::Let(
            Pattern::Ident(IdentWithToken::new("x")),
            Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(42.0)))))
        ),
        "let x = 42"
    )]
    #[case::let_array_pattern(
        Expr::Let(
            Pattern::Array(vec![
                Pattern::Ident(IdentWithToken::new("a")),
                Pattern::Ident(IdentWithToken::new("b")),
            ]),
            Shared::new(create_node(Expr::Ident(IdentWithToken::new("arr"))))
        ),
        "let [a, b] = arr"
    )]
    #[case::let_dict_pattern(
        Expr::Let(
            Pattern::Dict(vec![
                (IdentWithToken::new("x"), Pattern::Ident(IdentWithToken::new("x"))),
                (IdentWithToken::new("y"), Pattern::Ident(IdentWithToken::new("y"))),
            ]),
            Shared::new(create_node(Expr::Ident(IdentWithToken::new("d"))))
        ),
        "let {x: x, y: y} = d"
    )]
    #[case::var_simple(
        Expr::Var(
            Pattern::Ident(IdentWithToken::new("y")),
            Shared::new(create_node(Expr::Literal(Literal::String("hello".to_string()))))
        ),
        r#"var y = "hello""#
    )]
    #[case::assign_simple(
        Expr::Assign(
            IdentWithToken::new("z"),
            Shared::new(create_node(Expr::Ident(IdentWithToken::new("value"))))
        ),
        "z = value"
    )]
    fn test_to_code_variables(#[case] expr: Expr, #[case] expected: &str) {
        let node = create_node(expr);
        assert_eq!(node.to_code(), expected);
    }

    #[rstest]
    #[case::if_simple(
        Expr::If(smallvec![
            (
                Some(Shared::new(create_node(Expr::Ident(IdentWithToken::new("x"))))),
                Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(1.0)))))
            )
        ]),
        "if (x): 1"
    )]
    #[case::if_else(
        Expr::If(smallvec![
            (
                Some(Shared::new(create_node(Expr::Ident(IdentWithToken::new("x"))))),
                Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(1.0)))))
            ),
            (
                None,
                Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(2.0)))))
            )
        ]),
        "if (x): 1 else: 2"
    )]
    #[case::if_elif_else(
        Expr::If(smallvec![
            (
                Some(Shared::new(create_node(Expr::Ident(IdentWithToken::new("x"))))),
                Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(1.0)))))
            ),
            (
                Some(Shared::new(create_node(Expr::Ident(IdentWithToken::new("y"))))),
                Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(2.0)))))
            ),
            (
                None,
                Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(3.0)))))
            )
        ]),
        "if (x): 1 elif (y): 2 else: 3"
    )]
    fn test_to_code_if(#[case] expr: Expr, #[case] expected: &str) {
        let node = create_node(expr);
        assert_eq!(node.to_code(), expected);
    }

    #[rstest]
    #[case::while_inline(
        Expr::While(
            Shared::new(create_node(Expr::Ident(IdentWithToken::new("x")))),
            vec![Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(1.0)))))]
        ),
        "while (x): 1"
    )]
    #[case::while_block(
        Expr::While(
            Shared::new(create_node(Expr::Ident(IdentWithToken::new("x")))),
            vec![
                Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(1.0))))),
                Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(2.0)))))
            ]
        ),
        "while (x) do\n  1 | 2\nend"
    )]
    fn test_to_code_while(#[case] expr: Expr, #[case] expected: &str) {
        let node = create_node(expr);
        assert_eq!(node.to_code(), expected);
    }

    #[rstest]
    #[case::loop_single(
        Expr::Loop(vec![Shared::new(create_node(Expr::Break(None)))]),
        "loop do\n  break\nend"
    )]
    fn test_to_code_loop(#[case] expr: Expr, #[case] expected: &str) {
        let node = create_node(expr);
        assert_eq!(node.to_code(), expected);
    }

    #[rstest]
    #[case::foreach_inline(
        Expr::Foreach(
            IdentWithToken::new("item"),
            Shared::new(create_node(Expr::Ident(IdentWithToken::new("items")))),
            vec![Shared::new(create_node(Expr::Ident(IdentWithToken::new("item"))))]
        ),
        "foreach(item, items): item"
    )]
    #[case::foreach_block(
        Expr::Foreach(
            IdentWithToken::new("x"),
            Shared::new(create_node(Expr::Ident(IdentWithToken::new("arr")))),
            vec![
                Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(1.0))))),
                Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(2.0)))))
            ]
        ),
        "foreach(x, arr) do\n  1 | 2\nend"
    )]
    fn test_to_code_foreach(#[case] expr: Expr, #[case] expected: &str) {
        let node = create_node(expr);
        assert_eq!(node.to_code(), expected);
    }

    #[rstest]
    #[case::single(
        Expr::Block(vec![Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(1.0)))))]),
        "do\n  1\nend"
    )]
    #[case::multiple(
        Expr::Block(vec![
            Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(1.0))))),
            Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(2.0))))),
            Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(3.0)))))
        ]),
        "do\n  1 | 2 | 3\nend"
    )]
    fn test_to_code_block(#[case] expr: Expr, #[case] expected: &str) {
        let node = create_node(expr);
        assert_eq!(node.to_code(), expected);
    }

    #[rstest]
    #[case::no_params_inline(
        Expr::Def(
            IdentWithToken::new("test"),
            smallvec![],
            vec![Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(42.0)))))]
        ),
        "def test(): 42"
    )]
    #[case::with_params_inline(
        Expr::Def(
            IdentWithToken::new("add"),
            smallvec![
                Param::new(IdentWithToken::new("x")),
                Param::new(IdentWithToken::new("y"))
            ],
            vec![Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(1.0)))))]
        ),
        "def add(x, y): 1"
    )]
    #[case::with_default_value(
        Expr::Def(
            IdentWithToken::new("greet"),
            smallvec![
                Param::new(IdentWithToken::new("name")),
                Param::with_default(
                    IdentWithToken::new("greeting"),
                    Some(Shared::new(create_node(Expr::Literal(Literal::String("Hello".to_string())))))
                )
            ],
            vec![Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(1.0)))))]
        ),
        r#"def greet(name, greeting = "Hello"): 1"#
    )]
    #[case::with_multiple_defaults(
        Expr::Def(
            IdentWithToken::new("foo"),
            smallvec![
                Param::new(IdentWithToken::new("a")),
                Param::with_default(
                    IdentWithToken::new("b"),
                    Some(Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(2.0))))))
                ),
                Param::with_default(
                    IdentWithToken::new("c"),
                    Some(Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(3.0))))))
                )
            ],
            vec![Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(1.0)))))]
        ),
        "def foo(a, b = 2, c = 3): 1"
    )]
    #[case::block(
        Expr::Def(
            IdentWithToken::new("test"),
            smallvec![Param::new(IdentWithToken::new("x"))],
            vec![
                Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(1.0))))),
                Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(2.0)))))
            ]
        ),
        "def test(x) do\n  1 | 2\nend"
    )]
    fn test_to_code_def(#[case] expr: Expr, #[case] expected: &str) {
        let node = create_node(expr);
        assert_eq!(node.to_code(), expected);
    }

    #[rstest]
    #[case::no_params_inline(
        Expr::Fn(
            smallvec![],
            vec![Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(42.0)))))]
        ),
        "fn(): 42"
    )]
    #[case::with_params_inline(
        Expr::Fn(
            smallvec![
                Param::new(IdentWithToken::new("x")),
                Param::new(IdentWithToken::new("y"))
            ],
            vec![Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(1.0)))))]
        ),
        "fn(x, y): 1"
    )]
    #[case::with_default_value(
        Expr::Fn(
            smallvec![
                Param::new(IdentWithToken::new("x")),
                Param::with_default(
                    IdentWithToken::new("y"),
                    Some(Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(10.0))))))
                )
            ],
            vec![Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(1.0)))))]
        ),
        "fn(x, y = 10): 1"
    )]
    fn test_to_code_fn(#[case] expr: Expr, #[case] expected: &str) {
        let node = create_node(expr);
        assert_eq!(node.to_code(), expected);
    }

    #[rstest]
    #[case::simple(
        Expr::Match(
            Shared::new(create_node(Expr::Ident(IdentWithToken::new("x")))),
            smallvec![
                MatchArm {
                    pattern: Pattern::Literal(Literal::Number(Number::new(1.0))),
                    guard: None,
                    body: Shared::new(create_node(Expr::Literal(Literal::String("one".to_string()))))
                },
                MatchArm {
                    pattern: Pattern::Wildcard,
                    guard: None,
                    body: Shared::new(create_node(Expr::Literal(Literal::String("other".to_string()))))
                }
            ]
        ),
        "match (x) do\n  | 1: \"one\"\n  | _: \"other\"\nend"
    )]
    fn test_to_code_match(#[case] expr: Expr, #[case] expected: &str) {
        let node = create_node(expr);
        assert_eq!(node.to_code(), expected);
    }

    #[rstest]
    #[case::text_only(
        Expr::InterpolatedString(vec![StringSegment::Text("hello".to_string())]),
        r#"s"hello""#
    )]
    #[case::with_expr(
        Expr::InterpolatedString(vec![
            StringSegment::Text("Hello ".to_string()),
            StringSegment::Expr(Shared::new(create_node(Expr::Ident(IdentWithToken::new("name"))))),
            StringSegment::Text("!".to_string())
        ]),
        r#"s"Hello ${name}!""#
    )]
    fn test_to_code_interpolated_string(#[case] expr: Expr, #[case] expected: &str) {
        let node = create_node(expr);
        assert_eq!(node.to_code(), expected);
    }

    #[rstest]
    #[case::simple(
        Expr::Macro(
            IdentWithToken::new("double"),
            smallvec![Param::new(IdentWithToken::new("x"))],
            Shared::new(create_node(Expr::Ident(IdentWithToken::new("x"))))
        ),
        "macro double(x): x"
    )]
    #[case::with_default_value(
        Expr::Macro(
            IdentWithToken::new("greet_macro"),
            smallvec![
                Param::new(IdentWithToken::new("name")),
                Param::with_default(
                    IdentWithToken::new("prefix"),
                    Some(Shared::new(create_node(Expr::Literal(Literal::String("Hi".to_string())))))
                )
            ],
            Shared::new(create_node(Expr::Ident(IdentWithToken::new("name"))))
        ),
        r#"macro greet_macro(name, prefix = "Hi"): name"#
    )]
    fn test_to_code_macro(#[case] expr: Expr, #[case] expected: &str) {
        let node = create_node(expr);
        assert_eq!(node.to_code(), expected);
    }

    #[rstest]
    #[case::quote(
        Expr::Quote(Shared::new(create_node(Expr::Ident(IdentWithToken::new("x"))))),
        "quote: x"
    )]
    #[case::unquote(
        Expr::Unquote(Shared::new(create_node(Expr::Ident(IdentWithToken::new("x"))))),
        "unquote(x)"
    )]
    fn test_to_code_quote_unquote(#[case] expr: Expr, #[case] expected: &str) {
        let node = create_node(expr);
        assert_eq!(node.to_code(), expected);
    }

    #[rstest]
    #[case::simple(
        Expr::Try(
            Shared::new(create_node(Expr::Ident(IdentWithToken::new("risky")))),
            Shared::new(create_node(Expr::Literal(Literal::String("error".to_string()))))
        ),
        r#"try risky catch: "error""#
    )]
    fn test_to_code_try(#[case] expr: Expr, #[case] expected: &str) {
        let node = create_node(expr);
        assert_eq!(node.to_code(), expected);
    }

    #[rstest]
    #[case::simple(
        Expr::Module(
            IdentWithToken::new("math"),
            vec![Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(1.0)))))]
        ),
        "module math do\n  1\nend"
    )]
    fn test_to_code_module(#[case] expr: Expr, #[case] expected: &str) {
        let node = create_node(expr);
        assert_eq!(node.to_code(), expected);
    }

    #[rstest]
    #[case::ident(
        Expr::QualifiedAccess(
            vec![IdentWithToken::new("module"), IdentWithToken::new("submodule")],
            AccessTarget::Ident(IdentWithToken::new("func"))
        ),
        "module::submodule::func"
    )]
    #[case::call(
        Expr::QualifiedAccess(
            vec![IdentWithToken::new("module")],
            AccessTarget::Call(
                IdentWithToken::new("func"),
                smallvec![Shared::new(create_node(Expr::Literal(Literal::Number(Number::new(1.0)))))]
            )
        ),
        "module::func(1)"
    )]
    fn test_to_code_qualified_access(#[case] expr: Expr, #[case] expected: &str) {
        let node = create_node(expr);
        assert_eq!(node.to_code(), expected);
    }

    #[rstest]
    #[case::include(
        Expr::Include(Literal::String("file.mq".to_string())),
        r#"include "file.mq""#
    )]
    #[case::import(
        Expr::Import(Literal::String("module.mq".to_string())),
        r#"import "module.mq""#
    )]
    fn test_to_code_include_import(#[case] expr: Expr, #[case] expected: &str) {
        let node = create_node(expr);
        assert_eq!(node.to_code(), expected);
    }

    // Complex expression tests
    #[rstest]
    #[case::nested_call(
        Expr::Call(
            IdentWithToken::new("map"),
            smallvec![
                Shared::new(create_node(Expr::Call(
                    IdentWithToken::new("filter"),
                    smallvec![Shared::new(create_node(Expr::Ident(IdentWithToken::new("items"))))]
                )))
            ]
        ),
        "map(filter(items))"
    )]
    fn test_to_code_complex(#[case] expr: Expr, #[case] expected: &str) {
        let node = create_node(expr);
        assert_eq!(node.to_code(), expected);
    }
}
