//! Session Title Derivation Utilities
//!
//! This module provides utilities for deriving session titles from user messages.

/// Maximum length for derived titles
pub const MAX_TITLE_LENGTH: usize = 50;

/// Derive a session title from user message text
///
/// Takes the first 50 characters, trimming whitespace and trying to break at word boundaries.
/// Sanitizes by removing newlines and control characters.
///
/// # Arguments
/// * `text` - The user message text to derive title from
///
/// # Returns
/// Derived title (max 50 chars, trimmed, sanitized)
pub fn derive_title_from_text(text: &str) -> String {
    // Sanitize: replace newlines and control chars with space
    let sanitized = text
        .chars()
        .map(|c| if c.is_control() { ' ' } else { c })
        .collect::<String>();

    // Trim whitespace
    let trimmed = sanitized.trim();

    // Handle empty case
    if trimmed.is_empty() {
        return "Untitled Session".to_string();
    }

    // If text is short enough, return as-is
    if trimmed.len() <= MAX_TITLE_LENGTH {
        return trimmed.to_string();
    }

    // Text is longer than MAX_TITLE_LENGTH - truncate at a valid char boundary
    let end = trimmed.floor_char_boundary(MAX_TITLE_LENGTH);
    let truncated = &trimmed[..end];

    // Try to break at word boundary within the truncated text
    if let Some(last_space) = truncated.rfind(' ') {
        // Break at word boundary
        trimmed[..last_space].trim_end().to_string()
    } else {
        // No spaces found - just truncate
        truncated.trim_end().to_string()
    }
}

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

    #[test]
    fn test_derive_title_from_text_short() {
        let text = "Hello world";
        let title = derive_title_from_text(text);
        assert_eq!(title, "Hello world");
    }

    #[test]
    fn test_derive_title_from_text_long_with_word_boundary() {
        let text = "This is a very long message that should be truncated at word boundary";
        let title = derive_title_from_text(text);
        assert!(title.len() <= 50);
        assert!(title.starts_with("This is a very long message"));
        // Should break at word boundary - the exact result depends on word positions
        // Just verify it doesn't exceed 50 chars and doesn't end mid-word
        assert!(!title.ends_with("at")); // Shouldn't break "at word"
                                         // The title should end on a complete word
        let last_char = title.chars().last().unwrap();
        assert!(last_char.is_alphanumeric() || last_char == ' ');
    }

    #[test]
    fn test_derive_title_from_text_long_no_spaces() {
        let text = "Thisisaverylongmessagewithoutanyspacesthatcannotbetruncatedatwordboundary";
        let title = derive_title_from_text(text);
        assert_eq!(title.len(), 50);
        // Without spaces, it will just truncate at 50 chars
        assert_eq!(title, &text[..50]);
    }

    #[test]
    fn test_derive_title_from_text_with_newlines() {
        let text = "First line\nSecond line\nThird line";
        let title = derive_title_from_text(text);
        // Newlines should be replaced with spaces
        assert!(!title.contains('\n'));
        assert!(title.contains("First line Second line"));
    }

    #[test]
    fn test_derive_title_from_text_empty() {
        let text = "";
        let title = derive_title_from_text(text);
        assert_eq!(title, "Untitled Session");
    }

    #[test]
    fn test_derive_title_from_text_whitespace_only() {
        let text = "   \n\t  ";
        let title = derive_title_from_text(text);
        assert_eq!(title, "Untitled Session");
    }

    #[test]
    fn test_derive_title_from_text_with_control_chars() {
        let text = "Hello\x00world\x01test\x02";
        let title = derive_title_from_text(text);
        // Control chars should be replaced with spaces
        assert!(!title.chars().any(|c| c.is_control() && c != ' '));
        assert_eq!(title, "Hello world test");
    }

    #[test]
    fn test_derive_title_exact_max_length() {
        // Create a string of exactly MAX_TITLE_LENGTH characters
        let text = "a".repeat(MAX_TITLE_LENGTH);
        let title = derive_title_from_text(&text);
        assert_eq!(title.len(), MAX_TITLE_LENGTH);
        assert_eq!(title, text);
    }

    #[test]
    fn test_derive_title_one_over_max_length() {
        // Create a string just over MAX_TITLE_LENGTH
        let text = "a".repeat(MAX_TITLE_LENGTH + 1);
        let title = derive_title_from_text(&text);
        assert_eq!(title.len(), MAX_TITLE_LENGTH);
    }
}
