//! Transaction management for apt-ostree daemon

use crate::daemon::{DaemonResult, DaemonError};
use std::collections::HashMap;
use std::fmt;
use uuid::Uuid;
use chrono::{DateTime, Utc};

/// Transaction types
#[derive(Debug, Clone, PartialEq)]
pub enum TransactionType {
    Install,
    Remove,
    Upgrade,
    Rollback,
    Deploy,
    Rebase,
}

/// Transaction states
#[derive(Debug, Clone, PartialEq)]
pub enum TransactionState {
    Pending,
    Running,
    Completed,
    Failed,
    Cancelled,
}

impl fmt::Display for TransactionState {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            TransactionState::Pending => write!(f, "pending"),
            TransactionState::Running => write!(f, "running"),
            TransactionState::Completed => write!(f, "completed"),
            TransactionState::Failed => write!(f, "failed"),
            TransactionState::Cancelled => write!(f, "cancelled"),
        }
    }
}

/// Transaction result
#[derive(Debug, Clone)]
pub struct TransactionResult {
    pub success: bool,
    pub message: String,
    pub details: Option<String>,
}

/// Transaction information
#[derive(Debug, Clone)]
pub struct Transaction {
    pub id: String,
    pub transaction_type: TransactionType,
    pub state: TransactionState,
    pub created_at: DateTime<Utc>,
    pub started_at: Option<DateTime<Utc>>,
    pub completed_at: Option<DateTime<Utc>>,
    pub result: Option<TransactionResult>,
    pub progress: f64, // 0.0 to 1.0
    pub metadata: HashMap<String, String>,
}

impl Transaction {
    pub fn new(transaction_type: TransactionType) -> Self {
        Self {
            id: Uuid::new_v4().to_string(),
            transaction_type,
            state: TransactionState::Pending,
            created_at: Utc::now(),
            started_at: None,
            completed_at: None,
            result: None,
            progress: 0.0,
            metadata: HashMap::new(),
        }
    }

    pub fn start(&mut self) -> DaemonResult<()> {
        if self.state != TransactionState::Pending {
            return Err(DaemonError::Transaction("Transaction is not in pending state".to_string()));
        }
        self.state = TransactionState::Running;
        self.started_at = Some(Utc::now());
        Ok(())
    }

    pub fn complete(&mut self, result: TransactionResult) -> DaemonResult<()> {
        if self.state != TransactionState::Running {
            return Err(DaemonError::Transaction("Transaction is not in running state".to_string()));
        }
        self.state = TransactionState::Completed;
        self.completed_at = Some(Utc::now());
        self.result = Some(result);
        self.progress = 1.0;
        Ok(())
    }

    pub fn fail(&mut self, error: String) -> DaemonResult<()> {
        if self.state != TransactionState::Running {
            return Err(DaemonError::Transaction("Transaction is not in running state".to_string()));
        }
        self.state = TransactionState::Failed;
        self.completed_at = Some(Utc::now());
        self.result = Some(TransactionResult {
            success: false,
            message: error,
            details: None,
        });
        Ok(())
    }

    pub fn cancel(&mut self) -> DaemonResult<()> {
        if self.state != TransactionState::Pending && self.state != TransactionState::Running {
            return Err(DaemonError::Transaction("Transaction cannot be cancelled in current state".to_string()));
        }
        self.state = TransactionState::Cancelled;
        self.completed_at = Some(Utc::now());
        self.result = Some(TransactionResult {
            success: false,
            message: "Transaction cancelled".to_string(),
            details: None,
        });
        Ok(())
    }

    pub fn update_progress(&mut self, progress: f64) -> DaemonResult<()> {
        if !(0.0..=1.0).contains(&progress) {
            return Err(DaemonError::Transaction("Progress must be between 0.0 and 1.0".to_string()));
        }
        self.progress = progress;
        Ok(())
    }

    pub fn add_metadata(&mut self, key: String, value: String) {
        self.metadata.insert(key, value);
    }
}

/// Transaction manager
#[allow(dead_code, clippy::new_without_default)]
pub struct TransactionManager {
    transactions: HashMap<String, Transaction>,
    next_transaction_id: u64,
}

impl TransactionManager {
    pub fn new() -> Self {
        Self {
            transactions: HashMap::new(),
            next_transaction_id: 1,
        }
    }

    pub fn start_transaction(&mut self, transaction_type: TransactionType) -> String {
        let transaction = Transaction::new(transaction_type);
        let id = transaction.id.clone();
        self.transactions.insert(id.clone(), transaction);
        id
    }

    pub fn get_transaction(&self, transaction_id: &str) -> Option<&Transaction> {
        self.transactions.get(transaction_id)
    }

    pub fn get_transaction_mut(&mut self, transaction_id: &str) -> Option<&mut Transaction> {
        self.transactions.get_mut(transaction_id)
    }

    pub fn get_transaction_status(&self, transaction_id: &str) -> DaemonResult<TransactionState> {
        let transaction = self.transactions.get(transaction_id)
            .ok_or_else(|| DaemonError::Transaction(format!("Transaction {} not found", transaction_id)))?;
        Ok(transaction.state.clone())
    }

    pub fn list_transactions(&self) -> Vec<&Transaction> {
        self.transactions.values().collect()
    }

    pub fn cleanup_completed_transactions(&mut self, max_age_hours: u64) {
        let cutoff = Utc::now() - chrono::Duration::hours(max_age_hours as i64);
        self.transactions.retain(|_, transaction| {
            if let Some(completed_at) = transaction.completed_at {
                completed_at > cutoff
            } else {
                true // Keep incomplete transactions
            }
        });
    }

    pub fn get_active_transactions(&self) -> Vec<&Transaction> {
        self.transactions.values()
            .filter(|t| t.state == TransactionState::Pending || t.state == TransactionState::Running)
            .collect()
    }

    pub fn start_existing_transaction(&mut self, transaction_id: &str) -> DaemonResult<()> {
        if let Some(transaction) = self.transactions.get_mut(transaction_id) {
            transaction.start()?;
        }
        Ok(())
    }

    pub fn complete_transaction(&mut self, transaction_id: &str, success: bool, message: String) -> DaemonResult<()> {
        if let Some(transaction) = self.transactions.get_mut(transaction_id) {
            let result = TransactionResult {
                success,
                message: message.clone(),
                details: None,
            };
            if success {
                transaction.complete(result)?;
            } else {
                transaction.fail(message)?;
            }
        }
        Ok(())
    }

    pub fn get_active_transaction_count(&self) -> usize {
        self.transactions.values()
            .filter(|t| t.state == TransactionState::Running || t.state == TransactionState::Pending)
            .count()
    }
}

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