/// CST-based formatter for Sixu language
///
/// This formatter preserves all comments and produces formatted output
/// with consistent spacing, indentation, and line breaks.
use crate::cst::node::*;

pub struct CstFormatter {
    indent_size: usize,
}

impl Default for CstFormatter {
    fn default() -> Self {
        Self { indent_size: 4 }
    }
}

impl CstFormatter {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn with_indent(indent_size: usize) -> Self {
        Self { indent_size }
    }

    /// Format a CST root node into a string
    pub fn format(&self, root: &CstRoot) -> String {
        let mut output = String::new();

        for node in &root.nodes {
            self.format_node(node, 0, &mut output);
        }

        // 确保文件以换行符结尾
        if !output.ends_with('\n') {
            output.push('\n');
        }

        output
    }

    fn format_node(&self, node: &CstNode, indent_level: usize, output: &mut String) {
        match node {
            CstNode::Trivia(trivia) => self.format_trivia(trivia, indent_level, output),
            CstNode::Paragraph(para) => self.format_paragraph(para, indent_level, output),
            CstNode::Command(cmd) => self.format_command(cmd, indent_level, output),
            CstNode::SystemCall(call) => self.format_systemcall(call, indent_level, output),
            CstNode::TextLine(text) => self.format_textline(text, indent_level, output),
            CstNode::Block(block) => self.format_block(block, indent_level, output),
            CstNode::EmbeddedCode(code) => self.format_embedded_code(code, indent_level, output),
            CstNode::Attribute(attr) => self.format_attribute(attr, indent_level, output),
            CstNode::Error { content, .. } => {
                // 保留错误节点的原始内容
                output.push_str(content);
                output.push('\n');
            }
        }
    }

    fn format_trivia(&self, trivia: &CstTrivia, indent_level: usize, output: &mut String) {
        match trivia {
            CstTrivia::Whitespace { content, .. } => {
                // 处理空行：如果包含2个或以上换行符（表示源码中有空行），输出一个空行
                let newline_count = content.chars().filter(|&c| c == '\n').count();
                if newline_count >= 2 {
                    // 多个换行符，输出一个空行
                    output.push('\n');
                }
            }
            CstTrivia::LineComment { content, .. } => {
                self.indent(indent_level, output);
                output.push_str("//");
                output.push_str(content);
                output.push('\n');
            }
            CstTrivia::BlockComment { content, .. } => {
                // 多行注释需要特殊处理
                let lines: Vec<&str> = content.lines().collect();

                if lines.len() <= 1 {
                    // 单行注释：/* content */
                    self.indent(indent_level, output);
                    output.push_str("/*");
                    output.push_str(content);
                    output.push_str("*/");
                    output.push('\n');
                } else {
                    // 多行注释：提取有意义的内容行，剥除已有的 * 前缀后重新格式化
                    let mut content_lines: Vec<&str> = Vec::new();
                    for line in &lines {
                        let trimmed = line.trim();
                        // 剥除已有的 * 前缀（避免重复格式化时不断叠加 *）
                        let stripped = if trimmed.starts_with("* ") {
                            &trimmed[2..]
                        } else if trimmed == "*" {
                            ""
                        } else if trimmed.starts_with('*') {
                            &trimmed[1..]
                        } else {
                            trimmed
                        };
                        content_lines.push(stripped);
                    }

                    // 去除首尾的空行（来自 /* 和 */ 边界）
                    while content_lines.first().map(|s| s.is_empty()).unwrap_or(false) {
                        content_lines.remove(0);
                    }
                    while content_lines.last().map(|s| s.is_empty()).unwrap_or(false) {
                        content_lines.pop();
                    }

                    self.indent(indent_level, output);
                    output.push_str("/*\n");

                    for line in &content_lines {
                        self.indent(indent_level, output);
                        output.push_str(" *");
                        if !line.is_empty() {
                            output.push(' ');
                            output.push_str(line);
                        }
                        output.push('\n');
                    }

                    self.indent(indent_level, output);
                    output.push_str(" */");
                    output.push('\n');
                }
            }
        }
    }

    fn format_paragraph(&self, para: &CstParagraph, indent_level: usize, output: &mut String) {
        // 段落前加一个空行（如果不是文件开头）
        if !output.is_empty() && !output.ends_with("\n\n") {
            output.push('\n');
        }

        // ::name
        output.push_str("::");
        output.push_str(&para.name);

        // 参数
        if !para.parameters.is_empty() {
            output.push('(');
            for (i, param) in para.parameters.iter().enumerate() {
                if i > 0 {
                    output.push_str(", ");
                }
                self.format_parameter(param, output);
            }
            output.push(')');
        }

        output.push(' ');
        self.format_block(&para.block, indent_level, output);
    }

    fn format_parameter(&self, param: &CstParameter, output: &mut String) {
        output.push_str(&param.name);
        if let Some(ref default_value) = param.default_value {
            output.push('=');
            self.format_value(default_value, output);
        }
    }

    fn format_block(&self, block: &CstBlock, indent_level: usize, output: &mut String) {
        // Block开括号需要缩进（除非是段落的根block，indent_level为0）
        if indent_level > 0 {
            self.indent(indent_level, output);
        }
        output.push_str("{\n");

        for child in &block.children {
            self.format_node(child, indent_level + 1, output);
        }

        self.indent(indent_level, output);
        output.push_str("}\n");
    }

    fn format_attribute(&self, attr: &CstAttribute, indent_level: usize, output: &mut String) {
        self.indent(indent_level, output);
        output.push_str("#[");
        output.push_str(&attr.keyword);
        if let Some(condition) = &attr.condition {
            output.push_str("(\"");
            output.push_str(condition);
            output.push_str("\")");
        }
        output.push_str("]\n");
    }

    fn format_command(&self, cmd: &CstCommand, indent_level: usize, output: &mut String) {
        self.indent(indent_level, output);

        output.push('@');
        output.push_str(&cmd.command);

        if !cmd.arguments.is_empty() {
            match cmd.syntax {
                CommandSyntax::Parenthesized { .. } => {
                    // 括号语法：@cmd(a=1, b=2)
                    output.push('(');
                    for (i, arg) in cmd.arguments.iter().enumerate() {
                        if i > 0 {
                            output.push_str(", ");
                        }
                        self.format_argument(arg, output);
                    }
                    output.push(')');
                }
                CommandSyntax::SpaceSeparated => {
                    // 空格分隔：@cmd a=1 b=2
                    for arg in &cmd.arguments {
                        output.push(' ');
                        self.format_argument(arg, output);
                    }
                }
            }
        }

        output.push('\n');
    }

    fn format_systemcall(&self, call: &CstSystemCall, indent_level: usize, output: &mut String) {
        self.indent(indent_level, output);

        output.push('#');
        output.push_str(&call.command);

        if !call.arguments.is_empty() {
            match call.syntax {
                CommandSyntax::Parenthesized { .. } => {
                    // 括号语法：#goto(paragraph="main")
                    output.push('(');
                    for (i, arg) in call.arguments.iter().enumerate() {
                        if i > 0 {
                            output.push_str(", ");
                        }
                        self.format_argument(arg, output);
                    }
                    output.push(')');
                }
                CommandSyntax::SpaceSeparated => {
                    // 空格分隔：#goto paragraph="main"
                    for arg in &call.arguments {
                        output.push(' ');
                        self.format_argument(arg, output);
                    }
                }
            }
        }

        output.push('\n');
    }

    fn format_argument(&self, arg: &CstArgument, output: &mut String) {
        output.push_str(&arg.name);
        if let Some(ref value) = arg.value {
            output.push('=');
            self.format_value(value, output);
        }
    }

    fn format_value(&self, value: &CstValue, output: &mut String) {
        // 数组类型统一规范化为紧缩格式（不含空格），其余类型直接输出原始文本
        if matches!(value.kind, CstValueKind::Array) {
            if let crate::format::RValue::Literal(lit) = &value.parsed {
                output.push_str(&Self::format_literal_compact(lit));
                return;
            }
        }
        output.push_str(&value.raw);
    }

    /// 将 Literal 格式化为紧缩形式（数组内部无空格）
    fn format_literal_compact(lit: &crate::format::Literal) -> String {
        use crate::format::Literal;
        match lit {
            Literal::Array(elements) => {
                let parts: Vec<String> = elements
                    .iter()
                    .map(Self::format_literal_compact)
                    .collect();
                format!("[{}]", parts.join(","))
            }
            Literal::String(s) => format!("\"{}\"", s),
            other => other.to_string(),
        }
    }

    fn format_textline(&self, text: &CstTextLine, indent_level: usize, output: &mut String) {
        self.indent(indent_level, output);

        if let Some(ref leading) = text.leading {
            self.format_leading_text(leading, output);
            output.push(' ');
        }

        if let Some(ref main_text) = text.text {
            self.format_text(main_text, output);
        }

        if let Some(ref tailing) = text.tailing {
            output.push(' ');
            self.format_tailing_text(tailing, output);
        }

        output.push('\n');
    }

    fn format_leading_text(&self, leading: &CstLeadingText, output: &mut String) {
        output.push('[');
        match &leading.content {
            CstLeadingTextContent::Text(s) => output.push_str(s),
            CstLeadingTextContent::Template(tpl) => {
                output.push('`');
                self.format_template_literal(tpl, output);
                output.push('`');
            }
        }
        output.push(']');
    }

    fn format_text(&self, text: &CstText, output: &mut String) {
        // raw 字段已经包含了引号等原始文本
        output.push_str(&text.raw);
    }

    fn format_template_literal(&self, tpl: &CstTemplateLiteral, output: &mut String) {
        for part in &tpl.parts {
            match part {
                CstTemplatePart::Text { content, .. } => {
                    output.push_str(content);
                }
                CstTemplatePart::Value { variable, .. } => {
                    output.push_str("${");
                    output.push_str(&variable.chain.join("."));
                    output.push('}');
                }
            }
        }
    }

    fn format_tailing_text(&self, tailing: &CstTailingText, output: &mut String) {
        output.push('#');
        output.push_str(&tailing.marker);
    }

    fn format_embedded_code(
        &self,
        code: &CstEmbeddedCode,
        indent_level: usize,
        output: &mut String,
    ) {
        match code.syntax {
            EmbeddedCodeSyntax::Brace => {
                let trimmed_code = code.code.trim();
                if trimmed_code.contains('\n') {
                    // 多行语法：@{ \n code \n }
                    self.indent(indent_level, output);
                    output.push_str("@{\n");

                    // 先去除尾部所有空白（包括 } 前的缩进空格），再去除首部换行。
                    // 不能只用 trim_matches(\n|\r)，因为 parser 会把 } 前的缩进空格
                    // 也捕获进 code.code，若不 trim 空格，每轮格式化会多出一行"空行"。
                    let code_content = code
                        .code
                        .trim_end()
                        .trim_start_matches(|c: char| c == '\n' || c == '\r');
                    output.push_str(code_content);
                    output.push('\n');

                    self.indent(indent_level, output);
                    output.push_str("}\n");
                } else {
                    // 单行语法：@{ code }
                    self.indent(indent_level, output);
                    output.push_str("@{ ");
                    output.push_str(trimmed_code);
                    output.push_str(" }\n");
                }
            }
            EmbeddedCodeSyntax::Hash => {
                let trimmed_code = code.code.trim();
                if trimmed_code.contains('\n') {
                    // 多行语法：开始和结束标记在独立的行上，代码内容保留原样
                    self.indent(indent_level, output);
                    output.push_str("##\n");
                    // 去除尾部空白（parser 会捕获闭合 ## 前的缩进空白，
                    // 若不 trim 会在每轮格式化中累积空行）
                    let code_content = code.code.trim_end();
                    output.push_str(code_content);
                    output.push('\n');
                    self.indent(indent_level, output);
                    output.push_str("##\n");
                } else {
                    // 单行语法：## code ##
                    self.indent(indent_level, output);
                    output.push_str("## ");
                    output.push_str(trimmed_code);
                    output.push_str(" ##\n");
                }
            }
        }
    }

    fn indent(&self, level: usize, output: &mut String) {
        for _ in 0..(level * self.indent_size) {
            output.push(' ');
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::cst::parser::parse_tolerant;

    #[test]
    fn test_format_simple_command() {
        let input = "@command(arg=1)";
        let cst = parse_tolerant("test", input);
        let formatter = CstFormatter::new();
        let result = formatter.format(&cst);

        assert!(result.contains("@command(arg=1)"));
    }

    #[test]
    fn test_format_array_compact() {
        let formatter = CstFormatter::new();

        // 已紧缩的输入应原样保留
        let cst = parse_tolerant("test", "@cmd x=[0,0]\n");
        let result = formatter.format(&cst);
        assert!(result.contains("@cmd x=[0,0]"), "got: {}", result);

        // 含空格的输入应规范化为紧缩格式
        let cst = parse_tolerant("test", "@cmd x=[ 0, 0 ]\n");
        let result = formatter.format(&cst);
        assert!(result.contains("@cmd x=[0,0]"), "got: {}", result);

        // 嵌套数组
        let cst = parse_tolerant("test", "@cmd pts=[[1, 2], [3, 4]]\n");
        let result = formatter.format(&cst);
        assert!(result.contains("@cmd pts=[[1,2],[3,4]]"), "got: {}", result);

        // 含字符串元素的数组（规范化为双引号）
        let cst = parse_tolerant("test", "@cmd tags=[\"a\", \"b\"]\n");
        let result = formatter.format(&cst);
        assert!(result.contains(r#"@cmd tags=["a","b"]"#), "got: {}", result);

        // 格式化幂等性：再次格式化结果不变
        let cst2 = parse_tolerant("test", &result);
        let result2 = formatter.format(&cst2);
        assert_eq!(result, result2, "Array formatting is not idempotent");
    }

    #[test]
    fn test_format_paragraph() {
        let input = r#"
::test {
@command(arg=1)
}
"#;
        let cst = parse_tolerant("test", input);
        let formatter = CstFormatter::new();
        let result = formatter.format(&cst);

        assert!(result.contains("::test {"));
        assert!(result.contains("    @command(arg=1)"));
        assert!(result.contains("}"));
    }

    #[test]
    fn test_format_preserves_comments() {
        let input = r#"
// 这是注释
::test {
    /* 块注释 */
    @command arg=1
}
"#;
        let cst = parse_tolerant("test", input);
        let formatter = CstFormatter::new();
        let result = formatter.format(&cst);

        // 应该保留注释
        assert!(result.contains("// 这是注释"));
        assert!(result.contains("/* 块注释 */"));
    }

    #[test]
    fn test_format_multiple_paragraphs() {
        let input = r#"
::first {
    @cmd1 arg=1
}

::second {
    @cmd2 arg=2
}
"#;
        let cst = parse_tolerant("test", input);
        let formatter = CstFormatter::new();
        let result = formatter.format(&cst);

        assert!(result.contains("::first {"));
        assert!(result.contains("::second {"));
        // 段落间应该有空行
        assert!(result.contains("}\n\n::second"));
    }

    #[test]
    fn test_format_text_line() {
        let input = r#"
::test {
    [speaker] "Hello, world!"
}
"#;
        let cst = parse_tolerant("test", input);
        let formatter = CstFormatter::new();
        let result = formatter.format(&cst);

        println!("Formatted result:\n{}", result);
        // 文本行目前可能还没完全实现解析
        assert!(result.contains("::test {"));
    }

    #[test]
    fn test_format_system_call() {
        let input = r#"
::test {
    #goto(next)
}
"#;
        let cst = parse_tolerant("test", input);
        let formatter = CstFormatter::new();
        let result = formatter.format(&cst);

        assert!(result.contains("#goto(next)"));
    }

    #[test]
    fn test_format_preserves_indent_before_comments() {
        let input = r#"
::test {
    // 这是一个注释
    @command arg=1
    /* 这是块注释 */
    @another arg=2
}
"#;
        let cst = parse_tolerant("test", input);
        let formatter = CstFormatter::new();
        let result = formatter.format(&cst);

        println!("Formatted result:\n{}", result);
        // 注释应该有正确的缩进
        assert!(result.contains("    // 这是一个注释"));
        assert!(result.contains("    /* 这是块注释 */"));
    }

    #[test]
    fn test_format_no_extra_blank_lines() {
        let input = r#"
::test {
    @cmd1 arg=1
    @cmd2 arg=2
    @cmd3 arg=3
}
"#;
        let cst = parse_tolerant("test", input);
        let formatter = CstFormatter::new();
        let result = formatter.format(&cst);

        println!("Formatted result:\n{}", result);
        // 命令之间不应该有空行（原本没有空行的地方）
        assert!(!result.contains("@cmd1(arg=1)\n\n    @cmd2"));
        assert!(!result.contains("@cmd2(arg=2)\n\n    @cmd3"));
    }

    #[test]
    fn test_format_reduces_multiple_blank_lines() {
        let input = r#"
::test {
    @cmd1(arg=1)


    @cmd2(arg=2)
}
"#;
        let cst = parse_tolerant("test", input);
        let formatter = CstFormatter::new();
        let result = formatter.format(&cst);

        println!("Formatted result:\n{}", result);
        // 多个空行应该被缩减为一个
        assert!(result.contains("@cmd1(arg=1)\n\n    @cmd2"));
    }

    #[test]
    fn test_format_preserves_command_syntax() {
        // 测试括号语法保留
        let input1 = r#"
::test {
    @command(arg=1, flag)
}
"#;
        let cst1 = parse_tolerant("test", input1);
        let formatter = CstFormatter::new();
        let result1 = formatter.format(&cst1);

        println!("Parenthesized syntax result:\n{}", result1);
        assert!(result1.contains("@command(arg=1, flag)"));

        // 测试空格分隔语法保留
        let input2 = r#"
::test {
    @command arg=1 flag
}
"#;
        let cst2 = parse_tolerant("test", input2);
        let result2 = formatter.format(&cst2);

        println!("Space-separated syntax result:\n{}", result2);
        assert!(result2.contains("@command arg=1 flag"));
    }

    #[test]
    fn test_format_preserves_systemcall_syntax() {
        // 测试括号语法保留
        let input1 = r#"
::test {
    #goto(paragraph="main")
}
"#;
        let cst1 = parse_tolerant("test", input1);
        let formatter = CstFormatter::new();
        let result1 = formatter.format(&cst1);

        println!("Parenthesized systemcall result:\n{}", result1);
        assert!(result1.contains("#goto(paragraph=\"main\")"));

        // 测试空格分隔语法保留
        let input2 = r#"
::test {
    #goto paragraph="main"
}
"#;
        let cst2 = parse_tolerant("test", input2);
        let result2 = formatter.format(&cst2);

        println!("Space-separated systemcall result:\n{}", result2);
        assert!(result2.contains("#goto paragraph=\"main\""));
    }

    /// 辅助函数：格式化 N 次，确保结果稳定（幂等性）
    fn format_n_times(input: &str, n: usize) -> Vec<String> {
        let formatter = CstFormatter::new();
        let mut results = Vec::new();
        let mut current = input.to_string();
        for _ in 0..n {
            let cst = parse_tolerant("test", &current);
            current = formatter.format(&cst);
            results.push(current.clone());
        }
        results
    }

    #[test]
    fn test_format_hash_multiline_idempotent() {
        let input = "::main {\n    ##\n    let x = 1;\n    let y = 2;\n    ##\n}\n";
        let results = format_n_times(input, 5);

        // 第一次格式化后的结果应和后续每次相同
        for (i, result) in results.iter().enumerate().skip(1) {
            assert_eq!(
                &results[0],
                result,
                "## 多行脚本格式化不幂等：第 1 次和第 {} 次结果不同\n第 1 次:\n{}\n第 {} 次:\n{}",
                i + 1,
                &results[0],
                i + 1,
                result
            );
        }
    }

    #[test]
    fn test_format_hash_multiline_no_indent_idempotent() {
        // 代码内容无缩进的情况
        let input = "::main {\n##\nconst y = \"hello\";\nconsole.log(y);\n##\n}\n";
        let results = format_n_times(input, 5);

        for (i, result) in results.iter().enumerate().skip(1) {
            assert_eq!(
                &results[0],
                result,
                "## 无缩进脚本块格式化不幂等：第 1 次和第 {} 次结果不同",
                i + 1
            );
        }
    }

    #[test]
    fn test_format_block_comment_with_stars_idempotent() {
        let input = "::main {\n    /*\n     * line 1\n     * line 2\n     */\n    @cmd arg=1\n}\n";
        let results = format_n_times(input, 5);

        for (i, result) in results.iter().enumerate().skip(1) {
            assert_eq!(
                &results[0], result,
                "带 * 的多行注释格式化不幂等：第 1 次和第 {} 次结果不同\n第 1 次:\n{}\n第 {} 次:\n{}",
                i + 1, &results[0], i + 1, result
            );
        }
    }

    #[test]
    fn test_format_block_comment_without_stars_idempotent() {
        let input = "::main {\n    /*\n     line 1\n     line 2\n     */\n    @cmd arg=1\n}\n";
        let results = format_n_times(input, 5);

        for (i, result) in results.iter().enumerate().skip(1) {
            assert_eq!(
                &results[0], result,
                "不带 * 的多行注释格式化不幂等：第 1 次和第 {} 次结果不同\n第 1 次:\n{}\n第 {} 次:\n{}",
                i + 1, &results[0], i + 1, result
            );
        }
    }

    #[test]
    fn test_format_block_comment_inline_multiline_idempotent() {
        // /* 多行注释\n   第二行 */ 这种内联多行注释
        let input = "::test {\n    /* 多行注释\n       第二行 */\n    @cmd arg=1\n}\n";
        let results = format_n_times(input, 5);

        for (i, result) in results.iter().enumerate().skip(1) {
            assert_eq!(
                &results[0],
                result,
                "内联多行注释格式化不幂等：第 1 次和第 {} 次结果不同\n第 1 次:\n{}\n第 {} 次:\n{}",
                i + 1,
                &results[0],
                i + 1,
                result
            );
        }
    }

    #[test]
    fn test_format_block_comment_empty_lines_idempotent() {
        // 多行注释中有空行
        let input = "::test {\n    /*\n     * line 1\n     *\n     * line 2\n     */\n}\n";
        let results = format_n_times(input, 5);

        for (i, result) in results.iter().enumerate().skip(1) {
            assert_eq!(
                &results[0], result,
                "含空行的多行注释格式化不幂等：第 1 次和第 {} 次结果不同\n第 1 次:\n{}\n第 {} 次:\n{}",
                i + 1, &results[0], i + 1, result
            );
        }
        // 验证空行被保留
        assert!(results[0].contains(" *\n"), "多行注释中的空行应被保留");
    }

    #[test]
    fn test_format_mixed_all_idempotent() {
        // 综合测试：同时包含 ## 代码块和多行注释
        let input = r#"::main {
    /*
     * 这是注释
     * 第二行
     */
    ##
    let x = 1;
    let y = 2;
    ##
    @cmd arg=1
}
"#;
        let results = format_n_times(input, 5);

        for (i, result) in results.iter().enumerate().skip(1) {
            assert_eq!(
                &results[0],
                result,
                "综合格式化不幂等：第 1 次和第 {} 次结果不同\n第 1 次:\n{}\n第 {} 次:\n{}",
                i + 1,
                &results[0],
                i + 1,
                result
            );
        }
    }

    #[test]
    fn test_format_brace_multiline_idempotent() {
        // 测试 @{...} 多行代码块格式化幂等性
        let input = "::code_test {\n    @{\n  const y = \"hello\";\n  console.log(y);\n    }\n}\n";
        let results = format_n_times(input, 5);

        for (i, result) in results.iter().enumerate().skip(1) {
            assert_eq!(
                &results[0],
                result,
                "@{{...}} 多行代码块格式化不幂等：第 1 次和第 {} 次结果不同\n第 1 次:\n{}\n第 {} 次:\n{}",
                i + 1,
                &results[0],
                i + 1,
                result
            );
        }
    }
}
