use axum::{
    http::StatusCode,
    response::{IntoResponse, Response},
    Json,
};
use thiserror::Error;
use tracing::error;

/// Result type alias for the control center
pub type Result<T> = std::result::Result<T, ControlCenterError>;

/// Main error type for the control center service
#[derive(Error, Debug)]
pub enum ControlCenterError {
    /// Database-related errors
    #[error("Database error: {0}")]
    Database(String),

    /// Authentication errors
    #[error("Authentication error: {0}")]
    Authentication(String),

    /// Authorization errors
    #[error("Authorization error: {0}")]
    Authorization(String),

    /// JWT token errors
    #[error("Token error: {0}")]
    Token(String),

    /// Validation errors
    #[error("Validation error: {0}")]
    Validation(String),

    /// User not found
    #[error("User not found: {0}")]
    UserNotFound(String),

    /// Role not found
    #[error("Role not found: {0}")]
    RoleNotFound(String),

    /// Permission not found
    #[error("Permission not found: {0}")]
    PermissionNotFound(String),

    /// Session not found
    #[error("Session not found: {0}")]
    SessionNotFound(String),

    /// Session expired
    #[error("Session expired: {0}")]
    SessionExpired(String),

    /// Resource not found
    #[error("Not found: {0}")]
    NotFound(String),

    /// Unauthorized access
    #[error("Unauthorized: {0}")]
    Unauthorized(String),

    /// Forbidden access
    #[error("Forbidden: {0}")]
    Forbidden(String),

    /// Conflict (e.g., duplicate resources)
    #[error("Conflict: {0}")]
    Conflict(String),

    /// Bad request
    #[error("Bad request: {0}")]
    BadRequest(String),

    /// Internal server error
    #[error("Internal server error: {0}")]
    Internal(String),

    /// Configuration errors
    #[error("Configuration error: {0}")]
    Configuration(String),

    /// External service errors
    #[error("External service error: {0}")]
    ExternalService(String),

    /// Rate limit exceeded
    #[error("Rate limit exceeded: {0}")]
    RateLimit(String),

    /// WebSocket errors
    #[error("WebSocket error: {0}")]
    WebSocket(String),

    /// Serialization errors
    #[error("Serialization error: {0}")]
    Serialization(String),

    /// Policy evaluation error
    #[error("Policy evaluation error: {0}")]
    PolicyEvaluation(String),

    /// Policy parsing error
    #[error("Policy parsing error: {0}")]
    PolicyParsing(String),

    /// Cedar policy error
    #[error("Cedar policy error: {0}")]
    Cedar(String),

    /// Compliance check failed
    #[error("Compliance check failed: {0}")]
    Compliance(String),

    /// Anomaly detection error
    #[error("Anomaly detection error: {0}")]
    Anomaly(String),

    /// Analysis failed error
    #[error("Analysis failed: {0}")]
    AnalysisFailed(String),

    /// Generic anyhow errors
    #[error(transparent)]
    Other(#[from] anyhow::Error),
}

impl ControlCenterError {
    /// Get the HTTP status code for this error
    pub fn status_code(&self) -> StatusCode {
        match self {
            Self::Database(_) => StatusCode::INTERNAL_SERVER_ERROR,
            Self::Authentication(_) => StatusCode::UNAUTHORIZED,
            Self::Authorization(_) => StatusCode::FORBIDDEN,
            Self::Token(_) => StatusCode::UNAUTHORIZED,
            Self::Validation(_) => StatusCode::BAD_REQUEST,
            Self::UserNotFound(_) => StatusCode::NOT_FOUND,
            Self::RoleNotFound(_) => StatusCode::NOT_FOUND,
            Self::PermissionNotFound(_) => StatusCode::NOT_FOUND,
            Self::SessionNotFound(_) => StatusCode::NOT_FOUND,
            Self::SessionExpired(_) => StatusCode::UNAUTHORIZED,
            Self::NotFound(_) => StatusCode::NOT_FOUND,
            Self::Unauthorized(_) => StatusCode::UNAUTHORIZED,
            Self::Forbidden(_) => StatusCode::FORBIDDEN,
            Self::Conflict(_) => StatusCode::CONFLICT,
            Self::BadRequest(_) => StatusCode::BAD_REQUEST,
            Self::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR,
            Self::Configuration(_) => StatusCode::INTERNAL_SERVER_ERROR,
            Self::ExternalService(_) => StatusCode::BAD_GATEWAY,
            Self::RateLimit(_) => StatusCode::TOO_MANY_REQUESTS,
            Self::WebSocket(_) => StatusCode::BAD_REQUEST,
            Self::Serialization(_) => StatusCode::INTERNAL_SERVER_ERROR,
            Self::PolicyEvaluation(_) => StatusCode::INTERNAL_SERVER_ERROR,
            Self::PolicyParsing(_) => StatusCode::BAD_REQUEST,
            Self::Cedar(_) => StatusCode::INTERNAL_SERVER_ERROR,
            Self::Compliance(_) => StatusCode::FORBIDDEN,
            Self::Anomaly(_) => StatusCode::INTERNAL_SERVER_ERROR,
            Self::AnalysisFailed(_) => StatusCode::BAD_REQUEST,
            Self::Other(_) => StatusCode::INTERNAL_SERVER_ERROR,
        }
    }

    /// Get the error code string for API responses
    pub fn error_code(&self) -> &'static str {
        match self {
            Self::Database(_) => "DATABASE_ERROR",
            Self::Authentication(_) => "AUTHENTICATION_ERROR",
            Self::Authorization(_) => "AUTHORIZATION_ERROR",
            Self::Token(_) => "TOKEN_ERROR",
            Self::Validation(_) => "VALIDATION_ERROR",
            Self::UserNotFound(_) => "USER_NOT_FOUND",
            Self::RoleNotFound(_) => "ROLE_NOT_FOUND",
            Self::PermissionNotFound(_) => "PERMISSION_NOT_FOUND",
            Self::SessionNotFound(_) => "SESSION_NOT_FOUND",
            Self::SessionExpired(_) => "SESSION_EXPIRED",
            Self::NotFound(_) => "NOT_FOUND",
            Self::Unauthorized(_) => "UNAUTHORIZED",
            Self::Forbidden(_) => "FORBIDDEN",
            Self::Conflict(_) => "CONFLICT",
            Self::BadRequest(_) => "BAD_REQUEST",
            Self::Internal(_) => "INTERNAL_ERROR",
            Self::Configuration(_) => "CONFIGURATION_ERROR",
            Self::ExternalService(_) => "EXTERNAL_SERVICE_ERROR",
            Self::RateLimit(_) => "RATE_LIMIT_EXCEEDED",
            Self::WebSocket(_) => "WEBSOCKET_ERROR",
            Self::Serialization(_) => "SERIALIZATION_ERROR",
            Self::PolicyEvaluation(_) => "POLICY_EVALUATION_ERROR",
            Self::PolicyParsing(_) => "POLICY_PARSING_ERROR",
            Self::Cedar(_) => "CEDAR_ERROR",
            Self::Compliance(_) => "COMPLIANCE_ERROR",
            Self::Anomaly(_) => "ANOMALY_ERROR",
            Self::AnalysisFailed(_) => "ANALYSIS_FAILED",
            Self::Other(_) => "INTERNAL_ERROR",
        }
    }

    /// Check if this error should be logged at ERROR level
    pub fn should_log_error(&self) -> bool {
        match self {
            Self::Database(_) => true,
            Self::Internal(_) => true,
            Self::Configuration(_) => true,
            Self::Serialization(_) => true,
            Self::PolicyEvaluation(_) => true,
            Self::Cedar(_) => true,
            Self::Anomaly(_) => true,
            Self::AnalysisFailed(_) => true,
            Self::Other(_) => true,
            _ => false, // Client errors are logged at WARN level
        }
    }
}

/// Error response structure for API
#[derive(serde::Serialize)]
pub struct ErrorResponse {
    pub error: String,
    pub error_code: String,
    pub message: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub details: Option<serde_json::Value>,
    pub timestamp: chrono::DateTime<chrono::Utc>,
}

impl ErrorResponse {
    pub fn new(error: &ControlCenterError) -> Self {
        Self {
            error: error.error_code().to_string(),
            error_code: error.error_code().to_string(),
            message: error.to_string(),
            details: None,
            timestamp: chrono::Utc::now(),
        }
    }

    pub fn with_details(mut self, details: serde_json::Value) -> Self {
        self.details = Some(details);
        self
    }
}

impl IntoResponse for ControlCenterError {
    fn into_response(self) -> Response {
        let status_code = self.status_code();

        // Log the error appropriately
        if self.should_log_error() {
            error!("Error occurred: {} (status: {})", self, status_code);
        } else {
            tracing::warn!("Client error: {} (status: {})", self, status_code);
        }

        let error_response = ErrorResponse::new(&self);

        (status_code, Json(error_response)).into_response()
    }
}

// Implement conversions for common error types

impl From<surrealdb::Error> for ControlCenterError {
    fn from(error: surrealdb::Error) -> Self {
        Self::Database(error.to_string())
    }
}

impl From<jsonwebtoken::errors::Error> for ControlCenterError {
    fn from(error: jsonwebtoken::errors::Error) -> Self {
        Self::Token(error.to_string())
    }
}

impl From<validator::ValidationErrors> for ControlCenterError {
    fn from(errors: validator::ValidationErrors) -> Self {
        let details = errors
            .field_errors()
            .iter()
            .map(|(field, errors)| {
                let messages: Vec<String> = errors
                    .iter()
                    .filter_map(|e| e.message.as_ref().map(|m| m.to_string()))
                    .collect();
                (field.to_string(), messages)
            })
            .collect::<std::collections::HashMap<_, _>>();

        Self::Validation(format!("Validation failed: {:?}", details))
    }
}

impl From<serde_json::Error> for ControlCenterError {
    fn from(error: serde_json::Error) -> Self {
        Self::Serialization(error.to_string())
    }
}

impl From<std::io::Error> for ControlCenterError {
    fn from(error: std::io::Error) -> Self {
        Self::Internal(format!("IO error: {}", error))
    }
}

impl From<toml::de::Error> for ControlCenterError {
    fn from(error: toml::de::Error) -> Self {
        Self::Configuration(format!("TOML parsing error: {}", error))
    }
}

impl From<argon2::Error> for ControlCenterError {
    fn from(error: argon2::Error) -> Self {
        Self::Authentication(format!("Password hashing error: {}", error))
    }
}

impl From<reqwest::Error> for ControlCenterError {
    fn from(error: reqwest::Error) -> Self {
        Self::ExternalService(error.to_string())
    }
}

impl From<cedar_policy::ParseError> for ControlCenterError {
    fn from(err: cedar_policy::ParseError) -> Self {
        ControlCenterError::Cedar(err.to_string())
    }
}

impl From<cedar_policy::PolicySetError> for ControlCenterError {
    fn from(err: cedar_policy::PolicySetError) -> Self {
        ControlCenterError::Cedar(err.to_string())
    }
}

impl From<crate::kms::kms_service_client::KmsClientError> for ControlCenterError {
    fn from(error: crate::kms::kms_service_client::KmsClientError) -> Self {
        Self::ExternalService(format!("KMS error: {}", error))
    }
}

impl From<crate::kms::error::KmsError> for ControlCenterError {
    fn from(error: crate::kms::error::KmsError) -> Self {
        Self::Internal(format!("KMS error: {}", error))
    }
}

/// Helper macro for creating specific error types
#[macro_export]
macro_rules! control_center_error {
    ($variant:ident, $msg:expr) => {
        $crate::error::ControlCenterError::$variant($msg.to_string())
    };
    ($variant:ident, $fmt:expr, $($arg:tt)*) => {
        $crate::error::ControlCenterError::$variant(format!($fmt, $($arg)*))
    };
}

/// Helper functions for creating common errors
pub fn user_not_found(identifier: &str) -> ControlCenterError {
    ControlCenterError::UserNotFound(identifier.to_string())
}

pub fn session_not_found(session_id: &str) -> ControlCenterError {
    ControlCenterError::SessionNotFound(session_id.to_string())
}

pub fn session_expired(session_id: &str) -> ControlCenterError {
    ControlCenterError::SessionExpired(session_id.to_string())
}

pub fn unauthorized(message: &str) -> ControlCenterError {
    ControlCenterError::Unauthorized(message.to_string())
}

pub fn forbidden(message: &str) -> ControlCenterError {
    ControlCenterError::Forbidden(message.to_string())
}

pub fn bad_request(message: &str) -> ControlCenterError {
    ControlCenterError::BadRequest(message.to_string())
}

pub fn conflict(message: &str) -> ControlCenterError {
    ControlCenterError::Conflict(message.to_string())
}

pub fn internal_error(message: &str) -> ControlCenterError {
    ControlCenterError::Internal(message.to_string())
}

pub fn not_found(message: &str) -> ControlCenterError {
    ControlCenterError::NotFound(message.to_string())
}
