//! Control Center Error System
//!
//! Domain-specific error hierarchies with a unified facade for API responses.
//!
//! # Architecture
//!
//! ```text
//! ┌─────────────────────────────────────────────┐
//! │      ControlCenterError (Facade)            │
//! ├─────────────────────────────────────────────┤
//! │                                             │
//! │  ┌──────────┐  ┌──────────┐  ┌──────────┐ │
//! │  │   Auth   │  │ Database │  │  Policy  │ │
//! │  └────┬─────┘  └────┬─────┘  └────┬─────┘ │
//! │       │             │             │        │
//! │  ┌────┴─────┐  ┌────┴─────┐               │
//! │  │   Http   │  │  Infra   │               │
//! │  └──────────┘  └──────────┘               │
//! │                                             │
//! └─────────────────────────────────────────────┘
//! ```

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

// Domain-specific error modules
pub mod auth;
pub mod database;
pub mod http;
pub mod infrastructure;
pub mod policy;

// Error trait interface (stable public API, reduces coupling)
pub mod interface;
pub use interface::{ApiErrorResponse, ErrorContext};

// Example demonstrating trait-based error handling
#[cfg(test)]
pub mod trait_usage_example;

// Re-export domain errors for direct usage
pub use auth::AuthError;
pub use database::DatabaseError;
pub use http::HttpError;
pub use infrastructure::InfrastructureError;
pub use policy::PolicyError;

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

/// Main error facade aggregating all domain-specific errors
#[derive(Error, Debug)]
pub enum ControlCenterError {
    /// Authentication and authorization errors
    #[error(transparent)]
    Auth(#[from] AuthError),

    /// Database errors
    #[error(transparent)]
    Database(#[from] DatabaseError),

    /// Policy and compliance errors
    #[error(transparent)]
    Policy(#[from] PolicyError),

    /// HTTP and validation errors
    #[error(transparent)]
    Http(#[from] HttpError),

    /// Infrastructure and system errors
    #[error(transparent)]
    Infrastructure(#[from] InfrastructureError),

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

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

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

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

    /// Analysis errors
    #[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::Auth(e) => e.status_code(),
            Self::Database(e) => e.status_code(),
            Self::Policy(e) => e.status_code(),
            Self::Http(e) => e.status_code(),
            Self::Infrastructure(e) => e.status_code(),
            Self::WebSocket(_) => StatusCode::BAD_REQUEST,
            Self::SessionNotFound(_) | Self::PermissionNotFound(_) | Self::UserNotFound(_) => {
                StatusCode::NOT_FOUND
            }
            Self::AnalysisFailed(_) => StatusCode::INTERNAL_SERVER_ERROR,
            Self::Other(_) => StatusCode::INTERNAL_SERVER_ERROR,
        }
    }

    /// Get the error code string for API responses
    pub fn error_code(&self) -> &'static str {
        match self {
            Self::Auth(e) => e.error_code(),
            Self::Database(e) => e.error_code(),
            Self::Policy(e) => e.error_code(),
            Self::Http(e) => e.error_code(),
            Self::Infrastructure(e) => e.error_code(),
            Self::WebSocket(_) => "WEBSOCKET_ERROR",
            Self::SessionNotFound(_) => "SESSION_NOT_FOUND",
            Self::PermissionNotFound(_) => "PERMISSION_NOT_FOUND",
            Self::UserNotFound(_) => "USER_NOT_FOUND",
            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::Auth(_) => false, // Client errors logged at WARN
            Self::Database(e) => e.should_log_error(),
            Self::Policy(e) => e.should_log_error(),
            Self::Http(_) => false, // Client errors logged at WARN
            Self::Infrastructure(e) => e.should_log_error(),
            Self::WebSocket(_) => false, // Client errors logged at WARN
            Self::SessionNotFound(_) | Self::PermissionNotFound(_) | Self::UserNotFound(_) => false, /* Client errors */
            Self::AnalysisFailed(_) => true, // Server errors
            Self::Other(_) => true,
        }
    }
}

/// 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()
    }
}

// Manual Clone implementation to handle anyhow::Error which doesn't implement
// Clone
impl Clone for ControlCenterError {
    fn clone(&self) -> Self {
        match self {
            Self::Auth(e) => Self::Auth(e.clone()),
            Self::Database(e) => Self::Database(e.clone()),
            Self::Policy(e) => Self::Policy(e.clone()),
            Self::Http(e) => Self::Http(e.clone()),
            Self::Infrastructure(e) => Self::Infrastructure(e.clone()),
            Self::WebSocket(msg) => Self::WebSocket(msg.clone()),
            Self::SessionNotFound(id) => Self::SessionNotFound(id.clone()),
            Self::PermissionNotFound(id) => Self::PermissionNotFound(id.clone()),
            Self::UserNotFound(id) => Self::UserNotFound(id.clone()),
            Self::AnalysisFailed(msg) => Self::AnalysisFailed(msg.clone()),
            // For anyhow::Error, convert to string to make it cloneable
            Self::Other(e) => Self::Other(anyhow::anyhow!("{}", e)),
        }
    }
}

// Implement stable ErrorContext trait for decoupled error handling
impl ErrorContext for ControlCenterError {
    fn error_code(&self) -> &'static str {
        // Delegate to existing method
        ControlCenterError::error_code(self)
    }

    fn status_code(&self) -> StatusCode {
        // Delegate to existing method
        ControlCenterError::status_code(self)
    }

    fn should_log_error(&self) -> bool {
        // Delegate to existing method
        ControlCenterError::should_log_error(self)
    }

    fn details(&self) -> Option<String> {
        match self {
            Self::Auth(e) => Some(format!("{:?}", e)),
            Self::Database(e) => Some(format!("{:?}", e)),
            Self::Policy(e) => Some(format!("{:?}", e)),
            Self::Http(e) => Some(format!("{:?}", e)),
            Self::Infrastructure(e) => Some(format!("{:?}", e)),
            Self::WebSocket(msg) => Some(msg.clone()),
            Self::SessionNotFound(id) => Some(format!("Session ID: {}", id)),
            Self::PermissionNotFound(id) => Some(format!("Permission ID: {}", id)),
            Self::UserNotFound(id) => Some(format!("User ID: {}", id)),
            Self::AnalysisFailed(msg) => Some(msg.clone()),
            Self::Other(e) => Some(format!("{}", e)),
        }
    }

    fn clone_boxed(&self) -> Box<dyn ErrorContext> {
        Box::new(self.clone())
    }
}

// Additional From implementations for external error types
impl From<surrealdb::Error> for ControlCenterError {
    fn from(error: surrealdb::Error) -> Self {
        ControlCenterError::Database(DatabaseError::Database(error.to_string()))
    }
}

impl From<std::io::Error> for ControlCenterError {
    fn from(error: std::io::Error) -> Self {
        ControlCenterError::Infrastructure(InfrastructureError::External(error.to_string()))
    }
}

impl From<serde_json::Error> for ControlCenterError {
    fn from(error: serde_json::Error) -> Self {
        ControlCenterError::Infrastructure(InfrastructureError::Configuration(format!(
            "JSON serialization error: {}",
            error
        )))
    }
}

// ============================================================================
// Backward Compatibility - Deprecated Helper Functions
// ============================================================================

#[deprecated(
    since = "1.1.0",
    note = "Use auth::user_not_found() instead. Will be removed in v2.0"
)]
pub fn user_not_found(identifier: &str) -> ControlCenterError {
    auth::user_not_found(identifier).into()
}

#[deprecated(
    since = "1.1.0",
    note = "Use auth::session_not_found() instead. Will be removed in v2.0"
)]
pub fn session_not_found(session_id: &str) -> ControlCenterError {
    auth::session_not_found(session_id).into()
}

#[deprecated(
    since = "1.1.0",
    note = "Use auth::session_expired() instead. Will be removed in v2.0"
)]
pub fn session_expired(session_id: &str) -> ControlCenterError {
    auth::session_expired(session_id).into()
}

#[deprecated(
    since = "1.1.0",
    note = "Use auth::unauthorized() instead. Will be removed in v2.0"
)]
pub fn unauthorized(message: &str) -> ControlCenterError {
    auth::unauthorized(message).into()
}

#[deprecated(
    since = "1.1.0",
    note = "Use auth::forbidden() instead. Will be removed in v2.0"
)]
pub fn forbidden(message: &str) -> ControlCenterError {
    auth::forbidden(message).into()
}

#[deprecated(
    since = "1.1.0",
    note = "Use http::bad_request() instead. Will be removed in v2.0"
)]
pub fn bad_request(message: &str) -> ControlCenterError {
    http::bad_request(message).into()
}

#[deprecated(
    since = "1.1.0",
    note = "Use http::conflict() instead. Will be removed in v2.0"
)]
pub fn conflict(message: &str) -> ControlCenterError {
    http::conflict(message).into()
}

#[deprecated(
    since = "1.1.0",
    note = "Use infrastructure::internal_error() instead. Will be removed in v2.0"
)]
pub fn internal_error(message: &str) -> ControlCenterError {
    infrastructure::internal_error(message).into()
}

#[deprecated(
    since = "1.1.0",
    note = "Use http::not_found() instead. Will be removed in v2.0"
)]
pub fn not_found(message: &str) -> ControlCenterError {
    http::not_found(message).into()
}

/// Helper macro for creating specific error types
#[macro_export]
macro_rules! control_center_error {
    // Auth domain
    (Authentication, $msg:expr) => {
        $crate::error::ControlCenterError::Auth($crate::error::auth::AuthError::Authentication(
            $msg.to_string(),
        ))
    };
    (Authorization, $msg:expr) => {
        $crate::error::ControlCenterError::Auth($crate::error::auth::AuthError::Authorization(
            $msg.to_string(),
        ))
    };

    // Database domain
    (Database, $msg:expr) => {
        $crate::error::ControlCenterError::Database(
            $crate::error::database::DatabaseError::Database($msg.to_string()),
        )
    };

    // HTTP domain
    (Validation, $msg:expr) => {
        $crate::error::ControlCenterError::Http($crate::error::http::HttpError::Validation(
            $msg.to_string(),
        ))
    };
    (BadRequest, $msg:expr) => {
        $crate::error::ControlCenterError::Http($crate::error::http::HttpError::BadRequest(
            $msg.to_string(),
        ))
    };

    // Infrastructure domain
    (Internal, $msg:expr) => {
        $crate::error::ControlCenterError::Infrastructure(
            $crate::error::infrastructure::InfrastructureError::Internal($msg.to_string()),
        )
    };
    (Configuration, $msg:expr) => {
        $crate::error::ControlCenterError::Infrastructure(
            $crate::error::infrastructure::InfrastructureError::Configuration($msg.to_string()),
        )
    };

    // Policy domain
    (PolicyEvaluation, $msg:expr) => {
        $crate::error::ControlCenterError::Policy($crate::error::policy::PolicyError::Evaluation(
            $msg.to_string(),
        ))
    };
    (Compliance, $msg:expr) => {
        $crate::error::ControlCenterError::Policy($crate::error::policy::PolicyError::Compliance(
            $msg.to_string(),
        ))
    };
}
