//! Document formatting provider
//!
//! Provides auto-formatting for Storybook files

use tower_lsp::lsp_types::{
    FormattingOptions,
    Position,
    Range,
    TextEdit,
};

use super::document::Document;

/// Format the entire document
pub fn format_document(doc: &Document, _options: &FormattingOptions) -> Option<Vec<TextEdit>> {
    // Don't format if there are parse errors - the AST would be invalid
    // and formatting could produce garbage output
    doc.ast.as_ref()?;

    // For now, implement basic formatting rules:
    // 1. 4-space indentation
    // 2. Consistent spacing around colons
    // 3. Blank lines between top-level declarations

    let formatted = format_text(&doc.text);

    if formatted == doc.text {
        return None; // No changes needed
    }

    // Return a single edit that replaces the entire document
    Some(vec![TextEdit {
        range: Range {
            start: Position {
                line: 0,
                character: 0,
            },
            end: Position {
                line: doc.positions.line_count() as u32,
                character: 0,
            },
        },
        new_text: formatted,
    }])
}

/// Format the text according to Storybook style rules
pub(crate) fn format_text(text: &str) -> String {
    let mut result = String::new();
    let mut indent_level = 0;
    let mut prev_was_blank = false;
    let mut in_prose_block = false;

    for line in text.lines() {
        let trimmed = line.trim();

        // Handle prose blocks - don't format their content
        if trimmed.starts_with("---") {
            in_prose_block = !in_prose_block;
            result.push_str(&"    ".repeat(indent_level));
            result.push_str(trimmed);
            result.push('\n');
            continue;
        }

        if in_prose_block {
            // Preserve prose content exactly as-is
            result.push_str(line);
            result.push('\n');
            continue;
        }

        // Skip blank lines
        if trimmed.is_empty() {
            if !prev_was_blank {
                result.push('\n');
                prev_was_blank = true;
            }
            continue;
        }

        prev_was_blank = false;

        // Adjust indentation based on braces
        if trimmed.starts_with('}') {
            indent_level = indent_level.saturating_sub(1);
        }

        // Add indentation
        result.push_str(&"    ".repeat(indent_level));

        // Format the line content
        let formatted_line = format_line(trimmed);
        result.push_str(&formatted_line);
        result.push('\n');

        // Increase indentation for opening braces
        if trimmed.ends_with('{') {
            indent_level += 1;
        }
    }

    result
}

/// Format a single line
fn format_line(line: &str) -> String {
    // Ensure consistent spacing around colons in field assignments
    if let Some(colon_pos) = line.find(':') {
        if !line.starts_with("//") && !line.contains("::") {
            let before = line[..colon_pos].trim_end();
            let after = line[colon_pos + 1..].trim_start();
            return format!("{}: {}", before, after);
        }
    }

    line.to_string()
}

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

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

        // Check key formatting features
        assert!(
            formatted.contains("age: 25"),
            "Should have space after colon"
        );
        assert!(
            formatted.contains("character Alice"),
            "Should have character declaration"
        );
    }

    #[test]
    fn test_preserve_prose() {
        let input = "---backstory\nSome  irregular   spacing\n---";
        let formatted = format_text(input);
        assert!(formatted.contains("Some  irregular   spacing"));
    }
}
