//! Publication management

use crate::error::Result;
use crate::types::{DeploymentMethod, PublicationTargetType, WebsiteType};
use crate::LifecycleError;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};

/// Publication target
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PublicationTarget {
    /// Target name
    pub name: String,
    /// Target type
    pub target_type: PublicationTargetType,
    /// Target enabled
    pub enabled: bool,
    /// Configuration
    pub config: PublicationConfig,
}

impl PublicationTarget {
    /// Create new publication target
    pub fn new(name: impl Into<String>, target_type: PublicationTargetType) -> Self {
        Self {
            name: name.into(),
            target_type,
            enabled: false,
            config: PublicationConfig::default(),
        }
    }

    /// Enable target
    pub fn enable(mut self) -> Self {
        self.enabled = true;
        self
    }
}

/// Publication configuration
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct PublicationConfig {
    /// Configuration map
    pub data: std::collections::HashMap<String, serde_json::Value>,
}

/// Rustelo publication target
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RusteloTarget {
    /// Site path
    pub site_path: String,
    /// Domain
    pub domain: String,
}

/// Website publication target
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WebsiteTarget {
    /// Website type
    pub website_type: WebsiteType,
    /// Repository URL
    pub repo_url: String,
    /// Deployment method
    pub deployment: DeploymentMethod,
}

/// Publication record
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PublicationRecord {
    /// Publication ID
    pub id: String,
    /// Target name
    pub target: String,
    /// Version published
    pub version: String,
    /// Publication timestamp
    pub timestamp: DateTime<Utc>,
    /// Status (success, pending, failed)
    pub status: String,
}

/// Publication manager
#[derive(Debug)]
pub struct PublicationManager {
    /// Publication targets
    pub targets: Vec<PublicationTarget>,
    /// Publication history
    pub history: Vec<PublicationRecord>,
}

impl PublicationManager {
    /// Create a new publication manager
    pub fn new() -> Self {
        Self {
            targets: vec![],
            history: vec![],
        }
    }

    /// Add a publication target
    pub fn add_target(&mut self, target: PublicationTarget) {
        self.targets.push(target);
    }

    /// Get target by name
    pub fn get_target(&self, name: &str) -> Option<&PublicationTarget> {
        self.targets.iter().find(|t| t.name == name)
    }

    /// Get mutable target by name
    pub fn get_target_mut(&mut self, name: &str) -> Option<&mut PublicationTarget> {
        self.targets.iter_mut().find(|t| t.name == name)
    }

    /// Enable target
    pub fn enable_target(&mut self, name: &str) -> Result<()> {
        if let Some(target) = self.get_target_mut(name) {
            target.enabled = true;
            Ok(())
        } else {
            Err(LifecycleError::publication(format!(
                "Target not found: {}",
                name
            )))
        }
    }

    /// Publish to target
    pub fn publish_to(&mut self, target_name: &str, version: &str) -> Result<String> {
        if let Some(target) = self.get_target(target_name) {
            if !target.enabled {
                return Err(LifecycleError::publication(format!(
                    "Target disabled: {}",
                    target_name
                )));
            }

            let id = uuid::Uuid::new_v4().to_string();
            let record = PublicationRecord {
                id: id.clone(),
                target: target_name.to_string(),
                version: version.to_string(),
                timestamp: Utc::now(),
                status: "success".to_string(),
            };

            self.history.push(record);
            tracing::info!("Published {} v{} to {}", target_name, version, target_name);
            Ok(id)
        } else {
            Err(LifecycleError::publication(format!(
                "Target not found: {}",
                target_name
            )))
        }
    }

    /// Pre-publish checks
    pub fn pre_publish_checks(&self, target_name: &str) -> Result<Vec<String>> {
        if let Some(target) = self.get_target(target_name) {
            let mut checks = vec![];

            // Target enabled check
            if !target.enabled {
                checks.push("Target is not enabled".to_string());
            }

            // Configuration completeness check
            if target.config.data.is_empty() {
                checks.push("Target configuration is empty".to_string());
            }

            Ok(checks)
        } else {
            Err(LifecycleError::publication(format!(
                "Target not found: {}",
                target_name
            )))
        }
    }

    /// Get publication history for target
    pub fn get_history(&self, target_name: &str) -> Vec<&PublicationRecord> {
        self.history
            .iter()
            .filter(|r| r.target == target_name)
            .collect()
    }

    /// Get latest publication
    pub fn get_latest(&self, target_name: &str) -> Option<&PublicationRecord> {
        self.get_history(target_name)
            .iter()
            .max_by_key(|r| r.timestamp)
            .copied()
    }

    /// Rollback publication
    pub fn rollback(&mut self, target_name: &str, version: &str) -> Result<()> {
        if self.get_target(target_name).is_none() {
            return Err(LifecycleError::publication(format!(
                "Target not found: {}",
                target_name
            )));
        }

        // Find and mark as rolled back
        for record in &mut self.history {
            if record.target == target_name && record.version == version {
                record.status = "rolled_back".to_string();
                tracing::info!("Rolled back {} v{}", target_name, version);
                return Ok(());
            }
        }

        Err(LifecycleError::publication(format!(
            "Version not found: {}",
            version
        )))
    }

    /// Get all enabled targets
    pub fn enabled_targets(&self) -> Vec<&PublicationTarget> {
        self.targets.iter().filter(|t| t.enabled).collect()
    }

    /// Get publication summary
    pub fn summary(&self) -> PublicationSummary {
        PublicationSummary {
            total_targets: self.targets.len(),
            enabled_targets: self.enabled_targets().len(),
            total_publications: self.history.len(),
            successful_publications: self
                .history
                .iter()
                .filter(|r| r.status == "success")
                .count(),
        }
    }
}

impl Default for PublicationManager {
    fn default() -> Self {
        Self::new()
    }
}

/// Publication summary
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PublicationSummary {
    /// Total publication targets
    pub total_targets: usize,
    /// Enabled targets
    pub enabled_targets: usize,
    /// Total publications
    pub total_publications: usize,
    /// Successful publications
    pub successful_publications: usize,
}

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

    #[test]
    fn test_publication_target_creation() {
        let target = PublicationTarget::new("docker", PublicationTargetType::Docker).enable();
        assert!(target.enabled);
        assert_eq!(target.name, "docker");
    }

    #[test]
    fn test_publication_manager_creation() {
        let manager = PublicationManager::new();
        assert_eq!(manager.targets.len(), 0);
        assert_eq!(manager.history.len(), 0);
    }

    #[test]
    fn test_add_target() {
        let mut manager = PublicationManager::new();
        let target = PublicationTarget::new("docker", PublicationTargetType::Docker).enable();
        manager.add_target(target);

        assert_eq!(manager.targets.len(), 1);
        assert!(manager.get_target("docker").is_some());
    }

    #[test]
    fn test_publish_to_target() {
        let mut manager = PublicationManager::new();
        let target = PublicationTarget::new("docker", PublicationTargetType::Docker).enable();
        manager.add_target(target);

        let result = manager.publish_to("docker", "1.0.0");
        assert!(result.is_ok());
        assert_eq!(manager.history.len(), 1);
    }

    #[test]
    fn test_pre_publish_checks() {
        let mut manager = PublicationManager::new();
        let target = PublicationTarget::new("docker", PublicationTargetType::Docker);
        manager.add_target(target);

        let checks = manager.pre_publish_checks("docker").unwrap();
        assert!(!checks.is_empty());
        assert!(checks.iter().any(|c| c.contains("not enabled")));
    }

    #[test]
    fn test_publication_summary() {
        let mut manager = PublicationManager::new();
        let target = PublicationTarget::new("docker", PublicationTargetType::Docker).enable();
        manager.add_target(target);
        manager.publish_to("docker", "1.0.0").ok();

        let summary = manager.summary();
        assert_eq!(summary.total_targets, 1);
        assert_eq!(summary.enabled_targets, 1);
        assert_eq!(summary.total_publications, 1);
    }

    #[test]
    fn test_publication_history() {
        let mut manager = PublicationManager::new();
        let target = PublicationTarget::new("docker", PublicationTargetType::Docker).enable();
        manager.add_target(target);

        manager.publish_to("docker", "1.0.0").ok();
        manager.publish_to("docker", "1.0.1").ok();

        let history = manager.get_history("docker");
        assert_eq!(history.len(), 2);
    }
}
