//! Error types for the Kubernetes manager library.
//!
//! This module defines all error types that can occur during Kubernetes operations,
//! providing detailed context for debugging and error handling.

use thiserror::Error;

/// Result type alias for Kubernetes operations.
pub type Result<T> = std::result::Result<T, Error>;

/// Main error type for all Kubernetes operations.
#[derive(Error, Debug)]
pub enum Error {
    /// Error connecting to the Kubernetes cluster.
    #[error("Failed to connect to Kubernetes cluster: {0}")]
    ConnectionError(String),

    /// Error with Kubernetes configuration (kubeconfig, credentials, etc.).
    #[error("Configuration error: {0}")]
    ConfigurationError(String),

    /// Error during deployment operations.
    #[error("Deployment failed: {0}")]
    DeploymentError(String),

    /// Error during rollback operations.
    #[error("Rollback failed: {0}")]
    RollbackError(String),

    /// Error during scaling operations.
    #[error("Scaling failed: {0}")]
    ScalingError(String),

    /// Error during service operations.
    #[error("Service operation failed: {0}")]
    ServiceError(String),

    /// Error during secret operations.
    #[error("Secret operation failed: {0}")]
    SecretError(String),

    /// Error during namespace operations.
    #[error("Namespace operation failed: {0}")]
    NamespaceError(String),

    /// Error during ConfigMap operations.
    #[error("ConfigMap operation failed: {0}")]
    ConfigMapError(String),

    /// Error during Ingress operations.
    #[error("Ingress operation failed: {0}")]
    IngressError(String),

    /// Error during Gateway operations.
    #[error("Gateway operation failed: {0}")]
    GatewayError(String),

    /// Validation error for resource specs.
    #[error("Validation error: {0}")]
    ValidationError(String),

    /// Error during NetworkPolicy operations.
    #[error("NetworkPolicy operation failed: {0}")]
    NetworkPolicyError(String),

    /// Error during Job operations.
    #[error("Job operation failed: {0}")]
    JobError(String),

    /// Error during CronJob operations.
    #[error("CronJob operation failed: {0}")]
    CronJobError(String),

    /// Error during PersistentVolumeClaim operations.
    #[error("PersistentVolumeClaim operation failed: {0}")]
    PvcError(String),

    /// Error during HorizontalPodAutoscaler operations.
    #[error("HorizontalPodAutoscaler operation failed: {0}")]
    HpaError(String),

    /// Resource not found in the cluster.
    #[error("Resource not found: {kind} '{name}' in namespace '{namespace}'")]
    ResourceNotFound {
        /// Kind of resource.
        kind: String,
        /// Name of resource.
        name: String,
        /// Namespace where resource was searched.
        namespace: String,
    },

    /// Resource already exists.
    #[error("Resource already exists: {kind} '{name}' in namespace '{namespace}'")]
    ResourceAlreadyExists {
        /// Kind of resource.
        kind: String,
        /// Name of resource.
        name: String,
        /// Namespace where resource exists.
        namespace: String,
    },

    /// Timeout waiting for a resource to be ready.
    #[error("Timeout waiting for {resource} to be ready after {timeout_secs}s: {details}")]
    Timeout {
        /// Resource name.
        resource: String,
        /// Timeout duration in seconds.
        timeout_secs: u64,
        /// Additional details.
        details: String,
    },

    /// Pod failed with an error.
    #[error("Pod '{pod_name}' failed: {reason}")]
    PodFailed {
        /// Pod name.
        pod_name: String,
        /// Failure reason.
        reason: String,
    },

    /// Container crashed.
    #[error(
        "Container '{container}' in pod '{pod}' crashed: exit code {exit_code}, restarts: {restarts}"
    )]
    ContainerCrashed {
        /// Pod name.
        pod: String,
        /// Container name.
        container: String,
        /// Exit code.
        exit_code: i32,
        /// Restart count.
        restarts: i32,
    },

    /// Image pull error.
    #[error("Failed to pull image '{image}': {reason}")]
    ImagePullError {
        /// Image name.
        image: String,
        /// Error reason.
        reason: String,
    },

    /// Invalid resource specification.
    #[error("Invalid resource specification: {0}")]
    InvalidSpec(String),

    /// Serialization/deserialization error.
    #[error("Serialization error: {0}")]
    SerializationError(String),

    /// Underlying Kubernetes API error.
    #[error("Kubernetes API error: {0}")]
    KubeError(#[from] kube::Error),

    /// Generic internal error.
    #[error("Internal error: {0}")]
    Internal(String),
}

impl Error {
    /// Create a connection error.
    pub fn connection<S: Into<String>>(msg: S) -> Self {
        Self::ConnectionError(msg.into())
    }

    /// Create a configuration error.
    pub fn config<S: Into<String>>(msg: S) -> Self {
        Self::ConfigurationError(msg.into())
    }

    /// Create a deployment error.
    pub fn deployment<S: Into<String>>(msg: S) -> Self {
        Self::DeploymentError(msg.into())
    }

    /// Create a rollback error.
    pub fn rollback<S: Into<String>>(msg: S) -> Self {
        Self::RollbackError(msg.into())
    }

    /// Create a scaling error.
    pub fn scaling<S: Into<String>>(msg: S) -> Self {
        Self::ScalingError(msg.into())
    }

    /// Create a service error.
    pub fn service<S: Into<String>>(msg: S) -> Self {
        Self::ServiceError(msg.into())
    }

    /// Create a secret error.
    pub fn secret<S: Into<String>>(msg: S) -> Self {
        Self::SecretError(msg.into())
    }

    /// Create a namespace error.
    pub fn namespace<S: Into<String>>(msg: S) -> Self {
        Self::NamespaceError(msg.into())
    }

    /// Create a ConfigMap error.
    pub fn configmap<S: Into<String>>(msg: S) -> Self {
        Self::ConfigMapError(msg.into())
    }

    /// Create an Ingress error.
    pub fn ingress<S: Into<String>>(msg: S) -> Self {
        Self::IngressError(msg.into())
    }

    /// Create a Gateway error.
    pub fn gateway<S: Into<String>>(msg: S) -> Self {
        Self::GatewayError(msg.into())
    }

    /// Create a validation error.
    pub fn validation<S: Into<String>>(msg: S) -> Self {
        Self::ValidationError(msg.into())
    }

    /// Create a NetworkPolicy error.
    pub fn network_policy<S: Into<String>>(msg: S) -> Self {
        Self::NetworkPolicyError(msg.into())
    }

    /// Create a Job error.
    pub fn job<S: Into<String>>(msg: S) -> Self {
        Self::JobError(msg.into())
    }

    /// Create a CronJob error.
    pub fn cronjob<S: Into<String>>(msg: S) -> Self {
        Self::CronJobError(msg.into())
    }

    /// Create a PVC error.
    pub fn pvc<S: Into<String>>(msg: S) -> Self {
        Self::PvcError(msg.into())
    }

    /// Create an HPA error.
    pub fn hpa<S: Into<String>>(msg: S) -> Self {
        Self::HpaError(msg.into())
    }

    /// Create a resource not found error.
    pub fn not_found<S: Into<String>>(kind: S, name: S, namespace: S) -> Self {
        Self::ResourceNotFound {
            kind: kind.into(),
            name: name.into(),
            namespace: namespace.into(),
        }
    }

    /// Create a resource already exists error.
    pub fn already_exists<S: Into<String>>(kind: S, name: S, namespace: S) -> Self {
        Self::ResourceAlreadyExists {
            kind: kind.into(),
            name: name.into(),
            namespace: namespace.into(),
        }
    }

    /// Create a timeout error.
    pub fn timeout<S: Into<String>>(resource: S, timeout_secs: u64, details: S) -> Self {
        Self::Timeout {
            resource: resource.into(),
            timeout_secs,
            details: details.into(),
        }
    }

    /// Create a pod failed error.
    pub fn pod_failed<S: Into<String>>(pod_name: S, reason: S) -> Self {
        Self::PodFailed {
            pod_name: pod_name.into(),
            reason: reason.into(),
        }
    }

    /// Create a container crashed error.
    pub fn container_crashed<S: Into<String>>(
        pod: S,
        container: S,
        exit_code: i32,
        restarts: i32,
    ) -> Self {
        Self::ContainerCrashed {
            pod: pod.into(),
            container: container.into(),
            exit_code,
            restarts,
        }
    }

    /// Create an image pull error.
    pub fn image_pull<S1: Into<String>, S2: Into<String>>(image: S1, reason: S2) -> Self {
        Self::ImagePullError {
            image: image.into(),
            reason: reason.into(),
        }
    }

    /// Create an invalid spec error.
    pub fn invalid_spec<S: Into<String>>(msg: S) -> Self {
        Self::InvalidSpec(msg.into())
    }

    /// Create an internal error.
    pub fn internal<S: Into<String>>(msg: S) -> Self {
        Self::Internal(msg.into())
    }
}

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

    #[test]
    fn test_error_construction() {
        let err = Error::connection("test connection failed");
        assert!(err.to_string().contains("test connection failed"));

        let err = Error::not_found("Deployment", "my-app", "default");
        assert!(err.to_string().contains("my-app"));
        assert!(err.to_string().contains("default"));

        let err = Error::timeout("deployment/my-app", 120, "pods not ready");
        assert!(err.to_string().contains("120s"));
    }

    #[test]
    fn test_container_crashed_error() {
        let err = Error::container_crashed("my-pod", "app", 137, 3);
        let msg = err.to_string();
        assert!(msg.contains("my-pod"));
        assert!(msg.contains("app"));
        assert!(msg.contains("137"));
        assert!(msg.contains("3"));
    }

    #[test]
    fn test_image_pull_error() {
        let err = Error::image_pull("registry.io/myapp:v1", "ImagePullBackOff");
        let msg = err.to_string();
        assert!(msg.contains("registry.io/myapp:v1"));
        assert!(msg.contains("ImagePullBackOff"));
    }
}
