//! Error handling utilities

use crate::*;
use std::collections::HashMap;

/// Application error type combining all error categories
#[derive(Debug, Clone, thiserror::Error)]
pub enum AppError {
    /// Routing errors
    #[error("Routing error: {0}")]
    Routing(#[from] RoutingError),

    /// Component errors
    #[error("Component error: {0}")]
    Component(#[from] ComponentError),

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

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

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

/// Error information for user display
pub struct UserErrorInfo {
    /// Error code for programmatic handling
    pub code: String,
    /// User-friendly message
    pub message: String,
    /// Detailed error information for debugging
    pub details: HashMap<String, String>,
    /// Language for the error message
    pub language: String,
}

impl UserErrorInfo {
    /// Create new user error info
    pub fn new(code: String, message: String, language: String) -> Self {
        Self {
            code,
            message,
            details: HashMap::new(),
            language,
        }
    }

    /// Add detail information
    pub fn with_detail(mut self, key: String, value: String) -> Self {
        self.details.insert(key, value);
        self
    }
}

/// Convert errors to user-friendly information
pub trait ToUserError {
    /// Convert to user error info with localization support
    fn to_user_error(&self, language: &str) -> UserErrorInfo;
}

impl ToUserError for RoutingError {
    fn to_user_error(&self, language: &str) -> UserErrorInfo {
        match self {
            RoutingError::NotFound(path) => {
                let message = match language {
                    "es" => format!("La página '{}' no fue encontrada", path),
                    _ => format!("Page '{}' was not found", path),
                };
                UserErrorInfo::new(
                    "ROUTE_NOT_FOUND".to_string(),
                    message,
                    language.to_string(),
                ).with_detail("path".to_string(), path.clone())
            }
            RoutingError::InvalidRoute(route) => {
                let message = match language {
                    "es" => format!("Ruta inválida: '{}'", route),
                    _ => format!("Invalid route: '{}'", route),
                };
                UserErrorInfo::new(
                    "INVALID_ROUTE".to_string(),
                    message,
                    language.to_string(),
                ).with_detail("route".to_string(), route.clone())
            }
            RoutingError::RenderError(msg) => {
                let message = match language {
                    "es" => "Error al renderizar la página",
                    _ => "Failed to render page",
                };
                UserErrorInfo::new(
                    "RENDER_ERROR".to_string(),
                    message.to_string(),
                    language.to_string(),
                ).with_detail("technical_error".to_string(), msg.clone())
            }
            RoutingError::MetadataError(msg) => {
                let message = match language {
                    "es" => "Error al cargar metadatos de la página",
                    _ => "Failed to load page metadata",
                };
                UserErrorInfo::new(
                    "METADATA_ERROR".to_string(),
                    message.to_string(),
                    language.to_string(),
                ).with_detail("technical_error".to_string(), msg.clone())
            }
        }
    }
}

impl ToUserError for ComponentError {
    fn to_user_error(&self, language: &str) -> UserErrorInfo {
        match self {
            ComponentError::NotFound(component) => {
                let message = match language {
                    "es" => format!("Componente '{}' no encontrado", component),
                    _ => format!("Component '{}' not found", component),
                };
                UserErrorInfo::new(
                    "COMPONENT_NOT_FOUND".to_string(),
                    message,
                    language.to_string(),
                ).with_detail("component".to_string(), component.clone())
            }
            ComponentError::RenderError(msg) => {
                let message = match language {
                    "es" => "Error al renderizar el componente",
                    _ => "Failed to render component",
                };
                UserErrorInfo::new(
                    "COMPONENT_RENDER_ERROR".to_string(),
                    message.to_string(),
                    language.to_string(),
                ).with_detail("technical_error".to_string(), msg.clone())
            }
            ComponentError::LayoutError(msg) => {
                let message = match language {
                    "es" => "Error de diseño",
                    _ => "Layout error",
                };
                UserErrorInfo::new(
                    "LAYOUT_ERROR".to_string(),
                    message.to_string(),
                    language.to_string(),
                ).with_detail("technical_error".to_string(), msg.clone())
            }
            ComponentError::ContentError(msg) => {
                let message = match language {
                    "es" => "Error al cargar contenido",
                    _ => "Failed to load content",
                };
                UserErrorInfo::new(
                    "CONTENT_ERROR".to_string(),
                    message.to_string(),
                    language.to_string(),
                ).with_detail("technical_error".to_string(), msg.clone())
            }
        }
    }
}

impl ToUserError for AppError {
    fn to_user_error(&self, language: &str) -> UserErrorInfo {
        match self {
            AppError::Routing(err) => err.to_user_error(language),
            AppError::Component(err) => err.to_user_error(language),
            AppError::Config(msg) => {
                let message = match language {
                    "es" => "Error de configuración",
                    _ => "Configuration error",
                };
                UserErrorInfo::new(
                    "CONFIG_ERROR".to_string(),
                    message.to_string(),
                    language.to_string(),
                ).with_detail("technical_error".to_string(), msg.clone())
            }
            AppError::Network(msg) => {
                let message = match language {
                    "es" => "Error de conexión",
                    _ => "Network error",
                };
                UserErrorInfo::new(
                    "NETWORK_ERROR".to_string(),
                    message.to_string(),
                    language.to_string(),
                ).with_detail("technical_error".to_string(), msg.clone())
            }
            AppError::Serialization(msg) => {
                let message = match language {
                    "es" => "Error de datos",
                    _ => "Data processing error",
                };
                UserErrorInfo::new(
                    "SERIALIZATION_ERROR".to_string(),
                    message.to_string(),
                    language.to_string(),
                ).with_detail("technical_error".to_string(), msg.clone())
            }
        }
    }
}

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

    #[test]
    fn test_routing_error_conversion() {
        let error = RoutingError::NotFound("/test".to_string());
        let user_error = error.to_user_error("en");

        assert_eq!(user_error.code, "ROUTE_NOT_FOUND");
        assert!(user_error.message.contains("not found"));
        assert_eq!(user_error.language, "en");
        assert_eq!(user_error.details.get("path"), Some(&"/test".to_string()));
    }

    #[test]
    fn test_spanish_localization() {
        let error = RoutingError::NotFound("/test".to_string());
        let user_error = error.to_user_error("es");

        assert_eq!(user_error.code, "ROUTE_NOT_FOUND");
        assert!(user_error.message.contains("no fue encontrada"));
        assert_eq!(user_error.language, "es");
    }

    #[test]
    fn test_component_error_conversion() {
        let error = ComponentError::NotFound("TestComponent".to_string());
        let user_error = error.to_user_error("en");

        assert_eq!(user_error.code, "COMPONENT_NOT_FOUND");
        assert!(user_error.message.contains("not found"));
        assert_eq!(user_error.details.get("component"), Some(&"TestComponent".to_string()));
    }

    #[test]
    fn test_app_error_conversion() {
        let routing_error = RoutingError::NotFound("/test".to_string());
        let app_error = AppError::Routing(routing_error);
        let user_error = app_error.to_user_error("en");

        assert_eq!(user_error.code, "ROUTE_NOT_FOUND");
        assert!(user_error.message.contains("not found"));
    }
}
