use mq_lang::{CstNode, CstNodeKind};

#[allow(dead_code)]
#[cfg(target_os = "windows")]
const NEW_LINE: &str = "\r\n";
#[allow(dead_code)]
#[cfg(not(target_os = "windows"))]
const NEW_LINE: &str = "\n";

#[derive(Clone, Debug, Default)]
pub struct Formatter {
    config: FormatterConfig,
    output: String,
    indent_cache: Vec<String>,
}

#[derive(PartialEq, Eq, PartialOrd, Ord)]
enum SortPriority {
    Import = 0,
    Include = 1,
    Let = 2,
    Def = 3,
    Macro = 4,
    Other = 5,
}

#[derive(Clone, Debug)]
pub struct FormatterConfig {
    pub indent_width: usize,
    pub sort_imports: bool,
    pub sort_functions: bool,
    pub sort_fields: bool,
}

impl Default for FormatterConfig {
    fn default() -> Self {
        Self {
            indent_width: 2,
            sort_imports: false,
            sort_functions: false,
            sort_fields: false,
        }
    }
}

impl From<&CstNode> for SortPriority {
    fn from(node: &CstNode) -> Self {
        match node.kind {
            CstNodeKind::Import => SortPriority::Import,
            CstNodeKind::Include => SortPriority::Include,
            CstNodeKind::Let | CstNodeKind::Var => SortPriority::Let,
            CstNodeKind::Def | CstNodeKind::Fn => SortPriority::Def,
            CstNodeKind::Macro => SortPriority::Macro,
            _ => SortPriority::Other,
        }
    }
}

pub(crate) fn ident(node: &CstNode) -> Option<String> {
    match node {
        CstNode {
            kind:
                CstNodeKind::Import
                | CstNodeKind::Include
                | CstNodeKind::Def
                | CstNodeKind::Let
                | CstNodeKind::Var
                | CstNodeKind::Macro,
            token: Some(_),
            children,
            ..
        } if !children.is_empty() => Some(children[0].to_string()),
        _ => None,
    }
}

pub fn needs_pipe(node: &CstNode) -> bool {
    !matches!(node.kind, CstNodeKind::Def | CstNodeKind::Macro | CstNodeKind::Eof)
}

impl Formatter {
    pub fn new(config: Option<FormatterConfig>) -> Self {
        Self {
            config: config.unwrap_or_default(),
            output: String::new(),
            indent_cache: Vec::new(),
        }
    }

    pub fn format(&mut self, code: &str) -> Result<String, mq_lang::CstErrorReporter> {
        if code.is_empty() {
            return Ok(String::new());
        }

        let (mut nodes, errors) = mq_lang::parse_recovery(code);

        if errors.has_errors() {
            return Err(errors);
        }

        self.format_with_cst(&mut nodes)
    }

    pub fn format_with_cst(
        &mut self,
        nodes: &mut Vec<mq_lang::Shared<mq_lang::CstNode>>,
    ) -> Result<String, mq_lang::CstErrorReporter> {
        if self.config.sort_imports || self.config.sort_functions || self.config.sort_fields {
            let sorted_nodes = self.sort_nodes(nodes);
            for node in sorted_nodes {
                self.format_node(node, 0);
            }
        } else {
            for node in nodes {
                self.format_node(mq_lang::Shared::clone(node), 0);
            }
        }

        if !self.output.contains('\n') {
            return Ok(self.output.trim_end().to_string());
        }

        let mut result = String::with_capacity(self.output.len());
        for line in self.output.lines() {
            result.push_str(line.trim_end());
            result.push('\n');
        }

        self.output.clear();
        Ok(result)
    }

    #[inline(always)]
    fn should_insert_pipe_before(
        node: &mq_lang::Shared<mq_lang::CstNode>,
        index: usize,
        prev_node: &Option<mq_lang::Shared<mq_lang::CstNode>>,
    ) -> bool {
        (needs_pipe(node) && index != 0) || (!node.is_eof() && prev_node.as_ref().is_some_and(|p| needs_pipe(p)))
    }

    fn sort_nodes(
        &mut self,
        nodes: &mut Vec<mq_lang::Shared<mq_lang::CstNode>>,
    ) -> Vec<mq_lang::Shared<mq_lang::CstNode>> {
        nodes.retain(|node| !node.is_pipe());
        nodes.sort_by(|a, b| {
            let priority_a: SortPriority = (&**a).into();
            let priority_b: SortPriority = (&**b).into();

            if priority_a != priority_b {
                return priority_a.cmp(&priority_b);
            }

            match priority_a {
                SortPriority::Import if self.config.sort_imports => {
                    let ident_a = ident(a);
                    let ident_b = ident(b);
                    ident_a.cmp(&ident_b)
                }
                SortPriority::Let if self.config.sort_fields => {
                    let ident_a = ident(a);
                    let ident_b = ident(b);
                    ident_a.cmp(&ident_b)
                }
                SortPriority::Def if self.config.sort_functions => {
                    let ident_a = ident(a);
                    let ident_b = ident(b);
                    ident_a.cmp(&ident_b)
                }
                _ => std::cmp::Ordering::Equal,
            }
        });

        let mut sorted_nodes = Vec::new();
        let mut prev_node: Option<mq_lang::Shared<mq_lang::CstNode>> = None;

        for (i, node) in nodes.iter_mut().enumerate() {
            if Self::should_insert_pipe_before(node, i, &prev_node) {
                let pipe_node = mq_lang::CstNode::new_pipe(i != 0);
                sorted_nodes.push(mq_lang::Shared::new(pipe_node));
            }

            let node = if i == 0 && node.has_new_line() {
                let mut node = (**node).clone();

                if node.has_new_line() {
                    while matches!(node.leading_trivia.first(), Some(mq_lang::CstTrivia::NewLine)) {
                        node.leading_trivia.remove(0);
                    }
                }

                &mq_lang::Shared::new(node)
            } else if !needs_pipe(node)
                && !node
                    .leading_trivia
                    .first()
                    .is_some_and(|t| matches!(t, mq_lang::CstTrivia::NewLine))
            {
                let mut node = (**node).clone();
                node.leading_trivia.insert(0, mq_lang::CstTrivia::NewLine);
                &mq_lang::Shared::new(node)
            } else {
                node
            };

            sorted_nodes.push(mq_lang::Shared::clone(node));
            prev_node = Some(mq_lang::Shared::clone(node));
        }

        sorted_nodes
    }

    fn format_node(&mut self, node: mq_lang::Shared<mq_lang::CstNode>, indent_level: usize) {
        let has_leading_new_line = node.has_new_line();
        let indent_level_consider_new_line = if has_leading_new_line { indent_level } else { 0 };

        if !matches!(
            node.kind,
            // For CallDynamic, all nodes are output again, so do not output a newline here.
            mq_lang::CstNodeKind::Token
                | mq_lang::CstNodeKind::BinaryOp(_)
                | mq_lang::CstNodeKind::End
                | mq_lang::CstNodeKind::Do
                | mq_lang::CstNodeKind::CallDynamic
        ) {
            self.append_leading_trivia(&node, indent_level_consider_new_line);
        }

        match &node.kind {
            mq_lang::CstNodeKind::Array => {
                self.format_array(&node, indent_level_consider_new_line);
            }
            mq_lang::CstNodeKind::Dict => {
                self.format_dict(&node, indent_level_consider_new_line);
            }
            mq_lang::CstNodeKind::BinaryOp(_) | mq_lang::CstNodeKind::Assign => {
                self.format_binary_op(&node, indent_level);
            }
            mq_lang::CstNodeKind::UnaryOp(_) => {
                self.format_unary_op(&node, indent_level);
            }
            mq_lang::CstNodeKind::Group => {
                self.format_group(&node, indent_level_consider_new_line);
            }
            mq_lang::CstNodeKind::Block => {
                self.format_block(&node, indent_level_consider_new_line, indent_level);
            }
            mq_lang::CstNodeKind::Call | mq_lang::CstNodeKind::MacroCall => {
                self.format_call(&node, indent_level_consider_new_line)
            }
            mq_lang::CstNodeKind::CallDynamic => self.format_call_dynamic(&node, indent_level_consider_new_line),
            mq_lang::CstNodeKind::Quote => self.format_quote(&node, indent_level_consider_new_line),
            mq_lang::CstNodeKind::Unquote => self.format_call(&node, indent_level_consider_new_line),
            mq_lang::CstNodeKind::Def
            | mq_lang::CstNodeKind::Foreach
            | mq_lang::CstNodeKind::While
            | mq_lang::CstNodeKind::Loop
            | mq_lang::CstNodeKind::Fn => self.format_expr(
                &node,
                indent_level_consider_new_line,
                indent_level,
                !matches!(node.kind, mq_lang::CstNodeKind::Fn | mq_lang::CstNodeKind::Loop),
            ),
            mq_lang::CstNodeKind::Macro => self.format_macro(&node, indent_level_consider_new_line, indent_level),
            mq_lang::CstNodeKind::Eof => {}
            mq_lang::CstNodeKind::Elif => self.format_elif(&node, indent_level_consider_new_line),
            mq_lang::CstNodeKind::Else => self.format_else(&node, indent_level_consider_new_line),
            mq_lang::CstNodeKind::Ident => self.format_ident(&node, indent_level_consider_new_line),
            mq_lang::CstNodeKind::If => self.format_if(&node, indent_level_consider_new_line, indent_level),
            mq_lang::CstNodeKind::Include => self.format_include(&node, indent_level_consider_new_line),
            mq_lang::CstNodeKind::Import => self.format_import(&node, indent_level_consider_new_line),
            mq_lang::CstNodeKind::Module => self.format_module(&node, indent_level_consider_new_line),
            mq_lang::CstNodeKind::QualifiedAccess => {
                self.format_qualified_access(&node, indent_level_consider_new_line)
            }
            mq_lang::CstNodeKind::InterpolatedString => {
                self.append_interpolated_string(&node, indent_level_consider_new_line);
            }
            mq_lang::CstNodeKind::Let | mq_lang::CstNodeKind::Var => {
                self.format_var_decl(&node, indent_level_consider_new_line, indent_level)
            }
            mq_lang::CstNodeKind::Literal => self.append_literal(&node, indent_level_consider_new_line),
            mq_lang::CstNodeKind::Env => self.append_env(&node, indent_level_consider_new_line),
            mq_lang::CstNodeKind::Nodes
            | mq_lang::CstNodeKind::End
            | mq_lang::CstNodeKind::Self_
            | mq_lang::CstNodeKind::Do
            | mq_lang::CstNodeKind::Continue => self.format_keyword(&node, indent_level_consider_new_line),
            mq_lang::CstNodeKind::Break => self.format_break(&node, indent_level_consider_new_line),
            mq_lang::CstNodeKind::Selector | mq_lang::CstNodeKind::SelectorCall | mq_lang::CstNodeKind::SelfAttr => {
                self.format_selector(&node, indent_level_consider_new_line)
            }
            mq_lang::CstNodeKind::Try => self.format_try(&node, indent_level_consider_new_line),
            mq_lang::CstNodeKind::Catch => self.format_catch(&node, indent_level_consider_new_line),
            mq_lang::CstNodeKind::Match => self.format_match(&node, indent_level_consider_new_line, indent_level),
            mq_lang::CstNodeKind::MatchArm => self.format_match_arm(&node, indent_level_consider_new_line),
            mq_lang::CstNodeKind::OrPattern => self.format_or_pattern(&node, indent_level_consider_new_line),
            mq_lang::CstNodeKind::Pattern => self.format_pattern(&node, indent_level_consider_new_line),
            mq_lang::CstNodeKind::Token => self.append_token(&node, indent_level_consider_new_line),
            mq_lang::CstNodeKind::DictEntry => self.format_dict_entry(&node, indent_level_consider_new_line),
        }
    }

    fn format_include(&mut self, node: &mq_lang::Shared<mq_lang::CstNode>, indent_level: usize) {
        self.append_indent(indent_level);
        self.output.push_str(&node.to_string());
        self.append_space();

        node.children.iter().for_each(|child| {
            self.format_node(mq_lang::Shared::clone(child), indent_level);
        });
    }

    fn format_import(&mut self, node: &mq_lang::Shared<mq_lang::CstNode>, indent_level: usize) {
        self.append_indent(indent_level);
        self.output.push_str(&node.to_string());
        self.append_space();

        for (i, child) in node.children.iter().enumerate() {
            if i > 0 {
                self.append_space();
            }
            self.format_node(mq_lang::Shared::clone(child), indent_level);
        }
    }

    fn format_module(&mut self, node: &mq_lang::Shared<mq_lang::CstNode>, indent_level: usize) {
        self.append_indent(indent_level);
        self.output.push_str(&node.to_string());
        self.append_space();

        node.children.iter().for_each(|child| {
            self.format_node(mq_lang::Shared::clone(child), indent_level + 1);
        });
    }

    fn format_qualified_access(&mut self, node: &mq_lang::Shared<mq_lang::CstNode>, indent_level: usize) {
        self.append_indent(indent_level);
        // Output module name
        self.output.push_str(&node.to_string());

        // Output children (::, identifier, optional args)
        node.children.iter().for_each(|child| {
            self.format_node(mq_lang::Shared::clone(child), 0);
        });
    }

    fn format_array(&mut self, node: &mq_lang::Shared<mq_lang::CstNode>, indent_level: usize) {
        self.append_indent(indent_level);
        let len = node.children.len();

        if len == 0 {
            return;
        }

        let indent_adjustment = if self.is_let_line() || self.is_last_line_pipe() {
            self.current_line_indent()
        } else if indent_level == 0 {
            // If indent_level is 0, it means the array is on the same line (no newline)
            // Use the current line indent to calculate the base indent for children
            self.current_line_indent()
        } else {
            0
        };

        let is_multiline = node.children[1].has_new_line();

        for child in &node.children[..len.saturating_sub(1)] {
            self.format_node(mq_lang::Shared::clone(child), indent_level + indent_adjustment + 1);
        }

        if let Some(last) = node.children.last() {
            if is_multiline {
                self.append_newline();
                self.append_indent(indent_level + indent_adjustment);
            }

            self.format_node(mq_lang::Shared::clone(last), indent_level + indent_adjustment);
        }
    }

    fn format_group(&mut self, node: &mq_lang::Shared<mq_lang::CstNode>, indent_level: usize) {
        self.append_indent(indent_level);
        self.output.push_str(&node.to_string());

        node.children.iter().for_each(|child| {
            self.format_node(mq_lang::Shared::clone(child), indent_level);
        });
    }

    fn format_dict_entry(&mut self, node: &mq_lang::Shared<mq_lang::CstNode>, indent_level: usize) {
        self.append_indent(indent_level);

        let key = node.children.first();
        let colon = node.children.get(1);
        let value = node.children.get(2);

        if let (Some(key), Some(colon), Some(value)) = (key, colon, value) {
            self.format_node(mq_lang::Shared::clone(key), indent_level);
            self.format_node(mq_lang::Shared::clone(colon), 0);
            self.append_space();
            self.format_node(mq_lang::Shared::clone(value), indent_level);
        }
    }

    fn format_dict(&mut self, node: &mq_lang::Shared<mq_lang::CstNode>, indent_level: usize) {
        self.append_indent(indent_level);
        let len = node.children.len();
        let indent_adjustment = if self.is_let_line() || self.is_last_line_pipe() {
            self.current_line_indent()
        } else if indent_level == 0 {
            // If indent_level is 0, it means the dict is on the same line (no newline)
            // Use the current line indent to calculate the base indent for children
            self.current_line_indent()
        } else {
            0
        };

        for child in &node.children[..len.saturating_sub(1)] {
            self.format_node(mq_lang::Shared::clone(child), indent_level + indent_adjustment + 1);
        }

        if let Some(last) = node.children.last() {
            if last.has_new_line() {
                self.append_newline();
                self.append_indent(indent_level + indent_adjustment);
            }

            self.format_node(mq_lang::Shared::clone(last), indent_level + indent_adjustment);
        }
    }

    fn format_binary_op(&mut self, node: &mq_lang::Shared<mq_lang::CstNode>, block_indent_level: usize) {
        match node.binary_op() {
            Some((left, right)) => {
                self.format_node(left, block_indent_level);

                match &**node {
                    mq_lang::CstNode {
                        kind: mq_lang::CstNodeKind::BinaryOp(mq_lang::CstBinaryOp::RangeOp),
                        token: Some(token),
                        ..
                    } => {
                        self.append_leading_trivia(node, block_indent_level);

                        if node.has_new_line() {
                            self.append_indent(block_indent_level);
                        }
                        self.output.push_str(&token.to_string());
                    }
                    mq_lang::CstNode {
                        kind: mq_lang::CstNodeKind::BinaryOp(_),
                        token: Some(token),
                        ..
                    }
                    | mq_lang::CstNode {
                        kind: mq_lang::CstNodeKind::Assign,
                        token: Some(token),
                        ..
                    } => {
                        self.append_leading_trivia(node, block_indent_level);

                        if node.has_new_line() {
                            self.append_indent(block_indent_level);
                        }

                        self.output.push(' ');
                        self.output.push_str(&token.to_string());
                        self.output.push(' ');
                    }
                    _ => unreachable!("Expected BinaryOp or Assign node"),
                }

                let indent_level = if right.has_new_line() {
                    block_indent_level + 1
                } else {
                    block_indent_level
                };

                self.format_node(right, indent_level);
            }
            _ => unreachable!("Expected BinaryOp node with two children"),
        }
    }

    fn format_unary_op(&mut self, node: &mq_lang::Shared<mq_lang::CstNode>, indent_level: usize) {
        if let mq_lang::CstNode {
            kind: mq_lang::CstNodeKind::UnaryOp(op),
            token: Some(token),
            ..
        } = &**node
        {
            if node.has_new_line() {
                self.append_indent(indent_level);
            }
            self.output.push_str(&token.to_string());

            match op {
                mq_lang::CstUnaryOp::Not | mq_lang::CstUnaryOp::Negate => {
                    self.format_node(mq_lang::Shared::clone(&node.children[0]), indent_level);
                }
            }
        } else {
            unreachable!("Expected UnaryOp node");
        }
    }

    fn format_expr(
        &mut self,
        node: &mq_lang::Shared<mq_lang::CstNode>,
        indent_level: usize,
        block_indent_level: usize,
        append_space_after_keyword: bool,
    ) {
        let is_prev_pipe = self.is_prev_pipe();
        let indent_adjustment = self.calculate_indent_adjustment();

        if node.has_new_line() {
            self.append_indent(indent_level);
        }
        self.output.push_str(&node.to_string());

        if append_space_after_keyword {
            self.append_space();
        }

        // Check for 'do' keyword or colon
        let do_index = Self::find_token_position(node, |kind| matches!(kind, mq_lang::TokenKind::Do));
        let colon_index = Self::find_token_position(node, |kind| matches!(kind, mq_lang::TokenKind::Colon));
        let uses_do_syntax = do_index.is_some();

        // If there's no colon or do, split before the right parenthesis
        let expr_index = if let Some(index) = do_index.or(colon_index) {
            index
        } else {
            Self::find_token_position(node, |kind| matches!(kind, mq_lang::TokenKind::RParen))
                .map(|index| index + 1)
                .unwrap_or(0)
        };

        node.children.iter().take(expr_index).for_each(|child| {
            self.format_node(
                mq_lang::Shared::clone(child),
                if child.has_new_line() {
                    block_indent_level + 1
                } else {
                    block_indent_level
                } + indent_adjustment,
            );
        });

        let mut expr_nodes = node.children.iter().skip(expr_index).peekable();

        // Format colon or do keyword if it exists
        if (colon_index.is_some() || do_index.is_some())
            && let Some(separator_node) = expr_nodes.next()
        {
            if uses_do_syntax {
                // Format 'do' keyword with standardized spacing
                self.format_do_with_spacing(separator_node, &mut expr_nodes);
            } else {
                // Format colon with spacing
                self.format_colon_with_spacing(separator_node, &mut expr_nodes, block_indent_level + 1);
            }
        }

        let block_indent_level = if is_prev_pipe {
            block_indent_level + 2
        } else {
            block_indent_level + 1
        } + indent_adjustment;

        expr_nodes.for_each(|child| {
            self.format_node(mq_lang::Shared::clone(child), block_indent_level);
        });
    }

    fn format_macro(
        &mut self,
        node: &mq_lang::Shared<mq_lang::CstNode>,
        indent_level: usize,
        block_indent_level: usize,
    ) {
        let is_prev_pipe = self.is_prev_pipe();
        let indent_adjustment = self.calculate_indent_adjustment();

        if node.has_new_line() {
            self.append_indent(indent_level);
        }
        self.output.push_str(&node.to_string());
        self.append_space();

        let colon_index = Self::find_token_position(node, |kind| matches!(kind, mq_lang::TokenKind::Colon));

        // If there's no colon, split before the right parenthesis
        let expr_index = self.calculate_split_position(node, 0);

        node.children.iter().take(expr_index).for_each(|child| {
            self.format_node(
                mq_lang::Shared::clone(child),
                if child.has_new_line() {
                    block_indent_level + 1
                } else {
                    block_indent_level
                } + indent_adjustment,
            );
        });

        let mut expr_nodes = node.children.iter().skip(expr_index).peekable();

        // Format colon if it exists
        if colon_index.is_some()
            && let Some(colon_node) = expr_nodes.next()
        {
            self.format_colon_with_spacing(colon_node, &mut expr_nodes, block_indent_level + 1);
        }

        let block_indent_level = if is_prev_pipe {
            block_indent_level + 2
        } else {
            block_indent_level + 1
        } + indent_adjustment;

        expr_nodes.for_each(|child| {
            let child_indent = if matches!(child.kind, mq_lang::CstNodeKind::Block) && colon_index.is_none() {
                indent_level
            } else {
                block_indent_level
            };
            self.format_node(mq_lang::Shared::clone(child), child_indent);
        });
    }

    fn format_block(
        &mut self,
        node: &mq_lang::Shared<mq_lang::CstNode>,
        indent_level: usize,
        block_indent_level: usize,
    ) {
        let is_prev_pipe = self.is_prev_pipe();
        let indent_adjustment = if self.is_let_line() {
            self.current_line_indent()
        } else {
            0
        };

        if node.has_new_line() {
            self.append_indent(indent_level);
        } else if !self.output.is_empty() && !self.output.ends_with(' ') {
            self.append_space();
        }

        self.output.push_str(&node.to_string());
        self.append_space();

        let expr_nodes = node.children.iter().peekable();

        let base_indent = if indent_level > 0 {
            indent_level
        } else {
            block_indent_level
        };

        let child_indent_level = if is_prev_pipe { base_indent + 2 } else { base_indent + 1 } + indent_adjustment;

        expr_nodes.for_each(|child| {
            self.format_node(mq_lang::Shared::clone(child), child_indent_level);
        });
    }

    fn format_var_decl(
        &mut self,
        node: &mq_lang::Shared<mq_lang::CstNode>,
        indent_level: usize,
        block_indent_level: usize,
    ) {
        self.append_indent(indent_level);
        self.output.push_str(&node.to_string());
        self.append_space();

        let indent_level = if self.is_last_line_pipe() {
            block_indent_level
        } else {
            indent_level
        };

        node.children.iter().for_each(|child| {
            let indent_level = if child.has_new_line() {
                indent_level + 1
            } else {
                indent_level
            };

            self.format_node(mq_lang::Shared::clone(child), indent_level);
        });
    }

    fn format_quote(&mut self, node: &mq_lang::Shared<mq_lang::CstNode>, indent_level: usize) {
        self.append_indent(indent_level);
        self.output.push_str(&node.to_string());

        let current_line_indent = if indent_level == 0 {
            self.current_line_indent()
        } else {
            indent_level
        };

        node.children.iter().for_each(|child| {
            self.format_node(
                mq_lang::Shared::clone(child),
                if child.has_new_line() {
                    current_line_indent + 1
                } else {
                    current_line_indent
                },
            );
        });
    }

    fn format_call(&mut self, node: &mq_lang::Shared<mq_lang::CstNode>, indent_level: usize) {
        self.append_indent(indent_level);
        self.output.push_str(&node.to_string());

        let current_line_indent = if indent_level == 0 {
            self.current_line_indent()
        } else {
            indent_level
        };

        node.children.iter().for_each(|child| {
            self.format_node(
                mq_lang::Shared::clone(child),
                if child.has_new_line() {
                    current_line_indent + 1
                } else {
                    current_line_indent
                },
            );
        });
    }

    fn format_call_dynamic(&mut self, node: &mq_lang::Shared<mq_lang::CstNode>, indent_level: usize) {
        let current_line_indent = if indent_level == 0 {
            self.current_line_indent()
        } else {
            indent_level
        };

        node.children.iter().for_each(|child| {
            self.format_node(
                mq_lang::Shared::clone(child),
                if child.has_new_line() {
                    current_line_indent + 1
                } else {
                    current_line_indent
                },
            );
        });
    }

    fn format_if(&mut self, node: &mq_lang::Shared<mq_lang::CstNode>, indent_level: usize, block_indent_level: usize) {
        let is_prev_pipe = self.is_prev_pipe();
        self.append_indent(indent_level);
        self.output.push_str(&node.to_string());
        self.append_space();

        let indent_level = if self.is_last_line_pipe() {
            block_indent_level
        } else {
            indent_level
        };

        let indent_adjustment = self.calculate_indent_adjustment();

        // Find the colon position
        let colon_index = Self::find_token_position(node, |kind| matches!(kind, mq_lang::TokenKind::Colon));

        // If there's no colon, split after the right parenthesis
        let expr_index = self.calculate_split_position(node, 3);

        // Format nodes before colon/rparen (l_param, cond, r_param)
        node.children.iter().take(expr_index).for_each(|child| {
            self.format_node(mq_lang::Shared::clone(child), 0);
        });

        let mut remaining = node.children.iter().skip(expr_index).peekable();

        // Format colon if it exists
        if colon_index.is_some()
            && let Some(colon_node) = remaining.next()
        {
            self.format_colon_with_spacing(colon_node, &mut remaining, 0);
        } else {
            // No colon: check if we need space before the next node
            if let Some(next) = remaining.peek()
                && !next.has_new_line()
                && !matches!(next.kind, mq_lang::CstNodeKind::Block)
            {
                self.append_space();
            }
        }

        let block_indent_level = if is_prev_pipe {
            indent_level + 2
        } else {
            indent_level + 1
        } + indent_adjustment;

        let node_indent_level = if is_prev_pipe { indent_level + 1 } else { indent_level } + indent_adjustment;

        // Format the then expression
        if let Some(then_expr) = remaining.next() {
            self.format_node(mq_lang::Shared::clone(then_expr), block_indent_level);
        }

        // Format remaining nodes (elif/else clauses)
        for child in remaining {
            self.format_node(mq_lang::Shared::clone(child), node_indent_level);
        }
    }

    fn format_elif(&mut self, node: &mq_lang::Shared<mq_lang::CstNode>, indent_level: usize) {
        if !node.has_new_line() {
            self.append_space();
        }

        self.append_indent(indent_level);
        self.output.push_str(&node.to_string());
        self.append_space();

        // Find the colon position
        let colon_index = Self::find_token_position(node, |kind| matches!(kind, mq_lang::TokenKind::Colon));

        // If there's no colon, split after the right parenthesis
        let expr_index = self.calculate_split_position(node, 3);

        // Format nodes before colon/rparen (l_param, cond, r_param)
        node.children.iter().take(expr_index).for_each(|child| {
            self.format_node(mq_lang::Shared::clone(child), 0);
        });

        let mut remaining = node.children.iter().skip(expr_index).peekable();

        // Format colon if it exists
        if colon_index.is_some()
            && let Some(colon_node) = remaining.next()
        {
            self.format_colon_with_spacing(colon_node, &mut remaining, 0);
        } else {
            // No colon: check if we need space before the next node
            if let Some(next) = remaining.peek()
                && !next.has_new_line()
                && !matches!(next.kind, mq_lang::CstNodeKind::Block)
            {
                self.append_space();
            }
        }

        // Format the then expression
        if let Some(then_expr) = remaining.next() {
            self.format_node(mq_lang::Shared::clone(then_expr), indent_level + 1);
        }
    }

    fn format_else(&mut self, node: &mq_lang::Shared<mq_lang::CstNode>, indent_level: usize) {
        if !node.has_new_line() {
            self.append_space();
        }

        self.append_indent(indent_level);
        self.output.push_str(&node.to_string());

        // Find the colon position
        let colon_index = Self::find_token_position(node, |kind| matches!(kind, mq_lang::TokenKind::Colon));

        let mut remaining = node.children.iter().peekable();

        // Format colon if it exists
        if colon_index.is_some()
            && let Some(colon_node) = remaining.next()
        {
            self.format_colon_with_spacing(colon_node, &mut remaining, 0);
        } else {
            // No colon: check if we need space before the next node
            if let Some(next) = remaining.peek()
                && !next.has_new_line()
                && !matches!(next.kind, mq_lang::CstNodeKind::Block)
            {
                self.append_space();
            }
        }

        // Format the then expression
        for then_expr in remaining {
            self.format_node(mq_lang::Shared::clone(then_expr), indent_level + 1);
        }
    }

    fn format_ident(&mut self, node: &mq_lang::Shared<mq_lang::CstNode>, indent_level: usize) {
        self.append_indent(indent_level);

        // Check if this is a variadic parameter (exactly 1 child with Asterisk token)
        let is_variadic = node.children.len() == 1
            && node.children[0]
                .token
                .as_ref()
                .is_some_and(|t| matches!(t.kind, mq_lang::TokenKind::Asterisk));

        if is_variadic {
            self.output.push('*');
            self.output.push_str(&node.to_string());
            return;
        }

        self.output.push_str(&node.to_string());

        // Handle parameter with default value
        // Structure: [ident, '=', default_value]
        if !node.children.is_empty() {
            for child in &node.children {
                // For '=' token, add space before and after
                if matches!(child.kind, mq_lang::CstNodeKind::Token)
                    && child
                        .token
                        .as_ref()
                        .is_some_and(|t| matches!(t.kind, mq_lang::TokenKind::Equal))
                {
                    self.append_space();
                    self.output.push_str(&child.to_string());
                    self.append_space();
                } else if matches!(child.kind, mq_lang::CstNodeKind::Ident) {
                    // Skip the first ident child (it's already been output as the parent token)
                    continue;
                } else if matches!(child.kind, mq_lang::CstNodeKind::Selector) {
                    // Format selector (attribute access like value.test) directly
                    // to avoid inserting a newline from leading trivia
                    self.format_selector(child, 0);
                } else {
                    // Format default value expression
                    self.format_node(mq_lang::Shared::clone(child), 0);
                }
            }
        }
    }

    fn format_try(&mut self, node: &mq_lang::Shared<mq_lang::CstNode>, indent_level: usize) {
        self.append_indent(indent_level);
        self.output.push_str(&node.to_string());

        if let Some(colon) = node.children.first() {
            self.output.push_str(&colon.to_string());
            self.append_space();
        }

        for child in node.children.iter().skip(1) {
            if matches!(child.kind, mq_lang::CstNodeKind::Catch) {
                self.format_node(mq_lang::Shared::clone(child), indent_level);
            } else {
                let child_indent = if child.has_new_line() {
                    indent_level + 1
                } else {
                    indent_level
                };
                self.format_node(mq_lang::Shared::clone(child), child_indent);
            }
        }
    }

    fn format_catch(&mut self, node: &mq_lang::Shared<mq_lang::CstNode>, indent_level: usize) {
        if !node.has_new_line() {
            self.append_space();
        }

        self.append_indent(indent_level);
        self.output.push_str(&node.to_string());

        if let Some(colon) = node.children.first() {
            self.output.push_str(&colon.to_string());
            self.append_space();
        }

        for child in node.children.iter().skip(1) {
            let child_indent = if child.has_new_line() {
                indent_level + 1
            } else {
                indent_level
            };
            self.format_node(mq_lang::Shared::clone(child), child_indent);
        }
    }

    fn format_match(
        &mut self,
        node: &mq_lang::Shared<mq_lang::CstNode>,
        indent_level: usize,
        block_indent_level: usize,
    ) {
        let is_prev_pipe = self.is_prev_pipe();
        self.append_indent(indent_level);
        self.output.push_str(&node.to_string());
        self.append_space();

        let indent_level = if self.is_last_line_pipe() {
            block_indent_level
        } else {
            indent_level
        };

        let indent_adjustment = self.calculate_indent_adjustment();

        // Check for 'do' keyword or colon
        let do_pos = Self::find_token_position(node, |kind| matches!(kind, mq_lang::TokenKind::Do));
        let colon_pos = Self::find_token_position(node, |kind| matches!(kind, mq_lang::TokenKind::Colon));
        let uses_do_syntax = do_pos.is_some();
        let separator_pos = do_pos.or(colon_pos).unwrap_or(0);

        // Format arguments (lparen, value, rparen)
        for child in node.children.iter().take(separator_pos) {
            self.format_node(mq_lang::Shared::clone(child), 0);
        }

        // Format colon or do keyword
        let mut remaining_children = node.children.iter().skip(separator_pos).peekable();

        if let Some(separator) = remaining_children.next() {
            if uses_do_syntax {
                // Format 'do' keyword with standardized spacing
                self.format_do_with_spacing(separator, &mut remaining_children);
            } else {
                // Format colon
                self.format_node(mq_lang::Shared::clone(separator), 0);
            }
        }

        // Calculate indent level for match arms (similar to format_if)
        let node_indent_level = if is_prev_pipe {
            indent_level + 2
        } else {
            indent_level + 1
        } + indent_adjustment;

        // Calculate indent level for end keyword
        let end_indent_level = if is_prev_pipe { indent_level + 1 } else { indent_level } + indent_adjustment;

        // Format match arms and end
        let remaining_children: Vec<_> = remaining_children.collect();

        // Check if this is a multiline match (first match arm has new line)
        let is_multiline = remaining_children
            .iter()
            .any(|child| matches!(child.kind, mq_lang::CstNodeKind::MatchArm) && child.has_new_line());

        for (i, child) in remaining_children.iter().enumerate() {
            // Check if this is the last child and it's an End node
            if i == remaining_children.len() - 1 && matches!(child.kind, mq_lang::CstNodeKind::End) {
                // Add newline before end for multiline match
                if is_multiline {
                    self.append_newline();
                    self.append_indent(end_indent_level);
                    self.output.push_str("end");
                    continue;
                }
            }
            self.format_node(mq_lang::Shared::clone(child), node_indent_level);
        }
    }

    /// Formats a match arm node, handling pipe, pattern, optional guard, colon, and body.
    fn format_match_arm(&mut self, node: &mq_lang::Shared<mq_lang::CstNode>, indent_level: usize) {
        let children = &node.children;
        let mut idx = 0;

        // 1. Pipe
        if let Some(pipe) = children.get(idx) {
            if node.has_new_line() {
                self.append_indent(indent_level);
                self.output.push('|');
                self.append_space();
            } else {
                self.format_node(mq_lang::Shared::clone(pipe), 0);
            }
            idx += 1;
        }

        // 2. Pattern
        if let Some(pattern) = children.get(idx) {
            self.format_node(mq_lang::Shared::clone(pattern), 0);
            idx += 1;
        }

        // 3. Optional guard: if <expr>
        if let Some(if_token) = children.get(idx)
            && let Some(token) = if_token.token.as_ref()
            && matches!(token.kind, mq_lang::TokenKind::If)
        {
            self.append_space();
            self.output.push_str("if ");
            idx += 1;

            // Guard expression: all nodes until colon
            while let Some(expr) = children.get(idx) {
                if let Some(t) = expr.token.as_ref()
                    && matches!(t.kind, mq_lang::TokenKind::Colon)
                {
                    break;
                }
                self.format_node(mq_lang::Shared::clone(expr), 0);
                idx += 1;
            }
        }

        // 4. Colon
        if let Some(colon) = children.get(idx)
            && let Some(token) = colon.token.as_ref()
            && matches!(token.kind, mq_lang::TokenKind::Colon)
        {
            self.output.push_str(&token.to_string());
            self.append_space();
            idx += 1;
        }

        // 5. Body
        if let Some(body) = children.get(idx) {
            self.format_node(mq_lang::Shared::clone(body), indent_level + 1);
        }
    }

    fn format_pattern(&mut self, node: &mq_lang::Shared<mq_lang::CstNode>, indent_level: usize) {
        if indent_level > 0 {
            self.append_indent(indent_level);
        }

        // If pattern has a token, it's a simple pattern (literal, ident, wildcard)
        if let Some(token) = &node.token {
            match &token.kind {
                mq_lang::TokenKind::StringLiteral(s) => {
                    let escaped = Self::escape_string(s);
                    self.output.push('"');
                    self.output.push_str(&escaped);
                    self.output.push('"');
                }
                mq_lang::TokenKind::BytesLiteral(_) => {
                    self.output.push_str(&token.to_string());
                }
                mq_lang::TokenKind::NumberLiteral(n) => self.output.push_str(&n.to_string()),
                mq_lang::TokenKind::BoolLiteral(b) => self.output.push_str(&b.to_string()),
                mq_lang::TokenKind::None => self.output.push_str(&token.to_string()),
                mq_lang::TokenKind::Ident(name) => self.output.push_str(name),
                _ => {}
            }
        }

        // Format children (for complex patterns like arrays, dicts, type patterns)
        if !node.children.is_empty() {
            // Check if this is a type pattern (starts with colon)
            if let Some(first) = node.children.first()
                && let Some(token) = &first.token
            {
                if matches!(token.kind, mq_lang::TokenKind::Colon) {
                    // Type pattern: :type_name
                    self.output.push_str(&token.to_string());
                    if let Some(second) = node.children.get(1)
                        && let Some(t) = &second.token
                        && let mq_lang::TokenKind::Ident(name) = &t.kind
                    {
                        self.output.push_str(name);
                    }
                    return;
                } else if matches!(token.kind, mq_lang::TokenKind::LBracket) {
                    self.format_array_pattern(node);
                    return;
                } else if matches!(token.kind, mq_lang::TokenKind::LBrace) {
                    self.format_dict_pattern(node);
                    return;
                }
            }

            for child in &node.children {
                self.format_node(mq_lang::Shared::clone(child), indent_level);
            }
        }
    }

    fn format_or_pattern(&mut self, node: &mq_lang::Shared<mq_lang::CstNode>, indent_level: usize) {
        for child in &node.children {
            match &child.kind {
                mq_lang::CstNodeKind::Pattern | mq_lang::CstNodeKind::OrPattern => {
                    self.format_pattern(&mq_lang::Shared::clone(child), indent_level);
                }
                mq_lang::CstNodeKind::Token => {
                    if let Some(token) = &child.token
                        && matches!(token.kind, mq_lang::TokenKind::Or)
                    {
                        self.output.push_str(" || ");
                    }
                }
                _ => {}
            }
        }
    }

    fn format_array_pattern(&mut self, node: &mq_lang::Shared<mq_lang::CstNode>) {
        for child in &node.children {
            match &child.kind {
                mq_lang::CstNodeKind::Token => {
                    if let Some(token) = &child.token {
                        match &token.kind {
                            mq_lang::TokenKind::Comma => self.output.push_str(", "),
                            _ => self.output.push_str(&token.to_string()),
                        }
                    }
                }
                _ => self.output.push_str(&child.to_string()),
            }

            child.children.iter().for_each(|gc| {
                self.format_node(mq_lang::Shared::clone(gc), 0);
            });
        }
    }

    fn format_dict_pattern(&mut self, node: &mq_lang::Shared<mq_lang::CstNode>) {
        let mut i = 0;
        let children = &node.children;

        while i < children.len() {
            let child = &children[i];

            if let Some(token) = &child.token {
                match &token.kind {
                    mq_lang::TokenKind::Comma => {
                        self.output.push_str(", ");
                        i += 1;
                        continue;
                    }
                    mq_lang::TokenKind::Ident(name) => {
                        self.output.push_str(name);

                        // Check for colon and pattern after the identifier
                        if let Some(next) = children.get(i + 1)
                            && let Some(next_token) = &next.token
                            && matches!(next_token.kind, mq_lang::TokenKind::Colon)
                        {
                            self.output.push_str(&next_token.to_string());
                            self.output.push(' ');
                            i += 2; // Skip colon

                            // Format the pattern after colon
                            if let Some(pattern_node) = children.get(i)
                                && let Some(pattern_token) = &pattern_node.token
                                && let mq_lang::TokenKind::Ident(pattern_name) = &pattern_token.kind
                            {
                                self.output.push_str(pattern_name);
                            }
                        }
                    }
                    _ => {
                        self.output.push_str(&token.to_string());
                        i += 1;
                        continue;
                    }
                }
            }

            child.children.iter().for_each(|gc| {
                self.format_node(mq_lang::Shared::clone(gc), 0);
            });

            i += 1;
        }
    }

    fn append_leading_trivia(&mut self, node: &mq_lang::Shared<mq_lang::CstNode>, indent_level: usize) {
        for trivia in &node.leading_trivia {
            match trivia {
                mq_lang::CstTrivia::Whitespace(_) => {}
                comment @ mq_lang::CstTrivia::Comment(_) => {
                    if self.is_prev_pipe() {
                        self.append_space();
                    } else if node.has_new_line() && self.output.ends_with('\n') {
                        self.append_indent(indent_level);
                    }

                    if !self.output.is_empty() && !self.output.ends_with('\n') && !self.output.ends_with(' ') {
                        self.append_space();
                    }

                    self.output.push_str(&comment.to_string());
                }
                mq_lang::CstTrivia::NewLine => {
                    self.output.push('\n');
                }
                _ => {}
            }
        }
    }

    fn append_indent(&mut self, level: usize) {
        // Ensure cache has enough entries
        while self.indent_cache.len() <= level {
            let next_level = self.indent_cache.len();
            let indent_str = " ".repeat(next_level * self.config.indent_width);
            self.indent_cache.push(indent_str);
        }

        self.output.push_str(&self.indent_cache[level]);
    }

    fn append_env(&mut self, node: &mq_lang::Shared<mq_lang::CstNode>, indent_level: usize) {
        if let mq_lang::CstNode {
            kind: mq_lang::CstNodeKind::Env,
            token: Some(token),
            ..
        } = &**node
        {
            self.append_indent(indent_level);
            self.output.push_str(&token.to_string());
        }
    }

    fn append_literal(&mut self, node: &mq_lang::Shared<mq_lang::CstNode>, indent_level: usize) {
        // Check if this is a symbol literal (has children: colon token + identifier/string)
        if !node.children.is_empty() {
            self.append_symbol(node, indent_level);
            return;
        }

        if let mq_lang::CstNode {
            kind: mq_lang::CstNodeKind::Literal,
            token: Some(token),
            ..
        } = &**node
        {
            self.append_indent(indent_level);
            match &token.kind {
                mq_lang::TokenKind::StringLiteral(s) => {
                    let escaped = Self::escape_string(s);
                    self.output.push('"');
                    self.output.push_str(&escaped);
                    self.output.push('"');
                }
                mq_lang::TokenKind::BytesLiteral(_) => {
                    self.output.push_str(&token.to_string());
                }
                mq_lang::TokenKind::NumberLiteral(n) => self.output.push_str(&n.to_string()),
                mq_lang::TokenKind::BoolLiteral(b) => self.output.push_str(&b.to_string()),
                mq_lang::TokenKind::None => self.output.push_str(&token.to_string()),
                other => {
                    eprintln!(
                        "Warning: Unexpected token kind in append_literal: {:?}. Inserting placeholder.",
                        other
                    );
                    self.output.push_str(&other.to_string());
                }
            }
        }
    }

    fn append_symbol(&mut self, node: &mq_lang::Shared<mq_lang::CstNode>, indent_level: usize) {
        self.append_indent(indent_level);
        // Format symbol as :ident or :"string"
        for child in &node.children {
            if let Some(token) = &child.token {
                match &token.kind {
                    mq_lang::TokenKind::Colon => self.output.push(':'),
                    mq_lang::TokenKind::Ident(s) => self.output.push_str(s),
                    mq_lang::TokenKind::StringLiteral(s) => {
                        let escaped = Self::escape_string(s);
                        self.output.push('"');
                        self.output.push_str(&escaped);
                        self.output.push('"');
                    }
                    _ => {}
                }
            }
        }
    }

    fn append_interpolated_string(&mut self, node: &mq_lang::Shared<mq_lang::CstNode>, indent_level: usize) {
        if let mq_lang::CstNode {
            kind: mq_lang::CstNodeKind::InterpolatedString,
            token: Some(token),
            ..
        } = &**node
        {
            self.append_indent(indent_level);
            self.output.push_str("s\"");
            let escaped = token
                .to_string()
                .replace("\\", "\\\\") // Must be first to avoid double-escaping
                .replace("\"", "\\\"")
                .replace("\t", "\\t")
                .replace("\r", "\\r");
            self.output.push_str(&escaped);
            self.output.push('"');
        }
    }

    fn format_selector(&mut self, node: &mq_lang::Shared<mq_lang::CstNode>, indent_level: usize) {
        if let mq_lang::CstNode {
            kind: mq_lang::CstNodeKind::Selector | mq_lang::CstNodeKind::SelfAttr | mq_lang::CstNodeKind::SelectorCall,
            token: Some(token),
            children,
            ..
        } = &**node
        {
            self.append_indent(indent_level);
            self.output.push_str(&token.to_string());

            children.iter().for_each(|child| {
                self.format_node(mq_lang::Shared::clone(child), indent_level);
            });
        }
    }

    fn format_keyword(&mut self, node: &mq_lang::Shared<mq_lang::CstNode>, indent_level: usize) {
        if let mq_lang::CstNode { token: Some(token), .. } = &**node {
            match token.kind {
                mq_lang::TokenKind::End => {
                    if node.has_new_line() {
                        let indent_level = indent_level.saturating_sub(1);
                        self.append_leading_trivia(node, indent_level);
                        self.append_indent(indent_level);
                        self.output.push_str(&token.to_string());
                    } else {
                        if !self.output.ends_with(' ') {
                            self.append_space();
                        }
                        self.output.push_str(&token.to_string());
                    }
                }
                mq_lang::TokenKind::Do => {
                    if node.has_new_line() {
                        self.append_leading_trivia(node, indent_level);
                        self.append_indent(indent_level);
                        self.output.push_str(&token.to_string());
                    } else {
                        if !self.output.ends_with(' ') {
                            self.append_space();
                        }
                        self.output.push_str(&token.to_string());
                    }
                }
                _ => {
                    self.append_indent(indent_level);
                    self.output.push_str(&token.to_string());
                }
            }
        }
    }

    fn format_break(&mut self, node: &mq_lang::Shared<mq_lang::CstNode>, indent_level: usize) {
        self.append_indent(indent_level);
        self.output.push_str(&node.to_string());

        // Format children (colon and expression if present)
        for child in &node.children {
            self.format_node(mq_lang::Shared::clone(child), 0);
        }
    }

    fn append_token(&mut self, node: &mq_lang::Shared<mq_lang::CstNode>, indent_level: usize) {
        if let mq_lang::CstNode {
            kind: mq_lang::CstNodeKind::Token,
            token: Some(token),
            ..
        } = &**node
        {
            match token.kind {
                mq_lang::TokenKind::Comma => {
                    if node.has_new_line() {
                        self.append_leading_trivia(node, indent_level);
                        self.append_indent(indent_level);
                        self.output.push_str(&token.to_string());
                    } else {
                        self.output.push_str(&token.to_string());
                        self.output.push(' ');
                    }
                }
                mq_lang::TokenKind::Colon => self.output.push_str(&token.to_string()),
                mq_lang::TokenKind::Equal => {
                    self.output.push(' ');
                    self.output.push_str(&token.to_string());
                    self.output.push(' ');
                }
                mq_lang::TokenKind::Pipe => {
                    if node.has_new_line() {
                        self.append_leading_trivia(node, indent_level);
                        self.append_indent(indent_level);
                        self.output.push_str(&token.to_string());
                        self.output.push(' ');
                    } else {
                        self.output.push(' ');
                        self.output.push_str(&token.to_string());
                        self.output.push(' ');
                    }
                }
                mq_lang::TokenKind::RParen => {
                    if node.has_new_line() {
                        let indent_level = indent_level.saturating_sub(1);
                        self.append_leading_trivia(node, indent_level);
                        self.append_indent(indent_level);
                        self.output.push_str(&token.to_string());
                    } else {
                        self.output.push_str(&token.to_string());
                    }
                }
                _ => self.output.push_str(&token.to_string()),
            }
        }
    }

    #[inline(always)]
    fn append_space(&mut self) {
        self.output.push(' ');
    }

    #[inline(always)]
    fn append_newline(&mut self) {
        self.output.push('\n');
    }

    #[inline(always)]
    fn is_prev_pipe(&self) -> bool {
        self.output.ends_with("| ")
    }

    #[inline(always)]
    pub fn current_line_indent(&self) -> usize {
        // Find the last newline position
        let start = self.output.rfind('\n').map_or(0, |pos| pos + 1);
        let last_line = &self.output[start..];
        last_line.chars().take_while(|c| *c == ' ').count() / self.config.indent_width
    }

    #[inline(always)]
    pub fn is_last_line_pipe(&self) -> bool {
        let output = self.output.trim_end_matches('\n');
        // Find the last newline position
        let start = output.rfind('\n').map_or(0, |pos| pos + 1);
        let last_line = &output[start..];
        last_line.trim_start().starts_with('|')
    }

    #[inline(always)]
    fn is_let_line(&self) -> bool {
        // Find the last newline position
        let start = self.output.rfind('\n').map_or(0, |pos| pos + 1);

        if start < self.output.len() {
            let last_line = &self.output[start..];
            (!last_line.starts_with("let ") && last_line.trim().starts_with("let "))
                || last_line.trim().replace(" ", "").starts_with("|let")
        } else {
            false
        }
    }

    fn find_token_position<F>(node: &mq_lang::Shared<mq_lang::CstNode>, token_kind_matcher: F) -> Option<usize>
    where
        F: Fn(&mq_lang::TokenKind) -> bool,
    {
        node.children.iter().position(|c| {
            c.token
                .as_ref()
                .map(|token| token_kind_matcher(&token.kind))
                .unwrap_or(false)
        })
    }

    #[inline(always)]
    fn calculate_indent_adjustment(&self) -> usize {
        if self.is_let_line() {
            self.current_line_indent()
        } else {
            0
        }
    }

    #[inline(always)]
    fn calculate_split_position(&self, node: &mq_lang::Shared<mq_lang::CstNode>, fallback: usize) -> usize {
        let colon_index = Self::find_token_position(node, |kind| matches!(kind, mq_lang::TokenKind::Colon));

        match colon_index {
            Some(index) => index,
            None => Self::find_token_position(node, |kind| matches!(kind, mq_lang::TokenKind::RParen))
                .map(|index| index + 1)
                .unwrap_or(fallback),
        }
    }

    fn format_colon_with_spacing<'a, I>(
        &mut self,
        colon_node: &mq_lang::Shared<mq_lang::CstNode>,
        remaining: &mut std::iter::Peekable<I>,
        indent_level: usize,
    ) where
        I: Iterator<Item = &'a mq_lang::Shared<mq_lang::CstNode>>,
    {
        self.format_node(mq_lang::Shared::clone(colon_node), indent_level);

        if let Some(next) = remaining.peek()
            && !next.has_new_line()
            && !matches!(next.kind, mq_lang::CstNodeKind::Block)
        {
            self.append_space();
        }
    }

    /// Formats a 'do' keyword with standardized spacing.
    /// Adds space before the 'do' keyword, formats it, and adds space after
    /// if the next node doesn't have a newline and is not a MatchArm.
    fn format_do_with_spacing<'a, I>(
        &mut self,
        do_node: &mq_lang::Shared<mq_lang::CstNode>,
        remaining: &mut std::iter::Peekable<I>,
    ) where
        I: Iterator<Item = &'a mq_lang::Shared<mq_lang::CstNode>>,
    {
        // Add space before 'do' keyword
        self.append_space();

        // Format the 'do' keyword
        self.format_node(mq_lang::Shared::clone(do_node), 0);

        // Add space after 'do' if next node doesn't have newline and is not a MatchArm
        if let Some(next) = remaining.peek()
            && !next.has_new_line()
            && !matches!(next.kind, mq_lang::CstNodeKind::MatchArm)
        {
            self.append_space();
        }
    }

    /// Escapes control characters in a string, preserving existing valid escape sequences
    fn escape_string(s: &str) -> String {
        let mut result = String::with_capacity(s.len() * 2);

        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"),
                c if c.is_control() => {
                    let code = c as u32;
                    if code <= 0xFF {
                        result.push_str(&format!("\\x{:02x}", code));
                    } else {
                        result.push_str(&format!("\\u{{{:04x}}}", code));
                    }
                }
                c => result.push(c),
            }
        }

        result
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use rstest::rstest;

    #[rstest]
    #[case::empty("", "")]
    #[case::if_(
        "if(test):
        test
        else:
        test2",
        "if (test):
  test
else:
  test2
"
    )]
    #[case::def_(
        "def name(test):
        test | test2;",
        "def name(test):
  test | test2;
"
    )]
    #[case::foreach_(
        "foreach(x,array(1, 2, 3)):
        add(x, 1);",
        "foreach (x, array(1, 2, 3)):
  add(x, 1);
"
    )]
    #[case::def_(
        "def test():
        test1
        |test2
        |test3;",
        "def test():
  test1
  | test2
  | test3;
"
    )]
    #[case("test()|test2()", "test() | test2()")]
    #[case(
        "if(test):
        test
        elif(test2):
        test2
        else:
        test3",
        "if (test):
  test
elif (test2):
  test2
else:
  test3
"
    )]
    #[case::if_(
        "if(test):
        test
        elif(test2):
        test2
        else:
        test3",
        "if (test):
  test
elif (test2):
  test2
else:
  test3
"
    )]
    #[case::if_(
        "if(test):
        test
        else:
        test2",
        "if (test):
  test
else:
  test2
"
    )]
    #[case::if_else(
        "if(test):
        test
        else: do
        test2
        end",
        "if (test):
  test
else: do
    test2
  end
"
    )]
    #[case::one_line("if(test): test else: test2", "if (test): test else: test2")]
    #[case::one_line(
        "if(test): test elif(test2): test2 else: test3",
        "if (test): test elif (test2): test2 else: test3"
    )]
    #[case::foreach_one_line("foreach(x,array(1,2,3)):add(x,1);", "foreach (x, array(1, 2, 3)): add(x, 1);")]
    #[case::foreach_one_line(
        "foreach(x,array(1,2,3)):add(x,1);|add(1,2);",
        "foreach (x, array(1, 2, 3)): add(x, 1); | add(1, 2);"
    )]
    #[case::foreach_one_line(".[]|upcase()", ".[] | upcase()")]
    #[case::while_multiline(
        "while(condition()):
        process();",
        "while (condition()):
  process();
"
    )]
    #[case::while_oneline("while(condition()): process();", "while (condition()): process();")]
    #[case::while_with_pipe(
        "while(check_condition()):
        data
        | process()
        | output();",
        "while (check_condition()):
  data
  | process()
  | output();
"
    )]
    #[case::loop_multiline(
        "loop:
        process();",
        "loop:
  process();
"
    )]
    #[case::loop_oneline("loop: process();", "loop: process();")]
    #[case::loop_with_pipe(
        "loop:
        data
        | process()
        | output();",
        "loop:
  data
  | process()
  | output();
"
    )]
    #[case::loop_with_break(
        "loop:
        add(self,1)
        |if(gt(self,5)):break;;",
        "loop:
  add(self, 1)
  | if (gt(self, 5)): break;;
"
    )]
    #[case::def(
        r##".h
| let link = to_link(add("#", to_text(self)), to_text(self), "");
| if (eq(to_md_name(), "h1")):
to_md_list(link, 1)
elif (eq(to_md_name(),"h2")):
to_md_list(link, 2)
elif (eq(to_md_name(), "h3")):
to_md_list(link, 3)
elif (eq(to_md_name(), "h4")):
to_md_list(link, 4)
elif (eq(to_md_name(), "h5")):
to_md_list(link, 5)
else:
None"##,
        r##".h
| let link = to_link(add("#", to_text(self)), to_text(self), "");
| if (eq(to_md_name(), "h1")):
    to_md_list(link, 1)
  elif (eq(to_md_name(), "h2")):
    to_md_list(link, 2)
  elif (eq(to_md_name(), "h3")):
    to_md_list(link, 3)
  elif (eq(to_md_name(), "h4")):
    to_md_list(link, 4)
  elif (eq(to_md_name(), "h5")):
    to_md_list(link, 5)
  else:
    None
"##
    )]
    #[case::def(
        r#"def snake_to_camel(x):
  let words = split(x, "_")
  | foreach (word, words):
  let first_char = upcase(first(word))
  | let rest_str = downcase(slice(word, 1, len(word)))
  | s"${first_char}${rest_str}";
  | join("");
| snake_to_camel()"#,
        r#"def snake_to_camel(x):
  let words = split(x, "_")
  | foreach (word, words):
      let first_char = upcase(first(word))
      | let rest_str = downcase(slice(word, 1, len(word)))
      | s"${first_char}${rest_str}";
  | join("");
| snake_to_camel()
"#
    )]
    #[case::def(
        r#"def snake_to_camel(x): let words = split(x, "_") | foreach (word, words): let first_char = upcase(first(word)) | let rest_str = downcase(slice(word, 1, len(word))) | add(first_char, rest_str); | join("");| snake_to_camel()"#,
        r#"def snake_to_camel(x): let words = split(x, "_") | foreach (word, words): let first_char = upcase(first(word)) | let rest_str = downcase(slice(word, 1, len(word))) | add(first_char, rest_str); | join(""); | snake_to_camel()"#
    )]
    #[case::let_(r#"let test = "test""#, r#"let test = "test""#)]
    #[case::call(
        r#"test(
"test")"#,
        r#"test(
  "test")
"#
    )]
    #[case::call(
        r#"test(
"test"
  )"#,
        r#"test(
  "test"
)
"#
    )]
    #[case::call(
        r#"test(
"test"
,"test"
,true
  )"#,
        r#"test(
  "test"
  ,"test"
  ,true
)
"#
    )]
    #[case::interpolated_string(
        r#"test(
s"test${val1}"
  )"#,
        r#"test(
  s"test${val1}"
)
"#
    )]
    #[case::include("include  \"test.mq\"", "include \"test.mq\"")]
    #[case::nodes("nodes|nodes", "nodes | nodes")]
    #[case::fn_("fn(): program;", "fn(): program;")]
    #[case::fn_multiline(
        "fn(arg1,arg2):
        program;",
        "fn(arg1, arg2):
  program;
"
    )]
    #[case::fn_args("map( fn():program;)", "map(fn(): program;)")]
    #[case::array_empty("[]", "[]")]
    #[case::array_single_element("[1]", "[1]")]
    #[case::array_multiple_elements("[1,2,3]", "[1, 2, 3]")]
    #[case::array_mixed_types("[1,\"test\",true]", "[1, \"test\", true]")]
    #[case::array_nested("[[1,2],[3,4]]", "[[1, 2], [3, 4]]")]
    #[case::array_with_spaces("[ 1 , 2 , 3 ]", "[1, 2, 3]")]
    #[case::array_multiline(
        r#"[
    1,
    2,
    3
    ]"#,
        "[\n  1,\n  2,\n  3\n]\n"
    )]
    #[case::let_with_array(r#"let arr = [1, 2, 3]"#, r#"let arr = [1, 2, 3]"#)]
    // --- quote/unquote simple & nested cases ---
    #[case::quote_simple("quote do test;", "quote do test;")]
    #[case::unquote_simple("unquote(test)", "unquote(test)")]
    #[case::quote_unquote_nested("quote do unquote(test);", "quote do unquote(test);")]
    #[case::quote_with_pipe("quote do test | unquote(expr);", "quote do test | unquote(expr);")]
    #[case::quote_quote_nested("quote do quote do test;;", "quote do quote do test;;")]
    #[case::let_with_array_multiline(
        r#"let arr = [
1,
2,
3
]"#,
        "let arr = [\n  1,\n  2,\n  3\n]\n"
    )]
    #[case::dict_empty("{}", "{}")]
    #[case::def_with_let_and_array(
        r#"def foo():
  let arr = [
  1,
  2,
  3];
"#,
        r#"def foo():
  let arr = [
    1,
    2,
    3
  ];
"#
    )]
    #[case::dict_single_pair("{\"key\": \"value\"}", "{\"key\": \"value\"}")]
    #[case::dict_multiple_pairs(
        "{\"key1\": \"value1\", \"key2\": \"value2\"}",
        "{\"key1\": \"value1\", \"key2\": \"value2\"}"
    )]
    #[case::dict_with_spaces("{ \"key\" : \"value\" }", "{\"key\": \"value\"}")]
    #[case::dict_mixed_types(
        "{\"str\": \"value\", \"num\": 42, \"bool\": true}",
        "{\"str\": \"value\", \"num\": 42, \"bool\": true}"
    )]
    #[case::dict_nested("{\"outer\": {\"inner\": \"value\"}}", "{\"outer\": {\"inner\": \"value\"}}")]
    #[case::dict_multiline(
        r#"{
"key1": "value1",
"key2": "value2"
}"#,
        "{\n  \"key1\": \"value1\",\n  \"key2\": \"value2\"\n}\n"
    )]
    #[case::dict_multiline_mixed(
        r#"{
"str": "value",
"num": 42,
"bool": true
}"#,
        "{\n  \"str\": \"value\",\n  \"num\": 42,\n  \"bool\": true\n}\n"
    )]
    #[case::equal_operator("let x = 1 == 2", "let x = 1 == 2")]
    #[case::not_equal_operator("let y = 3 != 4", "let y = 3 != 4")]
    #[case::string_with_newline(r#""line1\nline2""#, r#""line1\nline2""#)]
    #[case::plus_operator("let x = 1 + 2", "let x = 1 + 2")]
    #[case::let_newline_after_equal(
        r#"let x =
"test""#,
        r#"let x =
  "test"
"#
    )]
    #[case::let_with_if_multiline(
        r#"let x = if(test):
test
else:
test2"#,
        r#"let x = if (test):
  test
else:
  test2
"#
    )]
    #[case::let_with_while_multiline(
        r#"let x = while(condition()):
process();"#,
        r#"let x = while (condition()):
  process();
"#
    )]
    #[case::less_than_operator("let x = 1 < 2", "let x = 1 < 2")]
    #[case::less_than_equal_operator("let x = 1 <= 2", "let x = 1 <= 2")]
    #[case::greater_than_operator("let x = 2 > 1", "let x = 2 > 1")]
    #[case::greater_than_equal_operator("let x = 2 >= 1", "let x = 2 >= 1")]
    #[case::range_operator("1..1", "1..1")]
    #[case::range_operator_with_spaces("1 .. 1", "1..1")]
    #[case::range_operator_with_variables("x..y", "x..y")]
    #[case::range_operator_with_string(r#""1" .. "2""#, r#""1".."2""#)]
    #[case::selector_attr(".code.lang", ".code.lang")]
    #[case::standalone_attr_selector(".lang", ".lang")]
    #[case::standalone_attr_selector_value(".value", ".value")]
    #[case::env("let ENV = $env", "let ENV = $env")]
    #[case::mul("1 * 1", "1 * 1")]
    #[case::mul("1 / 1", "1 / 1")]
    #[case::and("true && false", "true && false")]
    #[case::or("true || false", "true || false")]
    #[case::binary_op_multiline_or(
        r#"1
|| 2
|| 3"#,
        "1
 || 2
 || 3
"
    )]
    #[case::binary_op_multiline(
        r#"let v = 1
|| 2
|| 3"#,
        "let v = 1
   || 2
   || 3
"
    )]
    #[case::def_contains(
        r#"def contains(haystack, needle):
if (is_dict(haystack)):
  not(is_none(get(haystack, needle)))
else:
  index(haystack, needle) != -1;
"#,
        r#"def contains(haystack, needle):
  if (is_dict(haystack)):
    not(is_none(get(haystack, needle)))
  else:
    index(haystack, needle) != -1;
"#
    )]
    #[case::escape_sequence_clear_screen(r#""\x1b[2J\x1b[H""#, r#""\x1b[2J\x1b[H""#)]
    #[case::control_character_bell(r#""\x07""#, r#""\x07""#)]
    #[case::control_character_backspace(r#""\x08""#, r#""\x08""#)]
    #[case::control_character_vertical_tab(r#""\x0b""#, r#""\x0b""#)]
    #[case::control_character_form_feed(r#""\x0c""#, r#""\x0c""#)]
    #[case::control_character_escape(r#""\x1b""#, r#""\x1b""#)]
    #[case::control_character_delete(r#""\x7f""#, r#""\x7f""#)]
    #[case::not_operator("!true", "!true")]
    #[case::let_with_if_multiline_in_while(
        r#"while(condition()):
  let x = 1
  | let y = if(test):
test
else:
test2
end
"#,
        r#"while (condition()):
  let x = 1
  | let y = if (test):
      test
    else:
      test2
end
"#
    )]
    #[case::let_with_while_multiline(
        r#"let x = while(condition()):
process();"#,
        r#"let x = while (condition()):
  process();
"#
    )]
    #[case::let_with_while_multiline2(
        r#""test"
| let x = while(condition()):
process();"#,
        r#""test"
| let x = while (condition()):
  process();
"#
    )]
    #[case::array_index_access("let arr = [1, 2, 3]\n|arr[1]", "let arr = [1, 2, 3]\n| arr[1]\n")]
    #[case::array_index_access_inline("arr[0]", "arr[0]")]
    #[case::dict_index_access(
        "let d = {\"key\": \"value\"}\n|d[\"key\"]",
        "let d = {\"key\": \"value\"}\n| d[\"key\"]\n"
    )]
    #[case::dict_index_access_inline("d[\"key\"]", "d[\"key\"]")]
    #[case::comment_first_line("# comment\nlet x = 1", "# comment\nlet x = 1\n")]
    #[case::comment_inline("let x = 1 # inline comment", "let x = 1 # inline comment")]
    #[case::comment_multiline(
        "let x = 1\n# multiline comment\n| let y = 2",
        "let x = 1\n# multiline comment\n| let y = 2\n"
    )]
    #[case::comment_after_expr_multiline(
        "if(test):\n  test # comment\nelse:\n  test2 # comment2",
        "if (test):\n  test # comment\nelse:\n  test2 # comment2\n"
    )]
    #[case::comment_after_expr_inline(
        "if(test): test # comment else: test2 # comment2",
        "if (test): test # comment else: test2 # comment2"
    )]
    #[case::fn_as_call_arg_single_line("map(fn(): program;)", "map(fn(): program;)")]
    #[case::fn_as_call_arg_multi_line(
        "map(\n  fn(arg):\n    process(arg);\n)",
        "map(\n  fn(arg):\n    process(arg);\n)\n"
    )]
    #[case::fn_as_call_arg_with_other_args("map(fn(): program;, 1, \"test\")", "map(fn(): program;, 1, \"test\")")]
    #[case::nested_fn_as_call_arg("outer(map(fn(): inner();))", "outer(map(fn(): inner();))")]
    #[case::fn_as_call_arg_with_multiline_args(
        "map(\n  fn(x):\n    process(x);\n  ,\n  fn(y):\n    process(y);\n)",
        "map(\n  fn(x):\n    process(x);\n  ,\n  fn(y):\n    process(y);\n)\n"
    )]
    #[case::group_simple("(1)", "(1)")]
    #[case::group_with_expr("(1 + 2)", "(1 + 2)")]
    #[case::group_with_nested_group("((1 + 2) * 3)", "((1 + 2) * 3)")]
    #[case::group_with_multiple_ops("(1 + 2 * 3)", "(1 + 2 * 3)")]
    #[case::group_with_array("(array(1, 2))", "(array(1, 2))")]
    #[case::group_with_dict("({\"key\": \"value\"})", "({\"key\": \"value\"})")]
    #[case::group_with_comment("(1 + 2) # group comment", "(1 + 2) # group comment")]
    #[case::group_with_call("(test(1, 2))", "(test(1, 2))")]
    #[case::group_with_if("(if(test): test else: test2)", "(if (test): test else: test2)")]
    #[case::group_with_let("(let x = 1)", "(let x = 1)")]
    #[case::fn_end("fn(): test end", "fn(): test end")]
    #[case::negate_operator("-v", "-v")]
    #[case::try_catch_multiline(
        r#"try:
  process()
catch:
  handle_error()"#,
        "try:
  process()
catch:
  handle_error()
"
    )]
    #[case::try_catch_oneline("try: process() catch: handle_error()", "try: process() catch: handle_error()")]
    #[case::try_catch_with_finally(
        r#"try:
  process()
catch:
  handle_error()
"#,
        "try:
  process()
catch:
  handle_error()
"
    )]
    #[case::coalesce_operator("let x = a?? b", "let x = a ?? b")]
    #[case::coalesce_operator_with_call(
        "let result = get_value() ?? default()",
        "let result = get_value() ?? default()"
    )]
    #[case::coalesce_operator_with_literal("let x = value ?? 42", "let x = value ?? 42")]
    #[case::coalesce_operator_with_string("let s = str ?? \"default\"", "let s = str ?? \"default\"")]
    #[case::coalesce_operator_chain("let x = a ?? b ?? c", "let x = a ?? b ?? c")]
    #[case::coalesce_operator_in_if(
        "if(a ?? b): do_something() else: do_other()",
        "if (a ?? b): do_something() else: do_other()"
    )]
    #[case::coalesce_operator_in_array("[a ?? b, c]", "[a ?? b, c]")]
    #[case::coalesce_operator_in_dict("{\"key\": a ?? b}", "{\"key\": a ?? b}")]
    #[case::coalesce_operator_with_comment("let x = a ?? b # fallback", "let x = a ?? b # fallback")]
    #[case::call_dynamic("v[0](1,2,3)", "v[0](1, 2, 3)")]
    #[case::dict_with_fn_value_multiline(
        r#"{
"key1": fn():
process();
,"key2": "value2"
}"#,
        r#"{
  "key1": fn():
    process();
  ,"key2": "value2"
}
"#
    )]
    #[case::do_block_multiline(
        r#"do
  process1()
  | process2()
end
"#,
        "do
  process1()
  | process2()
end
"
    )]
    #[case::do_block_oneline("do process1() | process2();", "do process1() | process2();")]
    #[case::let_with_do_block_multiline(
        r#"let result = do
  step1()
  | step2();
"#,
        "let result = do
  step1()
  | step2();
"
    )]
    #[case::let_with_do_block_oneline("let result = do step1() | step2();", "let result = do step1() | step2();")]
    #[case::symbol_with_ident(":foo", ":foo")]
    #[case::symbol_with_string(r#":"bar""#, r#":"bar""#)]
    #[case::symbol_with_spaces(":  foo", ":foo")]
    #[case::symbol_in_array("[:foo, :bar]", "[:foo, :bar]")]
    #[case::symbol_in_dict(r#"{:key: "value"}"#, r#"{:key: "value"}"#)]
    #[case::symbol_comparison(":foo == :bar", ":foo == :bar")]
    #[case::match_simple(
        "match(x): | 1: \"one\" | _: \"other\" end",
        "match (x): | 1: \"one\" | _: \"other\" end"
    )]
    #[case::match_multiline(
        r#"match(x):
| 1: "one"
| 2: "two"
| _: "other"
end"#,
        r#"match (x):
  | 1: "one"
  | 2: "two"
  | _: "other"
end
"#
    )]
    #[case::match_with_guard(
        "match(x): | n if(n > 0): \"positive\" | _: \"non-positive\" end",
        "match (x): | n if (n > 0): \"positive\" | _: \"non-positive\" end"
    )]
    #[case::match_with_array_pattern(
        "match(arr): | [a, b]: add(a, b) | _: 0 end",
        "match (arr): | [a, b]: add(a, b) | _: 0 end"
    )]
    #[case::match_with_array_pattern_with_literal(
        "match(arr): | [1, 2]: add(1, 2) | _: 0 end",
        "match (arr): | [1, 2]: add(1, 2) | _: 0 end"
    )]
    #[case::match_with_array_pattern_with_symbol(
        "match(arr): | [:string, :string]: add(1, 2) | _: 0 end",
        "match (arr): | [:string, :string]: add(1, 2) | _: 0 end"
    )]
    #[case::match_with_dict_pattern(
        "match(obj): | {name: n}: n | _: \"unknown\" end",
        "match (obj): | {name: n}: n | _: \"unknown\" end"
    )]
    #[case::match_nested_in_let(
        r#"let result = match(x):
| 1: "one"
| 2: "two"
| _: "other"
end"#,
        r#"let result = match (x):
  | 1: "one"
  | 2: "two"
  | _: "other"
end
"#
    )]
    #[case::match_with_type_pattern(
        "match(val): | :string: \"is string\" | :number: \"is number\" | _: \"other\" end",
        "match (val): | :string: \"is string\" | :number: \"is number\" | _: \"other\" end"
    )]
    #[case::match_multiline_in_pipe(
        r#""test"
| match(x):
  | 1: "one"
  | 2: "two"
  end"#,
        r#""test"
| match (x):
    | 1: "one"
    | 2: "two"
  end
"#
    )]
    #[case::dict_nested_multiline_level3(
        r#"{
"level1": {
"level2": {
"level3": "value"
}
}
}"#,
        r#"{
  "level1": {
    "level2": {
      "level3": "value"
    }
  }
}
"#
    )]
    #[case::array_nested_multiline_level3(
        r#"[
[
[
"value"
]
]
]"#,
        r#"[
  [
    [
      "value"
    ]
  ]
]
"#
    )]
    #[case::comment_with_newline("# comment\nlet x = 1", "# comment\nlet x = 1\n")]
    #[case::comment_with_indent(
        "if(test):\n  test # comment\nelse:\n  test2 # comment2",
        "if (test):\n  test # comment\nelse:\n  test2 # comment2\n"
    )]
    #[case::comment_inline_with_indent("let x = 1 # inline comment", "let x = 1 # inline comment")]
    #[case::comment_multiline_with_indent(
        "let x = 1\n  # multiline comment\n| let y = 2",
        "let x = 1\n# multiline comment\n| let y = 2\n"
    )]
    #[case::comment_after_expr_multiline_with_indent(
        "if(test):\n  test # comment\nelse:\n    test2 # comment2",
        "if (test):\n  test # comment\nelse:\n  test2 # comment2\n"
    )]
    #[case::comment_after_expr_inline_with_indent(
        "if(test): test # comment else:    test2 # comment2",
        "if (test): test # comment else:    test2 # comment2"
    )]
    #[case::interpolated_string_with_escaped_brackets(r#"s"\\[${phrase}\\]\\(""#, r#"s"\\[${phrase}\\]\\(""#)]
    #[case::interpolated_string_with_backslash(r#"s"\\test""#, r#"s"\\test""#)]
    #[case::module_with_body(
        r#"module test:
import "foo.mq"
| def main(): test();
end"#,
        r#"module test:
  import "foo.mq"
  | def main(): test();
end
"#
    )]
    #[case::module_with_do(
        r#"module test  do
import "foo.mq"
| def main(): test();
end"#,
        r#"module test do
  import "foo.mq"
  | def main(): test();
end
"#
    )]
    #[case::module_with_do_and_new_line(
        r#"module test
do
import "foo.mq"
| def main(): test();
end"#,
        r#"module test
  do
  import "foo.mq"
  | def main(): test();
end
"#
    )]
    #[case::comment_preserves_indent_after_newline(
        "let x = 1\n    # indented comment after newline\n| let y = 2",
        "let x = 1\n# indented comment after newline\n| let y = 2\n"
    )]
    #[case::comment_preserves_indent_after_newline_deep(
        "if(test):\n  test\n    # deeper indented comment\nelse:\n  test2",
        "if (test):\n  test\n# deeper indented comment\nelse:\n  test2\n"
    )]
    #[case::comment_preserves_indent_after_newline_array(
        "[1,\n    # comment for 2\n  2]",
        "[1,\n  # comment for 2\n  2]\n"
    )]
    #[case::comment_preserves_indent_after_newline_dict(
        "{\n  \"a\": 1,\n    # comment for b\n  \"b\": 2\n}",
        "{\n  \"a\": 1,\n  # comment for b\n  \"b\": 2\n}\n"
    )]
    #[case::match_preserves_indent_after_newline(
        "let v = \nmatch (x):\n|    1: \"one\"\n|    2: \"two\"\n  end",
        "let v =\n  match (x):\n    | 1: \"one\"\n    | 2: \"two\"\n  end\n"
    )]
    #[case::match_with_do_block(
        "let result = match (x):\n| 1: do\n    foo() |\n    bar()\n  end\n| 2: \"two\"\nend",
        "let result = match (x):\n  | 1: do\n      foo() |\n      bar()\n    end\n  | 2: \"two\"\nend\n"
    )]
    #[case::assign("var i=0 | i=i + 1", "var i = 0 | i = i + 1")]
    #[case::assign_right_multiline(
        r#"let x =
"test""#,
        r#"let x =
  "test"
"#
    )]
    #[case::assign_right_multiline_pipe(
        r#"let x =
"test"
| upcase()"#,
        r#"let x =
  "test"
| upcase()
"#
    )]
    #[case::assign_right_multiline_if(
        r#"let x =
if(test):
test
else:
test2"#,
        r#"let x =
  if (test):
    test
  else:
    test2
"#
    )]
    #[case::macro_simple_oneline("macro test(): value;", "macro test(): value;")]
    #[case::macro_with_args_oneline("macro test(x, y): add(x, y);", "macro test(x, y): add(x, y);")]
    #[case::macro_multiline(
        r#"macro test():
  value;"#,
        r#"macro test():
  value;
"#
    )]
    #[case::macro_with_args_multiline(
        r#"macro test(x, y):
  add(x, y);"#,
        r#"macro test(x, y):
  add(x, y);
"#
    )]
    #[case::macro_with_do_block(r#"macro test(): do value end"#, r#"macro test(): do value end"#)]
    #[case::macro_with_do_block_multiline(
        r#"macro breakpoint():
do
if(is_debug_mode()):
_breakpoint()
end
end"#,
        r#"macro breakpoint():
  do
    if (is_debug_mode()):
      _breakpoint()
  end
end
"#
    )]
    #[case::macro_with_do_block_no_colon(
        r#"macro breakpoint() do
quote do
if(is_debug_mode()):_breakpoint()
end
end"#,
        r#"macro breakpoint() do
  quote do
    if (is_debug_mode()): _breakpoint()
  end
end
"#
    )]
    #[case::macro_with_if_multiline(
        r#"macro test(x):
if(x > 0):
positive()
else:
negative()
end"#,
        r#"macro test(x):
  if (x > 0):
    positive()
  else:
    negative()
end
"#
    )]
    #[case::macro_complex(
        r#"macro debug(msg):do
let formatted=s"DEBUG: ${to_string(msg)}"
|stderr(formatted)
end"#,
        r#"macro debug(msg): do
    let formatted = s"DEBUG: ${to_string(msg)}"
    | stderr(formatted)
  end
"#
    )]
    #[case::quote_multiline(
        r#"quote do
test
|unquote(expr)
;"#,
        r#"quote do
  test
  | unquote(expr);
"#
    )]
    #[case::quote_multiline_nested(
        r#"quote do
let x = 1
|let y = 2
|unquote(expr)
;"#,
        r#"quote do
  let x = 1
  | let y = 2
  | unquote(expr);
"#
    )]
    #[case::quote_multiline_with_if(
        r#"quote do
if(test):
value
else:
other
end
;"#,
        r#"quote do
  if (test):
    value
  else:
    other
end;
"#
    )]
    #[case::macro_call_test(
        r#"unless(cond) do
expr
end"#,
        r#"unless(cond) do
  expr
end
"#
    )]
    #[case::if_without_colon_oneline("if(test) 1 else 2", "if (test) 1 else 2")]
    #[case::if_without_colon_multiline(
        r#"if(test)
test
else
test2"#,
        r#"if (test)
  test
else
  test2
"#
    )]
    #[case::if_elif_else_without_colon_oneline("if(test) 1 elif(test2) 2 else 3", "if (test) 1 elif (test2) 2 else 3")]
    #[case::if_elif_else_without_colon_multiline(
        r#"if(test)
test
elif(test2)
test2
else
test3"#,
        r#"if (test)
  test
elif (test2)
  test2
else
  test3
"#
    )]
    #[case::if_without_colon_with_do_block(
        r#"if(test) do
test
end
else do
test2
end"#,
        r#"if (test) do
    test
  end
else do
    test2
  end
"#
    )]
    #[case::while_do_end_oneline("while(x > 0) do x - 1 end", "while (x > 0) do x - 1 end")]
    #[case::while_do_end_multiline(
        r#"while(x > 0) do
let x = x - 1 | x
end"#,
        r#"while (x > 0) do
  let x = x - 1 | x
end
"#
    )]
    #[case::while_do_end_with_break(
        r#"while(x < 10) do
let x = x + 1
| if(x == 3):
break
else:
x
end"#,
        r#"while (x < 10) do
  let x = x + 1
  | if (x == 3):
      break
    else:
      x
end
"#
    )]
    #[case::foreach_do_end_oneline(
        "foreach(item, arr) do process(item) end",
        "foreach (item, arr) do process(item) end"
    )]
    #[case::foreach_do_end_multiline(
        r#"foreach(x, array(1, 2, 3)) do
add(x, 1)
end"#,
        r#"foreach (x, array(1, 2, 3)) do
  add(x, 1)
end
"#
    )]
    #[case::foreach_do_end_with_continue(
        r#"foreach(x, array(1, 2, 3, 4, 5)) do
if(x == 3):
continue
else:
x + 10
end"#,
        r#"foreach (x, array(1, 2, 3, 4, 5)) do
  if (x == 3):
    continue
  else:
    x + 10
end
"#
    )]
    #[case::foreach_do_end_nested(
        r#"foreach(row, arr) do
foreach(x, row) do
x * 2
end
end"#,
        r#"foreach (row, arr) do
  foreach (x, row) do
    x * 2
  end
end
"#
    )]
    #[case::match_do_end_oneline(
        "match(x) do | 1: \"one\" | 2: \"two\" | _: \"other\" end",
        "match (x) do | 1: \"one\" | 2: \"two\" | _: \"other\" end"
    )]
    #[case::match_do_end_multiline(
        r#"match(2) do
| 1: "one"
| 2: "two"
| _: "other"
end"#,
        r#"match (2) do
  | 1: "one"
  | 2: "two"
  | _: "other"
end
"#
    )]
    #[case::match_do_end_type_pattern(
        r#"match(array(1, 2, 3)) do
| :array: "is_array"
| :number: "is_number"
| _: "other"
end"#,
        r#"match (array(1, 2, 3)) do
  | :array: "is_array"
  | :number: "is_number"
  | _: "other"
end
"#
    )]
    #[case::match_do_end_with_guard(
        r#"match(x) do
| n if(n > 0): "positive"
| _: "non-positive"
end"#,
        r#"match (x) do
  | n if (n > 0): "positive"
  | _: "non-positive"
end
"#
    )]
    #[case::def_with_default_single(
        "def greet(name = \"World\"):
        name;",
        "def greet(name = \"World\"):
  name;
"
    )]
    #[case::def_with_multiple_defaults(
        "def foo(a, b = 2, c = 3):
        a + b + c;",
        "def foo(a, b = 2, c = 3):
  a + b + c;
"
    )]
    #[case::def_with_mixed_params(
        "def bar(x,y=10):
        x+y;",
        "def bar(x, y = 10):
  x + y;
"
    )]
    #[case::def_with_array_default(
        "def baz(x,y=[1,2,3]):
        x+len(y);",
        "def baz(x, y = [1, 2, 3]):
  x + len(y);
"
    )]
    #[case::def_with_string_default(
        "def test(a,b=\"test\"):
        a+b;",
        "def test(a, b = \"test\"):
  a + b;
"
    )]
    #[case::def_with_number_default(
        "def calc(x=42):
        x * 2;",
        "def calc(x = 42):
  x * 2;
"
    )]
    #[case::def_with_boolean_default(
        "def result(x=true):
        x;",
        "def result(x = true):
  x;
"
    )]
    #[case::def_all_params_with_defaults(
        "def calc(a=1,b=2,c=3):
        a+b+c;",
        "def calc(a = 1, b = 2, c = 3):
  a + b + c;
"
    )]
    #[case::def_expr_with_default(
        "def calc(a=1,b=2,c=3 + 1):
        a+b+c;",
        "def calc(a = 1, b = 2, c = 3 + 1):
  a + b + c;
"
    )]
    #[case::def_expr_with_variadic(
        "def calc(v,*args):
        v + args;",
        "def calc(v, *args):
  v + args;
"
    )]
    #[case::assign_with_selector_attr("value.test|= value.attr", "value.test |= value.attr")]
    #[case::index_assign("arr[0]=10", "arr[0] = 10")]
    #[case::index_assign_string_key("dict[\"key\"]=\"value\"", "dict[\"key\"] = \"value\"")]
    #[case::index_compound_assign("arr[0]+=1", "arr[0] += 1")]
    #[case::selector_call_heading_single_arg(".h(1)", ".h(1)")]
    #[case::selector_call_heading_multi_arg(".h(1,2)", ".h(1, 2)")]
    #[case::selector_call_code_lang(".code(\"rust\")", ".code(\"rust\")")]
    #[case::selector_call_pipe(".h(1)|.text()", ".h(1) | .text()")]
    #[case::arrow_simple("->(): program;", "->(): program;")]
    #[case::arrow_multiline(
        "->(arg1,arg2):
        program;",
        "->(arg1, arg2):
  program;
"
    )]
    #[case::arrow_as_call_arg("map( ->():program;)", "map(->(): program;)")]
    #[case::arrow_as_call_arg_single_line("map(->(): program;)", "map(->(): program;)")]
    #[case::arrow_as_call_arg_with_other_args("map(->(): program;, 1, \"test\")", "map(->(): program;, 1, \"test\")")]
    #[case::nested_arrow_as_call_arg("outer(map(->(): inner();))", "outer(map(->(): inner();))")]
    #[case::arrow_end("->(): test end", "->(): test end")]
    #[case::bytes_literal_basic(r#"b"abc""#, r#"b"abc""#)]
    #[case::bytes_literal_hex(r#"b"\xf0\x9f\x99\x82""#, r#"b"\xf0\x9f\x99\x82""#)]
    #[case::bytes_literal_with_pipe(r#"b"abc"  |  len"#, r#"b"abc" | len"#)]
    #[case::bytes_literal_in_call(r#"len(b"abc")"#, r#"len(b"abc")"#)]
    fn test_format(#[case] code: &str, #[case] expected: &str) {
        let result = Formatter::new(None).format(code);
        assert_eq!(result.unwrap(), expected);
    }

    #[rstest]
    #[case::sort_imports(
        r#"import "c.mq"
| import "a.mq"
| import "b.mq""#,
        r#"import "a.mq"
| import "b.mq"
| import "c.mq"
"#
    )]
    #[case::sort_functions(
        r#"def z(): test;
def a(): test;
def m(): test;
"#,
        "def a(): test;\ndef m(): test;\ndef z(): test;\n"
    )]
    #[case::sort_fields(
        r#"let z = 1
| let a = 2
| let m = 3"#,
        r#"let a = 2
| let m = 3
| let z = 1
"#
    )]
    #[case::sort_mixed(
        r#"let z = 1
| import "b.mq"
def y(): test;
| let a = 2
| import "a.mq"
def b(): test;
macro m(): test"#,
        "import \"a.mq\"\n| import \"b.mq\"\n| let a = 2\n| let z = 1\n|\ndef b(): test;\ndef y(): test;\nmacro m(): test\n"
    )]
    #[case::sort_with_other(
        r#"def z(): test;
| let x = 1
| nodes
def a(): test;"#,
        "let x = 1\n|\ndef a(): test;\ndef z(): test;\n| nodes\n"
    )]
    #[case::sort_with_piped_calls(
        r#"def z(): test;
| let x = 1
| add() | mul() | sub()
def a(): test;
macro m(): test;
| let y = 2"#,
        "let x = 1\n| let y = 2\n|\ndef a(): test;\ndef z(): test;\nmacro m(): test\n| add()\n| mul()\n| sub()\n| ;\n"
    )]
    #[case::sort_all_types(
        r#"add() | sub()
def func_b(): test;
| let var_z = 1
| import "z.mq"
macro mac_y(): test;
| let var_a = 2
def func_a(): test;
| mul() | div()
| import "a.mq"
macro mac_x(): test;"#,
        "import \"a.mq\"\n| import \"z.mq\"\n| let var_a = 2\n| let var_z = 1\n|\ndef func_a(): test;\ndef func_b(): test;\nmacro mac_y(): test\nmacro mac_x(): test\n| add()\n| sub()\n| ;\n| mul()\n| div()\n| ;\n"
    )]
    fn test_format_with_sort(#[case] code: &str, #[case] expected: &str) {
        let config = FormatterConfig {
            indent_width: 2,
            sort_imports: true,
            sort_functions: true,
            sort_fields: true,
        };
        let result = Formatter::new(Some(config)).format(code);
        assert_eq!(result.unwrap(), expected);
    }
}
