use chrono::{DateTime, Local, Utc};
use regex::Regex;

/// Format commit message with conventional commit format
pub fn format_conventional_commit(
    commit_type: &str,
    scope: Option<&str>,
    description: &str,
    body: Option<&str>,
    footer: Option<&str>,
    breaking: bool,
) -> String {
    let mut message = String::new();
    
    // Type and scope
    message.push_str(commit_type);
    if let Some(s) = scope {
        message.push_str(&format!("({})", s));
    }
    if breaking {
        message.push('!');
    }
    message.push_str(&format!(": {}", description));
    
    // Body
    if let Some(b) = body {
        message.push_str(&format!("\n\n{}", b));
    }
    
    // Footer
    if let Some(f) = footer {
        message.push_str(&format!("\n\n{}", f));
    }
    
    message
}

/// Format commit with @commitlint format
pub fn format_commitlint_commit(
    commit_type: &str,
    scope: Option<&str>,
    subject: &str,
    body: Option<&str>,
    footer: Option<&str>,
    references: Option<&[&str]>,
) -> String {
    let mut message = String::new();
    
    // Header
    message.push_str(commit_type);
    if let Some(s) = scope {
        message.push_str(&format!("({})", s));
    }
    message.push_str(&format!(": {}", subject));
    
    // References
    if let Some(refs) = references {
        for reference in refs {
            message.push_str(&format!(" #{}", reference));
        }
    }
    
    // Body
    if let Some(b) = body {
        message.push_str(&format!("\n\n{}", b));
    }
    
    // Footer
    if let Some(f) = footer {
        message.push_str(&format!("\n\n{}", f));
    }
    
    message
}

/// Format date for commit message
pub fn format_commit_date(date: &DateTime<Local>) -> String {
    date.format("%Y-%m-%d %H:%M:%S").to_string()
}

/// Format date for changelog
pub fn format_changelog_date(date: &DateTime<Utc>) -> String {
    date.format("%Y-%m-%d").to_string()
}

/// Format tag name with version
pub fn format_tag_name(version: &str, prefix: Option<&str>) -> String {
    match prefix {
        Some(p) => format!("{}{}", p, version),
        None => version.to_string(),
    }
}

/// Wrap text at specified width
pub fn wrap_text(text: &str, width: usize) -> String {
    textwrap::fill(text, width)
}

/// Truncate text with ellipsis
pub fn truncate(text: &str, max_len: usize) -> String {
    if text.len() <= max_len {
        text.to_string()
    } else {
        format!("{}...", &text[..max_len.saturating_sub(3)])
    }
}

/// Clean commit message (remove comments, extra whitespace)
pub fn clean_message(message: &str) -> String {
    let comment_regex = Regex::new(r"^#.*$").unwrap();
    
    message
        .lines()
        .filter(|line| !comment_regex.is_match(line.trim()))
        .collect::<Vec<_>>()
        .join("\n")
        .trim()
        .to_string()
}

/// Format list as markdown bullet points
pub fn format_markdown_list(items: &[String]) -> String {
    items
        .iter()
        .map(|item| format!("- {}", item))
        .collect::<Vec<_>>()
        .join("\n")
}

/// Format changelog section
pub fn format_changelog_section(
    version: &str,
    date: &str,
    changes: &[(String, Vec<String>)],
) -> String {
    let mut section = format!("## [{}] - {}\n\n", version, date);
    
    for (category, items) in changes {
        if !items.is_empty() {
            section.push_str(&format!("### {}\n\n", category));
            for item in items {
                section.push_str(&format!("- {}\n", item));
            }
            section.push('\n');
        }
    }
    
    section
}

/// Format git config key
pub fn format_git_config_key(section: &str, subsection: Option<&str>, key: &str) -> String {
    match subsection {
        Some(sub) => format!("{}.{}.{}", section, sub, key),
        None => format!("{}.{}", section, key),
    }
}

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

    #[test]
    fn test_format_conventional_commit() {
        let msg = format_conventional_commit(
            "feat",
            Some("auth"),
            "add login functionality",
            Some("This adds OAuth2 login support."),
            Some("Closes #123"),
            false,
        );
        
        assert!(msg.contains("feat(auth): add login functionality"));
        assert!(msg.contains("This adds OAuth2 login support."));
        assert!(msg.contains("Closes #123"));
    }

    #[test]
    fn test_format_conventional_commit_breaking() {
        let msg = format_conventional_commit(
            "feat",
            None,
            "change API response format",
            None,
            Some("BREAKING CHANGE: response format changed"),
            true,
        );
        
        assert!(msg.starts_with("feat!: change API response format"));
    }

    #[test]
    fn test_truncate() {
        assert_eq!(truncate("hello", 10), "hello");
        assert_eq!(truncate("hello world", 8), "hello...");
    }
}
