//! Version Control Data Models
//!
//! Shared data structures used across different version control backends

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};

/// Represents a single version of content
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContentVersion {
    pub version: String,
    pub content_path: String,
    pub created_at: DateTime<Utc>,
    pub author: Option<String>,
    pub version_notes: Option<String>,
    pub parent_version: Option<String>,
    pub checksum: String,
    pub size_bytes: u64,
    pub metadata: std::collections::HashMap<String, String>,
}

impl ContentVersion {
    /// Create a new content version
    pub fn new(
        version: String,
        content_path: String,
        author: Option<String>,
        version_notes: Option<String>,
        parent_version: Option<String>,
        checksum: String,
        size_bytes: u64,
    ) -> Self {
        Self {
            version,
            content_path,
            created_at: Utc::now(),
            author,
            version_notes,
            parent_version,
            checksum,
            size_bytes,
            metadata: std::collections::HashMap::new(),
        }
    }

    /// Add metadata to this version
    pub fn with_metadata(mut self, key: String, value: String) -> Self {
        self.metadata.insert(key, value);
        self
    }

    /// Check if this version has changed from another version
    pub fn has_changes_from(&self, other: &ContentVersion) -> bool {
        self.checksum != other.checksum
    }
}

/// Complete version history for a piece of content
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VersionHistory {
    pub content_id: String,
    pub current_version: String,
    pub versions: Vec<ContentVersion>,
    pub total_versions: usize,
    pub created_at: DateTime<Utc>,
    pub last_modified: DateTime<Utc>,
}

impl VersionHistory {
    /// Create new version history
    pub fn new(content_id: String) -> Self {
        let now = Utc::now();
        Self {
            content_id,
            current_version: "1.0.0".to_string(),
            versions: Vec::new(),
            total_versions: 0,
            created_at: now,
            last_modified: now,
        }
    }

    /// Add a new version to the history
    pub fn add_version(&mut self, version: ContentVersion) {
        self.current_version = version.version.clone();
        self.versions.push(version);
        self.total_versions = self.versions.len();
        self.last_modified = Utc::now();

        // Keep versions sorted by creation date
        self.versions
            .sort_by(|a, b| a.created_at.cmp(&b.created_at));
    }

    /// Get the latest version
    pub fn get_latest_version(&self) -> Option<&ContentVersion> {
        self.versions.last()
    }

    /// Get a specific version by version string
    pub fn get_version(&self, version: &str) -> Option<&ContentVersion> {
        self.versions.iter().find(|v| v.version == version)
    }

    /// Get version by index (0 = oldest, -1 = newest)
    pub fn get_version_by_index(&self, index: i32) -> Option<&ContentVersion> {
        if index < 0 {
            let pos = (self.versions.len() as i32 + index) as usize;
            self.versions.get(pos)
        } else {
            self.versions.get(index as usize)
        }
    }

    /// Get all version identifiers
    pub fn get_version_list(&self) -> Vec<String> {
        self.versions.iter().map(|v| v.version.clone()).collect()
    }
}

/// Represents the differences between two versions of content
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VersionDiff {
    pub from_version: String,
    pub to_version: String,
    pub diff_type: DiffType,
    pub changes: Vec<DiffChange>,
    pub statistics: DiffStatistics,
    pub created_at: DateTime<Utc>,
}

/// Type of diff operation
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum DiffType {
    LineBased,
    WordBased,
    CharacterBased,
    Semantic,
}

/// Represents a single change in a diff
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DiffChange {
    pub change_type: ChangeType,
    pub line_number: Option<usize>,
    pub content: String,
    pub context: Option<String>,
}

/// Type of change in a diff
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ChangeType {
    Addition,
    Deletion,
    Modification,
    Context,
}

/// Statistics about a diff
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DiffStatistics {
    pub lines_added: usize,
    pub lines_removed: usize,
    pub lines_modified: usize,
    pub total_changes: usize,
    pub similarity_percentage: f64,
}

impl DiffStatistics {
    /// Create new diff statistics
    pub fn new(added: usize, removed: usize, modified: usize) -> Self {
        let total_changes = added + removed + modified;
        let similarity = if total_changes > 0 {
            // Simple similarity calculation - can be enhanced
            100.0 - (total_changes as f64 / (added + removed + modified * 2) as f64 * 100.0)
        } else {
            100.0
        };

        Self {
            lines_added: added,
            lines_removed: removed,
            lines_modified: modified,
            total_changes,
            similarity_percentage: similarity.max(0.0).min(100.0),
        }
    }

    /// Get a human-readable summary of the changes
    pub fn summary(&self) -> String {
        if self.total_changes == 0 {
            "No changes".to_string()
        } else {
            format!(
                "+{} -{} ~{} ({:.1}% similar)",
                self.lines_added,
                self.lines_removed,
                self.lines_modified,
                self.similarity_percentage
            )
        }
    }
}

impl VersionDiff {
    /// Create a new version diff
    pub fn new(from_version: String, to_version: String, diff_type: DiffType) -> Self {
        Self {
            from_version,
            to_version,
            diff_type,
            changes: Vec::new(),
            statistics: DiffStatistics::new(0, 0, 0),
            created_at: Utc::now(),
        }
    }

    /// Add a change to the diff
    pub fn add_change(&mut self, change: DiffChange) {
        self.changes.push(change);
        self.recalculate_statistics();
    }

    /// Recalculate statistics based on current changes
    fn recalculate_statistics(&mut self) {
        let mut added = 0;
        let mut removed = 0;
        let mut modified = 0;

        for change in &self.changes {
            match change.change_type {
                ChangeType::Addition => added += 1,
                ChangeType::Deletion => removed += 1,
                ChangeType::Modification => modified += 1,
                ChangeType::Context => {} // Context doesn't count as a change
            }
        }

        self.statistics = DiffStatistics::new(added, removed, modified);
    }

    /// Get a human-readable summary of the diff
    pub fn summary(&self) -> String {
        self.statistics.summary()
    }

    /// Check if there are any actual changes (excluding context)
    pub fn has_changes(&self) -> bool {
        self.statistics.total_changes > 0
    }
}

/// Version control operation result
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VersionControlResult<T> {
    pub success: bool,
    pub data: Option<T>,
    pub message: String,
    pub operation: String,
    pub timestamp: DateTime<Utc>,
}

impl<T> VersionControlResult<T> {
    /// Create a successful result
    pub fn success(data: T, operation: String) -> Self {
        Self {
            success: true,
            data: Some(data),
            message: "Operation completed successfully".to_string(),
            operation,
            timestamp: Utc::now(),
        }
    }

    /// Create an error result
    pub fn error(message: String, operation: String) -> Self {
        Self {
            success: false,
            data: None,
            message,
            operation,
            timestamp: Utc::now(),
        }
    }
}
