//! Error types for k3s-manager

use std::fmt;

/// Result type for k3s-manager operations
pub type Result<T> = std::result::Result<T, K3sError>;

/// Error types that can occur during K3s management operations
#[derive(Debug)]
pub enum K3sError {
    /// SSH connection failed
    ConnectionError {
        /// The host that failed to connect
        host: String,
        /// The underlying error message
        message: String,
    },

    /// Failed to execute a command on the remote host
    CommandError {
        /// The host where the command failed
        host: String,
        /// The command that was executed
        command: String,
        /// Exit code from the command
        exit_code: Option<i32>,
        /// Error message or output
        message: String,
    },

    /// K3s installation failed
    InstallationError {
        /// The node where installation failed
        node: String,
        /// The reason for failure
        reason: String,
    },

    /// K3s is already installed
    AlreadyInstalledError {
        /// The node where K3s is already installed
        node: String,
    },

    /// K3s is not installed on a node
    NotInstalledError {
        /// The node where K3s is not installed
        node: String,
    },

    /// Cluster is not ready
    ClusterNotReadyError {
        /// Number of attempts made
        attempts: u32,
        /// Last error message
        message: String,
    },

    /// Configuration error
    ConfigurationError {
        /// The configuration issue
        message: String,
    },

    /// I/O error occurred
    IoError {
        /// The operation that failed
        operation: String,
        /// The underlying error
        source: std::io::Error,
    },

    /// Invalid cluster state
    InvalidStateError {
        /// Description of the invalid state
        message: String,
    },

    /// Timeout error
    TimeoutError {
        /// The operation that timed out
        operation: String,
        /// Duration of the timeout
        timeout_seconds: u64,
    },
}

impl fmt::Display for K3sError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            K3sError::ConnectionError { host, message } => {
                write!(f, "Failed to connect to {}: {}", host, message)
            }
            K3sError::CommandError {
                host,
                command,
                exit_code,
                message,
            } => {
                if let Some(code) = exit_code {
                    write!(
                        f,
                        "Command '{}' failed on {} with exit code {}: {}",
                        command, host, code, message
                    )
                } else {
                    write!(f, "Command '{}' failed on {}: {}", command, host, message)
                }
            }
            K3sError::InstallationError { node, reason } => {
                write!(f, "K3s installation failed on {}: {}", node, reason)
            }
            K3sError::AlreadyInstalledError { node } => {
                write!(
                    f,
                    "K3s is already installed on {}. Use force=true to reinstall",
                    node
                )
            }
            K3sError::NotInstalledError { node } => {
                write!(f, "K3s is not installed on {}", node)
            }
            K3sError::ClusterNotReadyError { attempts, message } => {
                write!(
                    f,
                    "Cluster did not become ready after {} attempts: {}",
                    attempts, message
                )
            }
            K3sError::ConfigurationError { message } => {
                write!(f, "Configuration error: {}", message)
            }
            K3sError::IoError { operation, source } => {
                write!(f, "I/O error during {}: {}", operation, source)
            }
            K3sError::InvalidStateError { message } => {
                write!(f, "Invalid cluster state: {}", message)
            }
            K3sError::TimeoutError {
                operation,
                timeout_seconds,
            } => {
                write!(
                    f,
                    "Operation '{}' timed out after {} seconds",
                    operation, timeout_seconds
                )
            }
        }
    }
}

impl std::error::Error for K3sError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match self {
            K3sError::IoError { source, .. } => Some(source),
            _ => None,
        }
    }
}

impl From<std::io::Error> for K3sError {
    fn from(err: std::io::Error) -> Self {
        K3sError::IoError {
            operation: "unknown".to_string(),
            source: err,
        }
    }
}
