//! Core error types
//!
//! This module defines the core error types used throughout the framework.
//! All errors implement `std::error::Error` via the `thiserror` crate for
//! ergonomic error handling.
//!
//! # Error Categories
//!
//! - **Routing Errors**: Route not found, method not allowed, pattern errors
//! - **Request Errors**: Bad request, invalid parameters, body parsing errors
//! - **Internal Errors**: Framework bugs, unexpected states
//!
//! # Examples
//!
//! ```rust
//! use ruxno::CoreError;
//!
//! // Create routing errors
//! let error = CoreError::not_found("/api/users");
//! assert_eq!(error.status_code(), 404);
//!
//! // Create validation errors
//! let error = CoreError::bad_request("Invalid email format");
//! assert_eq!(error.status_code(), 400);
//!
//! // Pattern matching
//! match error {
//!     CoreError::NotFound(path) => println!("Path not found: {}", path),
//!     CoreError::BadRequest(msg) => println!("Bad request: {}", msg),
//!     _ => println!("Other error"),
//! }
//! ```

use thiserror::Error;

/// Core error type for the framework
///
/// This enum represents all errors that can occur in the core framework layers
/// (routing, middleware, handlers). Each variant maps to an appropriate HTTP
/// status code.
///
/// # Design
///
/// Uses `thiserror` for automatic `std::error::Error` implementation and
/// user-friendly error messages. All variants include context to aid debugging.
///
/// # Examples
///
/// ```rust
/// use ruxno::CoreError;
///
/// // Routing errors
/// let error = CoreError::not_found("/api/users");
/// assert_eq!(error.status_code(), 404);
///
/// // Request errors
/// let error = CoreError::bad_request("Missing required field: email");
/// assert_eq!(error.status_code(), 400);
///
/// // Pattern errors
/// let error = CoreError::invalid_pattern("Invalid route pattern: /user/:id:");
/// assert_eq!(error.status_code(), 500);
/// ```
#[derive(Debug, Error, Clone, PartialEq)]
pub enum CoreError {
    /// Route not found (404)
    ///
    /// Returned when no route matches the requested path and method.
    #[error("Route not found: {0}")]
    NotFound(String),

    /// Method not allowed (405)
    ///
    /// Returned when a route exists for the path but not for the HTTP method.
    #[error("Method not allowed for path: {0}")]
    MethodNotAllowed(String),

    /// Bad request (400)
    ///
    /// Returned for malformed requests, invalid parameters, or validation errors.
    #[error("Bad request: {0}")]
    BadRequest(String),

    /// Invalid route pattern (500)
    ///
    /// Returned when registering a route with an invalid pattern.
    /// This is a framework error, not a client error.
    #[error("Invalid route pattern: {0}")]
    InvalidPattern(String),

    /// Duplicate route (500)
    ///
    /// Returned when attempting to register a route that already exists.
    #[error("Duplicate route: {method} {path}")]
    DuplicateRoute {
        /// HTTP method
        method: http::Method,
        /// Route path
        path: String,
    },

    /// Missing parameter (400)
    ///
    /// Returned when a required path parameter is missing.
    #[error("Missing required parameter: {0}")]
    MissingParameter(String),

    /// Invalid parameter (400)
    ///
    /// Returned when a path parameter has an invalid value.
    #[error("Invalid parameter '{name}': {reason}")]
    InvalidParameter {
        /// Parameter name
        name: String,
        /// Reason for invalidity
        reason: String,
    },

    /// Body parsing error (400)
    ///
    /// Returned when request body cannot be parsed (JSON, form, etc.).
    #[error("Failed to parse request body: {0}")]
    BodyParseError(String),

    /// Payload too large (413)
    ///
    /// Returned when request body exceeds size limits.
    #[error("Payload too large: {0}")]
    PayloadTooLarge(String),

    /// Request header fields too large (431)
    ///
    /// Returned when request has too many headers or headers are too large.
    #[error("Request header fields too large: {0}")]
    RequestHeaderFieldsTooLarge(String),

    /// Internal error (500)
    ///
    /// Returned for unexpected framework errors or bugs.
    #[error("Internal error: {0}")]
    Internal(String),

    /// Custom error with status code
    ///
    /// Allows users to create custom errors with specific status codes and messages.
    #[error("{message}")]
    Custom {
        /// HTTP status code
        status: u16,
        /// Error message
        message: String,
    },
}

impl CoreError {
    /// Get the HTTP status code for this error
    ///
    /// Maps each error variant to its appropriate HTTP status code:
    /// - 400: Bad request, validation errors
    /// - 404: Route not found
    /// - 405: Method not allowed
    /// - 500: Internal errors, framework bugs
    ///
    /// # Examples
    ///
    /// ```rust
    /// use ruxno::CoreError;
    ///
    /// assert_eq!(CoreError::not_found("/api/users").status_code(), 404);
    /// assert_eq!(CoreError::bad_request("Invalid input").status_code(), 400);
    /// assert_eq!(CoreError::internal("Bug").status_code(), 500);
    /// ```
    pub fn status_code(&self) -> u16 {
        match self {
            CoreError::NotFound(_) => 404,
            CoreError::MethodNotAllowed(_) => 405,
            CoreError::BadRequest(_) => 400,
            CoreError::MissingParameter(_) => 400,
            CoreError::InvalidParameter { .. } => 400,
            CoreError::BodyParseError(_) => 400,
            CoreError::PayloadTooLarge(_) => 413,
            CoreError::RequestHeaderFieldsTooLarge(_) => 431,
            CoreError::InvalidPattern(_) => 500,
            CoreError::DuplicateRoute { .. } => 500,
            CoreError::Internal(_) => 500,
            CoreError::Custom { status, .. } => *status,
        }
    }

    /// Check if this is a client error (4xx)
    ///
    /// # Examples
    ///
    /// ```rust
    /// use ruxno::CoreError;
    ///
    /// assert!(CoreError::not_found("/api/users").is_client_error());
    /// assert!(CoreError::bad_request("Invalid").is_client_error());
    /// assert!(!CoreError::internal("Bug").is_client_error());
    /// ```
    pub fn is_client_error(&self) -> bool {
        let code = self.status_code();
        (400..500).contains(&code)
    }

    /// Check if this is a server error (5xx)
    ///
    /// # Examples
    ///
    /// ```rust
    /// use ruxno::CoreError;
    ///
    /// assert!(CoreError::internal("Bug").is_server_error());
    /// assert!(CoreError::invalid_pattern("Bad pattern").is_server_error());
    /// assert!(!CoreError::not_found("/api/users").is_server_error());
    /// ```
    pub fn is_server_error(&self) -> bool {
        let code = self.status_code();
        (500..600).contains(&code)
    }

    // Convenience constructors

    /// Create a "not found" error
    pub fn not_found(path: impl Into<String>) -> Self {
        CoreError::NotFound(path.into())
    }

    /// Create a "method not allowed" error
    pub fn method_not_allowed(path: impl Into<String>) -> Self {
        CoreError::MethodNotAllowed(path.into())
    }

    /// Create a "bad request" error
    pub fn bad_request(message: impl Into<String>) -> Self {
        CoreError::BadRequest(message.into())
    }

    /// Create an "invalid pattern" error
    pub fn invalid_pattern(pattern: impl Into<String>) -> Self {
        CoreError::InvalidPattern(pattern.into())
    }

    /// Create a "duplicate route" error
    pub fn duplicate_route(method: http::Method, path: impl Into<String>) -> Self {
        CoreError::DuplicateRoute {
            method,
            path: path.into(),
        }
    }

    /// Create a "missing parameter" error
    pub fn missing_parameter(name: impl Into<String>) -> Self {
        CoreError::MissingParameter(name.into())
    }

    /// Create an "invalid parameter" error
    pub fn invalid_parameter(name: impl Into<String>, reason: impl Into<String>) -> Self {
        CoreError::InvalidParameter {
            name: name.into(),
            reason: reason.into(),
        }
    }

    /// Create a "body parse error"
    pub fn body_parse_error(message: impl Into<String>) -> Self {
        CoreError::BodyParseError(message.into())
    }

    /// Create a "payload too large" error
    pub fn payload_too_large(message: impl Into<String>) -> Self {
        CoreError::PayloadTooLarge(message.into())
    }

    /// Create a "request header fields too large" error
    pub fn request_header_fields_too_large(message: impl Into<String>) -> Self {
        CoreError::RequestHeaderFieldsTooLarge(message.into())
    }

    /// Create an "internal error"
    pub fn internal(message: impl Into<String>) -> Self {
        CoreError::Internal(message.into())
    }

    /// Create a "custom error" with status code
    pub fn custom(status: u16, message: impl Into<String>) -> Self {
        CoreError::Custom {
            status,
            message: message.into(),
        }
    }
}

// Conversion from common error types

impl From<std::io::Error> for CoreError {
    fn from(err: std::io::Error) -> Self {
        CoreError::Internal(err.to_string())
    }
}

impl From<std::fmt::Error> for CoreError {
    fn from(err: std::fmt::Error) -> Self {
        CoreError::Internal(err.to_string())
    }
}

impl From<String> for CoreError {
    fn from(msg: String) -> Self {
        CoreError::Custom {
            status: 500,
            message: msg,
        }
    }
}

impl From<&str> for CoreError {
    fn from(msg: &str) -> Self {
        CoreError::Custom {
            status: 500,
            message: msg.to_string(),
        }
    }
}

/// Extension trait for converting `Result<T, CoreError>` to `Response`
///
/// This trait provides a blanket implementation for any `Result<T, CoreError>`
/// where `T` can be converted into a `Response`. This allows ergonomic error
/// handling in route handlers:
///
/// # Examples
///
/// ```rust
/// use ruxno::prelude::*;
///
/// async fn handler(ctx: Context) -> Response {
///     // Automatically converts Result<ApiResponse<User>, CoreError> to Response
///     fetch_user(ctx.param("id"))
///         .map(|user| ApiResponse::success(user))
///         .into_response()
/// }
/// ```
///
/// This works around Rust's orphan rule by defining our own trait instead of
/// trying to implement `From<Result<T, CoreError>>` for `Response` (which
/// would require owning either `Result` or `Response`).
pub trait IntoResponse {
    /// Convert this value into a `Response`
    fn into_response(self) -> crate::Response;
}

// Blanket implementation for Result<T, CoreError> where T: Into<Response>
impl<T> IntoResponse for Result<T, CoreError>
where
    T: Into<crate::Response>,
{
    fn into_response(self) -> crate::Response {
        match self {
            Ok(value) => value.into(),
            Err(error) => error.into_response(),
        }
    }
}

// Direct implementation for CoreError itself
impl IntoResponse for CoreError {
    fn into_response(self) -> crate::Response {
        let status = self.status_code();
        let json_body = serde_json::json!({
            "error": self.to_string(),
            "status": status,
        });

        crate::Response::json(&json_body)
            .map(|response| response.with_status(status))
            .unwrap_or_else(|_| {
                // Fallback if JSON serialization fails
                crate::Response::text("Internal Server Error").with_status(500)
            })
    }
}

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

    #[test]
    fn test_not_found_error() {
        let error = CoreError::not_found("/api/users");
        assert_eq!(error.status_code(), 404);
        assert!(error.is_client_error());
        assert!(!error.is_server_error());
        assert_eq!(error.to_string(), "Route not found: /api/users");
    }

    #[test]
    fn test_method_not_allowed_error() {
        let error = CoreError::method_not_allowed("/api/users");
        assert_eq!(error.status_code(), 405);
        assert!(error.is_client_error());
        assert_eq!(error.to_string(), "Method not allowed for path: /api/users");
    }

    #[test]
    fn test_bad_request_error() {
        let error = CoreError::bad_request("Invalid email format");
        assert_eq!(error.status_code(), 400);
        assert!(error.is_client_error());
        assert_eq!(error.to_string(), "Bad request: Invalid email format");
    }

    #[test]
    fn test_invalid_pattern_error() {
        let error = CoreError::invalid_pattern("/user/:id:");
        assert_eq!(error.status_code(), 500);
        assert!(error.is_server_error());
        assert!(!error.is_client_error());
        assert_eq!(error.to_string(), "Invalid route pattern: /user/:id:");
    }

    #[test]
    fn test_duplicate_route_error() {
        let error = CoreError::duplicate_route(http::Method::GET, "/api/users");
        assert_eq!(error.status_code(), 500);
        assert!(error.is_server_error());
        assert_eq!(error.to_string(), "Duplicate route: GET /api/users");
    }

    #[test]
    fn test_missing_parameter_error() {
        let error = CoreError::missing_parameter("id");
        assert_eq!(error.status_code(), 400);
        assert!(error.is_client_error());
        assert_eq!(error.to_string(), "Missing required parameter: id");
    }

    #[test]
    fn test_invalid_parameter_error() {
        let error = CoreError::invalid_parameter("id", "must be a positive integer");
        assert_eq!(error.status_code(), 400);
        assert!(error.is_client_error());
        assert_eq!(
            error.to_string(),
            "Invalid parameter 'id': must be a positive integer"
        );
    }

    #[test]
    fn test_body_parse_error() {
        let error = CoreError::body_parse_error("Invalid JSON");
        assert_eq!(error.status_code(), 400);
        assert!(error.is_client_error());
        assert_eq!(
            error.to_string(),
            "Failed to parse request body: Invalid JSON"
        );
    }

    #[test]
    fn test_internal_error() {
        let error = CoreError::internal("Unexpected state");
        assert_eq!(error.status_code(), 500);
        assert!(error.is_server_error());
        assert_eq!(error.to_string(), "Internal error: Unexpected state");
    }

    #[test]
    fn test_custom_error() {
        let error = CoreError::custom(503, "Service unavailable");
        assert_eq!(error.status_code(), 503);
        assert!(error.is_server_error());
        assert_eq!(error.to_string(), "Service unavailable");
    }

    #[test]
    fn test_from_io_error() {
        let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
        let error: CoreError = io_error.into();
        assert_eq!(error.status_code(), 500);
        assert!(error.is_server_error());
    }

    #[test]
    fn test_from_string() {
        let error: CoreError = "Something went wrong".to_string().into();
        assert_eq!(error.status_code(), 500);
        assert_eq!(error.to_string(), "Something went wrong");
    }

    #[test]
    fn test_from_str() {
        let error: CoreError = "Something went wrong".into();
        assert_eq!(error.status_code(), 500);
        assert_eq!(error.to_string(), "Something went wrong");
    }

    #[test]
    fn test_error_equality() {
        let error1 = CoreError::not_found("/api/users");
        let error2 = CoreError::not_found("/api/users");
        let error3 = CoreError::not_found("/api/posts");

        assert_eq!(error1, error2);
        assert_ne!(error1, error3);
    }

    #[test]
    fn test_error_clone() {
        let error = CoreError::bad_request("Invalid input");
        let cloned = error.clone();
        assert_eq!(error, cloned);
    }

    #[test]
    fn test_status_code_categories() {
        // Client errors (4xx)
        assert!(CoreError::not_found("/").is_client_error());
        assert!(CoreError::method_not_allowed("/").is_client_error());
        assert!(CoreError::bad_request("").is_client_error());
        assert!(CoreError::missing_parameter("id").is_client_error());
        assert!(CoreError::payload_too_large("").is_client_error());
        assert!(CoreError::request_header_fields_too_large("").is_client_error());

        // Server errors (5xx)
        assert!(CoreError::internal("").is_server_error());
        assert!(CoreError::invalid_pattern("").is_server_error());
        assert!(CoreError::duplicate_route(http::Method::GET, "/").is_server_error());
        assert!(CoreError::custom(500, "").is_server_error());
    }

    #[test]
    fn test_request_header_fields_too_large_error() {
        let error = CoreError::request_header_fields_too_large("Too many headers");
        assert_eq!(error.status_code(), 431);
        assert!(error.is_client_error());
        assert_eq!(
            error.to_string(),
            "Request header fields too large: Too many headers"
        );
    }

    #[test]
    fn test_into_response_trait_for_error() {
        use crate::Response;
        let error = CoreError::not_found("/api/users");
        let response: Response = error.into_response();
        assert_eq!(response.status(), 404);
    }

    #[test]
    fn test_into_response_trait_for_result_ok() {
        use crate::Response;

        // Mock successful result
        let result: Result<Response, CoreError> = Ok(Response::text("Success").with_status(200));

        let response = result.into_response();
        assert_eq!(response.status(), 200);
    }

    #[test]
    fn test_into_response_trait_for_result_err() {
        use crate::Response;

        // Mock error result
        let result: Result<Response, CoreError> = Err(CoreError::bad_request("Invalid input"));

        let response = result.into_response();
        assert_eq!(response.status(), 400);
    }
}
