// Word boundary detection for case transformation
//
// This module detects word boundaries in placeholder values based on casing
// transitions, separators, and other conventions to split values into individual
// words for transformation.

use crate::case_transform::CasingConvention;

/// Detect word boundaries and split a value into individual words
/// Based on the source casing convention
pub fn detect_word_boundaries(value: &str, convention: CasingConvention) -> Vec<String> {
    match convention {
        CasingConvention::PascalCase => split_pascal_case(value),
        CasingConvention::CamelCase => split_camel_case(value),
        CasingConvention::LowerSnakeCase | CasingConvention::ScreamingSnakeCase => {
            split_snake_case(value)
        }
        CasingConvention::KebabCase => split_kebab_case(value),
        CasingConvention::Uppercase | CasingConvention::Lowercase | CasingConvention::Fallback => {
            // Single word, no boundaries
            vec![value.to_string()]
        }
    }
}

/// Split PascalCase into words
/// Examples: ByItem → ["By", "Item"], XMLParser → ["X", "M", "L", "Parser"]
fn split_pascal_case(value: &str) -> Vec<String> {
    let mut words = Vec::new();
    let mut current_word = String::new();
    let mut chars = value.chars().peekable();

    while let Some(ch) = chars.next() {
        if ch.is_uppercase() {
            // Check if this is part of a sequence of uppercase letters (acronym)
            if let Some(&next_ch) = chars.peek() {
                if next_ch.is_uppercase() {
                    // Consecutive uppercase - each letter is a separate word
                    if !current_word.is_empty() {
                        words.push(current_word.clone());
                        current_word.clear();
                    }
                    current_word.push(ch);
                } else if next_ch.is_lowercase() {
                    // Uppercase followed by lowercase - start of new word
                    if !current_word.is_empty() {
                        words.push(current_word.clone());
                        current_word.clear();
                    }
                    current_word.push(ch);
                } else {
                    // Uppercase followed by non-letter (number)
                    current_word.push(ch);
                }
            } else {
                // Last character
                if !current_word.is_empty() {
                    words.push(current_word.clone());
                    current_word.clear();
                }
                current_word.push(ch);
            }
        } else {
            // Lowercase or number - continue current word
            current_word.push(ch);
        }
    }

    if !current_word.is_empty() {
        words.push(current_word);
    }

    words
}

/// Split camelCase into words
/// Examples: byItem → ["by", "Item"], userId → ["user", "Id"]
fn split_camel_case(value: &str) -> Vec<String> {
    let mut words = Vec::new();
    let mut current_word = String::new();

    for ch in value.chars() {
        if ch.is_uppercase() {
            // Start of new word
            if !current_word.is_empty() {
                words.push(current_word.clone());
                current_word.clear();
            }
            current_word.push(ch);
        } else {
            // Continue current word
            current_word.push(ch);
        }
    }

    if !current_word.is_empty() {
        words.push(current_word);
    }

    words
}

/// Split snake_case into words
/// Examples: by_item → ["by", "item"], user_email → ["user", "email"]
fn split_snake_case(value: &str) -> Vec<String> {
    value.split('_').map(|s| s.to_string()).collect()
}

/// Split kebab-case into words
/// Examples: by-item → ["by", "item"], order-processor → ["order", "processor"]
fn split_kebab_case(value: &str) -> Vec<String> {
    value.split('-').map(|s| s.to_string()).collect()
}

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

    #[test]
    fn test_split_pascal_case_simple() {
        assert_eq!(split_pascal_case("ByItem"), vec!["By", "Item"]);
        assert_eq!(split_pascal_case("UserEmail"), vec!["User", "Email"]);
        assert_eq!(split_pascal_case("Item"), vec!["Item"]);
    }

    #[test]
    fn test_split_pascal_case_acronym() {
        // XMLParser → ["X", "M", "L", "Parser"]
        assert_eq!(
            split_pascal_case("XMLParser"),
            vec!["X", "M", "L", "Parser"]
        );
        assert_eq!(split_pascal_case("HELLO"), vec!["H", "E", "L", "L", "O"]);
    }

    #[test]
    fn test_split_pascal_case_single_letter() {
        // ATrain → ["A", "Train"]
        assert_eq!(split_pascal_case("ATrain"), vec!["A", "Train"]);
    }

    #[test]
    fn test_split_pascal_case_with_numbers() {
        // Item2 → ["Item2"]
        assert_eq!(split_pascal_case("Item2"), vec!["Item2"]);
        // ByItem2 → ["By", "Item2"]
        assert_eq!(split_pascal_case("ByItem2"), vec!["By", "Item2"]);
    }

    #[test]
    fn test_split_camel_case() {
        assert_eq!(split_camel_case("byItem"), vec!["by", "Item"]);
        assert_eq!(split_camel_case("userEmail"), vec!["user", "Email"]);
    }

    #[test]
    fn test_split_snake_case() {
        assert_eq!(split_snake_case("by_item"), vec!["by", "item"]);
        assert_eq!(split_snake_case("user_email"), vec!["user", "email"]);
    }

    #[test]
    fn test_split_kebab_case() {
        assert_eq!(split_kebab_case("by-item"), vec!["by", "item"]);
        assert_eq!(
            split_kebab_case("order-processor"),
            vec!["order", "processor"]
        );
    }

    #[test]
    fn test_detect_word_boundaries_pascal() {
        let words = detect_word_boundaries("ByItem", CasingConvention::PascalCase);
        assert_eq!(words, vec!["By", "Item"]);
    }

    #[test]
    fn test_detect_word_boundaries_camel() {
        let words = detect_word_boundaries("byItem", CasingConvention::CamelCase);
        assert_eq!(words, vec!["by", "Item"]);
    }

    #[test]
    fn test_detect_word_boundaries_snake() {
        let words = detect_word_boundaries("user_email", CasingConvention::LowerSnakeCase);
        assert_eq!(words, vec!["user", "email"]);
    }

    #[test]
    fn test_detect_word_boundaries_kebab() {
        let words = detect_word_boundaries("order-processor", CasingConvention::KebabCase);
        assert_eq!(words, vec!["order", "processor"]);
    }

    #[test]
    fn test_detect_word_boundaries_single_word() {
        // Lowercase and Uppercase conventions treat as single word
        let words = detect_word_boundaries("byitem", CasingConvention::Lowercase);
        assert_eq!(words, vec!["byitem"]);

        let words = detect_word_boundaries("BYITEM", CasingConvention::Uppercase);
        assert_eq!(words, vec!["BYITEM"]);
    }
}
