//! Version Control Utilities
//!
//! Helper functions and utilities for version control operations

use crate::content::file_loader::FrontMatter;

/// Extract version from frontmatter or generate new one
pub fn get_or_create_version(frontmatter: &FrontMatter, _content_path: &str) -> String {
    frontmatter
        .version
        .clone()
        .unwrap_or_else(|| "1.0.0".to_string())
}

/// Check if content should be auto-versioned
pub fn should_auto_version(frontmatter: &FrontMatter) -> bool {
    // Auto-version if no version exists or if explicitly enabled
    frontmatter.version.is_none()
        || frontmatter
            .metadata
            .as_ref()
            .and_then(|m| m.get("auto_version"))
            .map(|v| v == "true")
            .unwrap_or(true)
}

/// Create version notes from frontmatter changes
pub fn create_version_notes(
    old_frontmatter: &FrontMatter,
    new_frontmatter: &FrontMatter,
) -> String {
    let mut changes = Vec::new();

    if old_frontmatter.title != new_frontmatter.title {
        changes.push("Title updated");
    }
    if old_frontmatter.tags != new_frontmatter.tags {
        changes.push("Tags modified");
    }
    if old_frontmatter.category != new_frontmatter.category {
        changes.push("Category changed");
    }

    if changes.is_empty() {
        "Content updated".to_string()
    } else {
        changes.join(", ")
    }
}

/// Parse semantic version string into components
pub fn parse_semantic_version(version: &str) -> Result<(u32, u32, u32), String> {
    let parts: Vec<&str> = version.split('.').collect();
    if parts.len() != 3 {
        return Err("Invalid semantic version format".to_string());
    }

    let major = parts[0]
        .parse::<u32>()
        .map_err(|_| "Invalid major version")?;
    let minor = parts[1]
        .parse::<u32>()
        .map_err(|_| "Invalid minor version")?;
    let patch = parts[2]
        .parse::<u32>()
        .map_err(|_| "Invalid patch version")?;

    Ok((major, minor, patch))
}

/// Generate next version string with specified increment type
pub enum VersionIncrement {
    Patch,
    Minor,
    Major,
}

pub fn increment_version(
    current_version: &str,
    increment_type: VersionIncrement,
) -> Result<String, String> {
    let (major, minor, patch) = parse_semantic_version(current_version)?;

    let (new_major, new_minor, new_patch) = match increment_type {
        VersionIncrement::Patch => (major, minor, patch + 1),
        VersionIncrement::Minor => (major, minor + 1, 0),
        VersionIncrement::Major => (major + 1, 0, 0),
    };

    Ok(format!("{}.{}.{}", new_major, new_minor, new_patch))
}

/// Compare two version strings
pub fn compare_versions(version1: &str, version2: &str) -> Result<std::cmp::Ordering, String> {
    let (major1, minor1, patch1) = parse_semantic_version(version1)?;
    let (major2, minor2, patch2) = parse_semantic_version(version2)?;

    match major1.cmp(&major2) {
        std::cmp::Ordering::Equal => match minor1.cmp(&minor2) {
            std::cmp::Ordering::Equal => Ok(patch1.cmp(&patch2)),
            other => Ok(other),
        },
        other => Ok(other),
    }
}

/// Validate version string format
pub fn validate_version_format(version: &str) -> bool {
    parse_semantic_version(version).is_ok()
}

/// Generate a unique version identifier (for complex scenarios)
pub fn generate_unique_version_id() -> String {
    use chrono::Utc;
    format!("v{}", Utc::now().timestamp())
}

/// Clean version string (remove 'v' prefix if present)
pub fn normalize_version_string(version: &str) -> String {
    version.strip_prefix('v').unwrap_or(version).to_string()
}

/// Format version for display
pub fn format_version_display(version: &str, include_prefix: bool) -> String {
    let clean_version = normalize_version_string(version);
    if include_prefix {
        format!("v{}", clean_version)
    } else {
        clean_version
    }
}

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

    #[test]
    fn test_parse_semantic_version() {
        assert_eq!(parse_semantic_version("1.0.0"), Ok((1, 0, 0)));
        assert_eq!(parse_semantic_version("2.5.10"), Ok((2, 5, 10)));
        assert!(parse_semantic_version("1.0").is_err());
        assert!(parse_semantic_version("1.0.a").is_err());
    }

    #[test]
    fn test_increment_version() {
        assert_eq!(
            increment_version("1.0.0", VersionIncrement::Patch),
            Ok("1.0.1".to_string())
        );
        assert_eq!(
            increment_version("1.0.5", VersionIncrement::Minor),
            Ok("1.1.0".to_string())
        );
        assert_eq!(
            increment_version("2.3.7", VersionIncrement::Major),
            Ok("3.0.0".to_string())
        );
    }

    #[test]
    fn test_compare_versions() {
        use std::cmp::Ordering;
        assert_eq!(compare_versions("1.0.0", "1.0.0"), Ok(Ordering::Equal));
        assert_eq!(compare_versions("1.0.0", "1.0.1"), Ok(Ordering::Less));
        assert_eq!(compare_versions("2.0.0", "1.9.9"), Ok(Ordering::Greater));
    }

    #[test]
    fn test_normalize_version_string() {
        assert_eq!(normalize_version_string("v1.0.0"), "1.0.0");
        assert_eq!(normalize_version_string("1.0.0"), "1.0.0");
    }

    #[test]
    fn test_format_version_display() {
        assert_eq!(format_version_display("1.0.0", true), "v1.0.0");
        assert_eq!(format_version_display("v1.0.0", false), "1.0.0");
    }
}
