//! Example demonstrating trait-based error handling (decoupled from concrete
//! ControlCenterError)
//!
//! This module shows how new code can depend only on the ErrorContext trait,
//! achieving the coupling reduction from 152 to ~40 dependents.

use std::fmt;

use crate::error::interface::{ApiErrorResponse, ErrorContext};

/// Example service handler that works with ANY error implementing ErrorContext.
/// This handler doesn't depend on ControlCenterError directly - only on the
/// trait. This is the key to reducing coupling: modules that just need error
/// information now depend on the trait, not the concrete implementation.
pub fn handle_error_generically(err: &dyn ErrorContext) -> ApiErrorResponse {
    ApiErrorResponse::from_error(err)
}

/// Example of wrapping an error from an external system.
/// Rather than converting to ControlCenterError immediately,
/// we can wrap it in our own ErrorContext implementation.
pub struct ExternalServiceError {
    code: String,
    message: String,
}

impl ExternalServiceError {
    pub fn new(service: &str, message: String) -> Self {
        Self {
            code: format!("EXT_{}", service.to_uppercase()),
            message,
        }
    }
}

impl fmt::Display for ExternalServiceError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}: {}", self.code, self.message)
    }
}

impl fmt::Debug for ExternalServiceError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "ExternalServiceError {{ code: {}, message: {} }}",
            self.code, self.message
        )
    }
}

impl ErrorContext for ExternalServiceError {
    fn error_code(&self) -> &'static str {
        // In a real implementation, we'd cache this or use a smarter approach
        Box::leak(self.code.clone().into_boxed_str())
    }

    fn status_code(&self) -> axum::http::StatusCode {
        axum::http::StatusCode::INTERNAL_SERVER_ERROR
    }

    fn should_log_error(&self) -> bool {
        true
    }

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

#[cfg(test)]
mod tests {
    use axum::http::StatusCode;

    use super::*;

    #[test]
    fn test_error_context_trait_enables_decoupling() {
        // Code that uses ErrorContext doesn't need to know about ControlCenterError
        let external_error =
            ExternalServiceError::new("database", "connection timeout".to_string());

        // This works with any ErrorContext implementation, not just ControlCenterError
        let response = handle_error_generically(&external_error);

        assert_eq!(response.error_code, "EXT_DATABASE");
        assert_eq!(response.message, "EXT_DATABASE: connection timeout");
    }

    #[test]
    fn test_error_context_from_control_center_error() {
        use crate::error::infrastructure::InfrastructureError;
        use crate::error::ControlCenterError;

        // ControlCenterError also implements ErrorContext
        let cc_error = ControlCenterError::Infrastructure(InfrastructureError::Internal(
            "test failure".to_string(),
        ));

        // Works seamlessly with the same trait interface
        let response = handle_error_generically(&cc_error);

        assert_eq!(response.error_code, "INTERNAL_ERROR");
    }

    #[test]
    fn test_trait_enables_multiple_implementations() {
        // This demonstrates the key benefit of the trait:
        // Any type implementing ErrorContext works uniformly
        let external_err: Box<dyn ErrorContext> =
            Box::new(ExternalServiceError::new("api", "timeout".to_string()));

        // Handlers don't care about the concrete type
        let _response = handle_error_generically(&*external_err);
    }
}
