//! Tests for LSP document formatting functionality
//!
//! This module tests auto-formatting for:
//! - Spacing normalization around colons
//! - Indentation preservation
//! - Prose block preservation
//! - Edge cases (empty documents, already formatted)

use tower_lsp::lsp_types::FormattingOptions;

use super::{
    document::Document,
    formatting::{
        format_document,
        format_text,
    },
};

#[test]
fn test_format_adds_space_after_colon() {
    let input = "character Alice{age:7}";
    let formatted = format_text(input);

    // Main formatting: adds space after colon
    assert!(formatted.contains("age: 7"));
}

#[test]
fn test_format_normalizes_colon_spacing() {
    let input = "character Alice{age   :   7}";
    let formatted = format_text(input);

    // Should normalize to single space after colon
    assert!(formatted.contains("age: 7"));
    assert!(!formatted.contains("age   :"));
}

#[test]
fn test_format_multiple_fields() {
    let input = "character Alice{age:7\nname:\"Alice\"}";
    let formatted = format_text(input);

    assert!(formatted.contains("age: 7"));
    assert!(formatted.contains("name: \"Alice\""));
}

#[test]
fn test_format_preserves_module_paths() {
    let input = "use characters::Alice;";
    let formatted = format_text(input);

    // Should NOT add space in :: paths
    assert!(formatted.contains("characters::Alice"));
    assert!(!formatted.contains("characters: :Alice"));
}

#[test]
fn test_format_preserves_prose_blocks() {
    let input = r#"character Alice{
---backstory
Some  irregular   spacing  here
---
age:7}"#;
    let formatted = format_text(input);

    // Prose content preserved exactly
    assert!(formatted.contains("Some  irregular   spacing  here"));
    // But other fields still formatted
    assert!(formatted.contains("age: 7"));
}

#[test]
fn test_format_indentation_with_braces() {
    let input = "character Alice{\nage:7\n}";
    let formatted = format_text(input);

    // Check indentation (4 spaces)
    assert!(formatted.contains("character Alice{"));
    assert!(formatted.contains("    age: 7"));
    assert!(formatted.contains("}"));
}

#[test]
fn test_format_nested_indentation() {
    let input = "template Person{\nnested:{\nvalue:123\n}\n}";
    let formatted = format_text(input);

    assert!(formatted.contains("    nested: {"));
    assert!(formatted.contains("        value: 123"));
    assert!(formatted.contains("    }"));
}

#[test]
fn test_format_empty_document() {
    let input = "";
    let formatted = format_text(input);

    assert_eq!(formatted, "");
}

#[test]
fn test_format_whitespace_only() {
    let input = "   \n  \n   ";
    let formatted = format_text(input);

    // Should collapse to single newline or empty
    assert!(formatted.len() <= 2);
}

#[test]
fn test_format_already_formatted() {
    let input = "character Alice{\n    age: 7\n}";
    let formatted = format_text(input);

    // Should remain essentially the same
    assert!(formatted.contains("age: 7"));
}

#[test]
fn test_format_removes_multiple_blank_lines() {
    let input = "character Alice{age:7}\n\n\n\ncharacter Bob{age:10}";
    let formatted = format_text(input);

    // Should consolidate multiple blank lines
    let double_newlines = formatted.matches("\n\n\n").count();
    assert_eq!(double_newlines, 0, "Should not have triple newlines");
}

#[test]
fn test_format_comments_unchanged() {
    let input = "// Comment\ncharacter Alice{age:7}";
    let formatted = format_text(input);

    assert!(formatted.contains("// Comment"));
}

#[test]
fn test_format_document_returns_text_edit() {
    let input = "character Alice{age:7}";
    let doc = Document::new(input.to_string());

    let result = format_document(&doc, &FormattingOptions::default());

    if doc.ast.is_some() {
        assert!(
            result.is_some(),
            "Should return TextEdit for valid document"
        );
    }
}

#[test]
fn test_format_document_no_changes_returns_none() {
    let input = "character Alice{\n    age: 7\n}\n";
    let doc = Document::new(input.to_string());

    let result = format_document(&doc, &FormattingOptions::default());

    // If document is already formatted, may return None
    // (depends on exact formatting match)
    if result.is_none() {
        assert!(
            doc.ast.is_some(),
            "Should only return None if formatting matches"
        );
    }
}
