//! Version Control Traits
//!
//! Defines the unified interface for version control operations that can be
//! implemented by different storage backends.

use anyhow::Result;
use async_trait::async_trait;

use super::models::{VersionDiff, VersionHistory};
use crate::content::file_loader::FileContent;

/// Core version control operations that all backends must implement
#[async_trait]
pub trait VersionControlBackend {
    /// Create a new version of content
    async fn create_version(
        &self,
        content_path: &str,
        content: &FileContent,
        author: Option<String>,
        version_notes: Option<String>,
    ) -> Result<String>;

    /// Get version history for content
    async fn get_version_history(&self, content_path: &str) -> Result<VersionHistory>;

    /// Get specific version of content
    async fn get_version(&self, content_path: &str, version: &str) -> Result<Option<FileContent>>;

    /// Rollback content to specific version
    async fn rollback_to_version(
        &self,
        content_path: &str,
        target_version: &str,
        author: Option<String>,
    ) -> Result<String>;

    /// Generate diff between two versions
    async fn generate_diff(
        &self,
        content_path: &str,
        from_version: &str,
        to_version: &str,
    ) -> Result<VersionDiff>;

    /// List all available versions for content
    async fn list_versions(&self, content_path: &str) -> Result<Vec<String>>;

    /// Auto-version content when it's modified (if enabled)
    async fn auto_version_content(
        &self,
        content_path: &str,
        content: &FileContent,
        author: Option<String>,
    ) -> Result<Option<String>>;

    /// Check if content has changed since last version
    async fn has_content_changed(&self, content_path: &str, content: &FileContent) -> Result<bool>;

    /// Get backend-specific configuration and status
    async fn get_backend_info(&self) -> Result<BackendInfo>;
}

/// Information about the version control backend
#[derive(Debug, Clone, serde::Serialize)]
pub struct BackendInfo {
    pub backend_type: String,
    pub supports_branching: bool,
    pub supports_merging: bool,
    pub storage_location: String,
    pub total_versions: usize,
    pub total_content_items: usize,
}

/// High-level version control manager that coordinates operations
pub struct VersionControlManager {
    backend: Box<dyn VersionControlBackend + Send + Sync>,
    config: VersionControlConfig,
}

/// Configuration for version control behavior
#[derive(Debug, Clone)]
pub struct VersionControlConfig {
    pub auto_version_enabled: bool,
    pub max_versions_per_content: Option<usize>,
    pub cleanup_old_versions: bool,
    pub require_version_notes: bool,
    pub default_author: Option<String>,
}

impl Default for VersionControlConfig {
    fn default() -> Self {
        Self {
            auto_version_enabled: true,
            max_versions_per_content: None, // No limit
            cleanup_old_versions: false,
            require_version_notes: false,
            default_author: None,
        }
    }
}

impl VersionControlManager {
    /// Create a new version control manager with the given backend
    pub fn new(
        backend: Box<dyn VersionControlBackend + Send + Sync>,
        config: Option<VersionControlConfig>,
    ) -> Self {
        Self {
            backend,
            config: config.unwrap_or_default(),
        }
    }

    /// Create a new version of content
    pub async fn create_version(
        &self,
        content_path: &str,
        content: &FileContent,
        author: Option<String>,
        version_notes: Option<String>,
    ) -> Result<String> {
        // Apply configuration validation
        if self.config.require_version_notes && version_notes.is_none() {
            return Err(anyhow::anyhow!("Version notes are required"));
        }

        let effective_author = author.or_else(|| self.config.default_author.clone());

        let version = self
            .backend
            .create_version(content_path, content, effective_author, version_notes)
            .await?;

        // Cleanup old versions if configured
        if self.config.cleanup_old_versions {
            if let Some(max_versions) = self.config.max_versions_per_content {
                self.cleanup_old_versions(content_path, max_versions)
                    .await?;
            }
        }

        Ok(version)
    }

    /// Get version history for content
    pub async fn get_version_history(&self, content_path: &str) -> Result<VersionHistory> {
        self.backend.get_version_history(content_path).await
    }

    /// Get specific version of content
    pub async fn get_version(
        &self,
        content_path: &str,
        version: &str,
    ) -> Result<Option<FileContent>> {
        self.backend.get_version(content_path, version).await
    }

    /// Rollback content to specific version
    pub async fn rollback_to_version(
        &self,
        content_path: &str,
        target_version: &str,
        author: Option<String>,
    ) -> Result<String> {
        let effective_author = author.or_else(|| self.config.default_author.clone());
        self.backend
            .rollback_to_version(content_path, target_version, effective_author)
            .await
    }

    /// Generate diff between two versions
    pub async fn generate_diff(
        &self,
        content_path: &str,
        from_version: &str,
        to_version: &str,
    ) -> Result<VersionDiff> {
        self.backend
            .generate_diff(content_path, from_version, to_version)
            .await
    }

    /// List all available versions for content
    pub async fn list_versions(&self, content_path: &str) -> Result<Vec<String>> {
        self.backend.list_versions(content_path).await
    }

    /// Auto-version content when it's modified (if enabled)
    pub async fn auto_version_content(
        &self,
        content_path: &str,
        content: &FileContent,
        author: Option<String>,
    ) -> Result<Option<String>> {
        if !self.config.auto_version_enabled {
            return Ok(None);
        }

        let effective_author = author.or_else(|| self.config.default_author.clone());
        self.backend
            .auto_version_content(content_path, content, effective_author)
            .await
    }

    /// Check if content has changed since last version
    pub async fn has_content_changed(
        &self,
        content_path: &str,
        content: &FileContent,
    ) -> Result<bool> {
        self.backend
            .has_content_changed(content_path, content)
            .await
    }

    /// Get backend information and statistics
    pub async fn get_backend_info(&self) -> Result<BackendInfo> {
        self.backend.get_backend_info().await
    }

    /// Get current configuration
    pub fn get_config(&self) -> &VersionControlConfig {
        &self.config
    }

    /// Update configuration
    pub fn update_config(&mut self, config: VersionControlConfig) {
        self.config = config;
    }

    // Private helper methods

    async fn cleanup_old_versions(&self, content_path: &str, max_versions: usize) -> Result<()> {
        let versions = self.list_versions(content_path).await?;

        if versions.len() > max_versions {
            let to_remove = versions.len() - max_versions;
            tracing::info!(
                "Cleaning up {} old versions for content: {}",
                to_remove,
                content_path
            );
            // Implementation would depend on backend capabilities
            // For now, just log the intention
        }

        Ok(())
    }
}
