mod array_of_table;
mod comment;
mod key;
mod key_value;
mod root;
mod table;
mod value;

use std::fmt::Write;

use itertools::Itertools;
use tombi_ast::{AstChildren, AstNode};
use tombi_config::TomlVersion;
use tombi_syntax::SyntaxKind::{LINE_BREAK, WHITESPACE};

use crate::types::AlignmentWidth;

pub trait Format {
    fn format(&self, f: &mut crate::Formatter) -> Result<(), std::fmt::Error>;
}

fn write_trailing_comment_alignment_space(
    f: &mut crate::Formatter,
    trailing_comment_alignment_width: AlignmentWidth,
) -> Result<(), std::fmt::Error> {
    let spaces =
        (trailing_comment_alignment_width.value() as usize).saturating_sub(f.current_line_width());
    write!(f, "{}", " ".repeat(spaces))?;
    Ok(())
}

fn filter_map_unique_keys<'a>(
    header_keys: AstChildren<tombi_ast::Key>,
    parent_header_keys: impl Iterator<Item = AstChildren<tombi_ast::Key>> + 'a,
    toml_version: TomlVersion,
) -> impl Iterator<Item = Vec<String>> + 'a {
    parent_header_keys
        .filter(move |keys| keys.clone().count() < header_keys.clone().count())
        .map(move |keys| keys.map(|key| key.to_raw_text(toml_version)).collect_vec())
        .unique()
}

pub(crate) fn has_empty_line_before<T: AstNode>(node: &T) -> bool {
    fn previous_non_whitespace(
        mut element: tombi_syntax::SyntaxElement,
    ) -> Option<tombi_syntax::SyntaxElement> {
        loop {
            if element.kind() != WHITESPACE {
                return Some(element);
            }
            element = element.prev_sibling_or_token()?;
        }
    }

    let Some(prev) = node
        .syntax()
        .prev_sibling_or_token()
        .and_then(previous_non_whitespace)
    else {
        return false;
    };
    if prev.kind() != LINE_BREAK {
        return false;
    }
    let Some(prev_prev) = prev
        .prev_sibling_or_token()
        .and_then(previous_non_whitespace)
    else {
        return false;
    };
    prev_prev.kind() == LINE_BREAK
}
