//! UCXL Standard Response and Error Codes Library
//! 
//! This module provides standardized UCXL error and response codes for consistent
//! cross-service communication and client handling.
//! 
//! Based on UCXL-ERROR-CODES.md and UCXL-RESPONSE-CODES.md v1.0

use serde::{Serialize, Deserialize};
use std::collections::HashMap;
use chrono::{DateTime, Utc};
use uuid::Uuid;

/// Standard UCXL Error Codes following format: UCXL-<HTTP-class>-<SHORT_NAME>
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum UCXLErrorCode {
    // 400 - Client Errors
    #[serde(rename = "UCXL-400-INVALID_ADDRESS")]
    InvalidAddress,
    #[serde(rename = "UCXL-400-MISSING_FIELD")]
    MissingField,
    #[serde(rename = "UCXL-400-INVALID_FORMAT")]
    InvalidFormat,
    
    // 401 - Authentication
    #[serde(rename = "UCXL-401-UNAUTHORIZED")]
    Unauthorized,
    
    // 403 - Authorization
    #[serde(rename = "UCXL-403-FORBIDDEN")]
    Forbidden,
    
    // 404 - Not Found
    #[serde(rename = "UCXL-404-NOT_FOUND")]
    NotFound,
    
    // 409 - Conflict
    #[serde(rename = "UCXL-409-CONFLICT")]
    Conflict,
    
    // 422 - Unprocessable Entity
    #[serde(rename = "UCXL-422-UNPROCESSABLE_ENTITY")]
    UnprocessableEntity,
    
    // 429 - Rate Limiting
    #[serde(rename = "UCXL-429-RATE_LIMIT")]
    RateLimit,
    
    // 500 - Server Errors
    #[serde(rename = "UCXL-500-INTERNAL_ERROR")]
    InternalError,
    
    // 503 - Service Unavailable
    #[serde(rename = "UCXL-503-SERVICE_UNAVAILABLE")]
    ServiceUnavailable,
    
    // 504 - Gateway Timeout
    #[serde(rename = "UCXL-504-GATEWAY_TIMEOUT")]
    GatewayTimeout,
}

/// Standard UCXL Response Codes following format: UCXL-<HTTP-class>-<SHORT_NAME>
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum UCXLResponseCode {
    // 200 - Success
    #[serde(rename = "UCXL-200-OK")]
    Ok,
    
    // 201 - Created
    #[serde(rename = "UCXL-201-CREATED")]
    Created,
    
    // 202 - Accepted (Async)
    #[serde(rename = "UCXL-202-ACCEPTED")]
    Accepted,
    
    // 204 - No Content
    #[serde(rename = "UCXL-204-NO_CONTENT")]
    NoContent,
    
    // 206 - Partial Content
    #[serde(rename = "UCXL-206-PARTIAL_CONTENT")]
    PartialContent,
    
    // 304 - Not Modified (Caching)
    #[serde(rename = "UCXL-304-NOT_MODIFIED")]
    NotModified,
}

/// UCXL Standard Error Response Payload
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct UCXLErrorResponse {
    pub error: UCXLError,
}

/// UCXL Error Details
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct UCXLError {
    pub code: UCXLErrorCode,
    pub message: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub details: Option<HashMap<String, serde_json::Value>>,
    pub source: String,
    pub path: String,
    pub request_id: String,
    pub timestamp: DateTime<Utc>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cause: Option<String>,
}

/// UCXL Standard Success Response Payload
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct UCXLSuccessResponse<T = serde_json::Value> {
    pub response: UCXLResponse<T>,
}

/// UCXL Response Details
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct UCXLResponse<T = serde_json::Value> {
    pub code: UCXLResponseCode,
    pub message: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub data: Option<T>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub details: Option<HashMap<String, serde_json::Value>>,
    pub request_id: String,
    pub timestamp: DateTime<Utc>,
}

impl UCXLErrorCode {
    /// Get the HTTP status code for this error code
    pub fn http_status(&self) -> u16 {
        match self {
            Self::InvalidAddress | Self::MissingField | Self::InvalidFormat => 400,
            Self::Unauthorized => 401,
            Self::Forbidden => 403,
            Self::NotFound => 404,
            Self::Conflict => 409,
            Self::UnprocessableEntity => 422,
            Self::RateLimit => 429,
            Self::InternalError => 500,
            Self::ServiceUnavailable => 503,
            Self::GatewayTimeout => 504,
        }
    }
    
    /// Get the string representation of the error code
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::InvalidAddress => "UCXL-400-INVALID_ADDRESS",
            Self::MissingField => "UCXL-400-MISSING_FIELD",
            Self::InvalidFormat => "UCXL-400-INVALID_FORMAT",
            Self::Unauthorized => "UCXL-401-UNAUTHORIZED",
            Self::Forbidden => "UCXL-403-FORBIDDEN",
            Self::NotFound => "UCXL-404-NOT_FOUND",
            Self::Conflict => "UCXL-409-CONFLICT",
            Self::UnprocessableEntity => "UCXL-422-UNPROCESSABLE_ENTITY",
            Self::RateLimit => "UCXL-429-RATE_LIMIT",
            Self::InternalError => "UCXL-500-INTERNAL_ERROR",
            Self::ServiceUnavailable => "UCXL-503-SERVICE_UNAVAILABLE",
            Self::GatewayTimeout => "UCXL-504-GATEWAY_TIMEOUT",
        }
    }
    
    /// Get the default error message for this code
    pub fn default_message(&self) -> &'static str {
        match self {
            Self::InvalidAddress => "Invalid UCXL address format",
            Self::MissingField => "Required field is missing",
            Self::InvalidFormat => "Input does not match the expected format",
            Self::Unauthorized => "Authentication credentials missing or invalid",
            Self::Forbidden => "Insufficient permissions for the action",
            Self::NotFound => "Requested resource not found",
            Self::Conflict => "Conflict with current state",
            Self::UnprocessableEntity => "Semantic validation failed",
            Self::RateLimit => "Too many requests; rate limiting in effect",
            Self::InternalError => "Internal server error",
            Self::ServiceUnavailable => "Service is currently unavailable",
            Self::GatewayTimeout => "Downstream gateway timed out",
        }
    }

    /// Check if this is a client error (4xx)
    pub fn is_client_error(&self) -> bool {
        matches!(self.http_status(), 400..=499)
    }

    /// Check if this is a server error (5xx)
    pub fn is_server_error(&self) -> bool {
        matches!(self.http_status(), 500..=599)
    }

    /// Check if this error should trigger a retry
    pub fn should_retry(&self) -> bool {
        matches!(self, 
            Self::RateLimit | 
            Self::InternalError | 
            Self::ServiceUnavailable | 
            Self::GatewayTimeout
        )
    }
}

impl UCXLResponseCode {
    /// Get the HTTP status code for this response code
    pub fn http_status(&self) -> u16 {
        match self {
            Self::Ok => 200,
            Self::Created => 201,
            Self::Accepted => 202,
            Self::NoContent => 204,
            Self::PartialContent => 206,
            Self::NotModified => 304,
        }
    }
    
    /// Get the string representation of the response code
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Ok => "UCXL-200-OK",
            Self::Created => "UCXL-201-CREATED",
            Self::Accepted => "UCXL-202-ACCEPTED",
            Self::NoContent => "UCXL-204-NO_CONTENT",
            Self::PartialContent => "UCXL-206-PARTIAL_CONTENT",
            Self::NotModified => "UCXL-304-NOT_MODIFIED",
        }
    }
    
    /// Get the default success message for this code
    pub fn default_message(&self) -> &'static str {
        match self {
            Self::Ok => "Request completed successfully",
            Self::Created => "Resource created successfully",
            Self::Accepted => "Request accepted for processing",
            Self::NoContent => "Request completed with no content to return",
            Self::PartialContent => "Partial results returned",
            Self::NotModified => "Resource not modified since last fetch",
        }
    }

    /// Check if this indicates an asynchronous operation
    pub fn is_async(&self) -> bool {
        matches!(self, Self::Accepted)
    }

    /// Check if this indicates partial/incomplete results
    pub fn is_partial(&self) -> bool {
        matches!(self, Self::PartialContent | Self::Accepted)
    }
}

impl std::fmt::Display for UCXLErrorCode {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.as_str())
    }
}

impl std::fmt::Display for UCXLResponseCode {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.as_str())
    }
}

/// Builder for creating UCXL Error responses
pub struct UCXLErrorBuilder {
    code: UCXLErrorCode,
    message: Option<String>,
    details: HashMap<String, serde_json::Value>,
    source: String,
    path: String,
    request_id: String,
    cause: Option<String>,
}

impl UCXLErrorBuilder {
    /// Create a new error builder with the specified code
    pub fn new(code: UCXLErrorCode) -> Self {
        Self {
            code,
            message: None,
            details: HashMap::new(),
            source: "ucxl-api/v1".to_string(),
            path: "/".to_string(),
            request_id: format!("req-{}", Uuid::new_v4().simple()),
            cause: None,
        }
    }

    /// Set a custom error message
    pub fn message(mut self, message: String) -> Self {
        self.message = Some(message);
        self
    }

    /// Add details about the field that failed
    pub fn field(mut self, field: &str, provided: serde_json::Value) -> Self {
        self.details.insert("field".to_string(), field.into());
        self.details.insert("provided".to_string(), provided);
        self
    }

    /// Add expected format information
    pub fn expected_format(mut self, format: &str) -> Self {
        self.details.insert("expected_format".to_string(), format.into());
        self
    }

    /// Add a detail field
    pub fn detail(mut self, key: &str, value: serde_json::Value) -> Self {
        self.details.insert(key.to_string(), value);
        self
    }

    /// Set the source service
    pub fn source(mut self, source: &str) -> Self {
        self.source = source.to_string();
        self
    }

    /// Set the request path
    pub fn path(mut self, path: &str) -> Self {
        self.path = path.to_string();
        self
    }

    /// Set the request ID
    pub fn request_id(mut self, request_id: &str) -> Self {
        self.request_id = request_id.to_string();
        self
    }

    /// Set the error cause
    pub fn cause(mut self, cause: &str) -> Self {
        self.cause = Some(cause.to_string());
        self
    }

    /// Build the error response
    pub fn build(self) -> UCXLErrorResponse {
        UCXLErrorResponse {
            error: UCXLError {
                code: self.code,
                message: self.message.unwrap_or_else(|| self.code.default_message().to_string()),
                details: if self.details.is_empty() { None } else { Some(self.details) },
                source: self.source,
                path: self.path,
                request_id: self.request_id,
                timestamp: Utc::now(),
                cause: self.cause,
            },
        }
    }
}

/// Builder for creating UCXL Success responses
pub struct UCXLResponseBuilder<T = serde_json::Value> {
    code: UCXLResponseCode,
    message: Option<String>,
    data: Option<T>,
    details: HashMap<String, serde_json::Value>,
    request_id: String,
}

impl<T> UCXLResponseBuilder<T> {
    /// Create a new response builder with the specified code
    pub fn new(code: UCXLResponseCode) -> Self {
        Self {
            code,
            message: None,
            data: None,
            details: HashMap::new(),
            request_id: format!("req-{}", Uuid::new_v4().simple()),
        }
    }

    /// Set a custom success message
    pub fn message(mut self, message: String) -> Self {
        self.message = Some(message);
        self
    }

    /// Set the response data
    pub fn data(mut self, data: T) -> Self {
        self.data = Some(data);
        self
    }

    /// Add a detail field
    pub fn detail(mut self, key: &str, value: serde_json::Value) -> Self {
        self.details.insert(key.to_string(), value);
        self
    }

    /// Set the request ID
    pub fn request_id(mut self, request_id: &str) -> Self {
        self.request_id = request_id.to_string();
        self
    }

    /// Build the success response
    pub fn build(self) -> UCXLSuccessResponse<T> {
        UCXLSuccessResponse {
            response: UCXLResponse {
                code: self.code,
                message: self.message.unwrap_or_else(|| self.code.default_message().to_string()),
                data: self.data,
                details: if self.details.is_empty() { None } else { Some(self.details) },
                request_id: self.request_id,
                timestamp: Utc::now(),
            },
        }
    }
}

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

    #[test]
    fn test_error_code_http_status() {
        assert_eq!(UCXLErrorCode::InvalidAddress.http_status(), 400);
        assert_eq!(UCXLErrorCode::Unauthorized.http_status(), 401);
        assert_eq!(UCXLErrorCode::InternalError.http_status(), 500);
    }

    #[test]
    fn test_error_code_categorization() {
        assert!(UCXLErrorCode::InvalidAddress.is_client_error());
        assert!(!UCXLErrorCode::InvalidAddress.is_server_error());
        assert!(!UCXLErrorCode::InternalError.is_client_error());
        assert!(UCXLErrorCode::InternalError.is_server_error());
    }

    #[test]
    fn test_retry_logic() {
        assert!(!UCXLErrorCode::InvalidAddress.should_retry());
        assert!(UCXLErrorCode::RateLimit.should_retry());
        assert!(UCXLErrorCode::ServiceUnavailable.should_retry());
    }

    #[test]
    fn test_error_builder() {
        let error_response = UCXLErrorBuilder::new(UCXLErrorCode::InvalidAddress)
            .message("Test error message".to_string())
            .field("address", "ucxl://invalid".into())
            .expected_format("ucxl://<agent>:<role>@<project>:<task>")
            .source("test-service")
            .path("/test")
            .cause("parse_error")
            .build();

        assert_eq!(error_response.error.code, UCXLErrorCode::InvalidAddress);
        assert_eq!(error_response.error.message, "Test error message");
        assert!(error_response.error.details.is_some());
        assert_eq!(error_response.error.source, "test-service");
    }

    #[test]
    fn test_response_builder() {
        let response = UCXLResponseBuilder::new(UCXLResponseCode::Ok)
            .message("Test success".to_string())
            .data(serde_json::json!({"test": "value"}))
            .detail("info", "additional info".into())
            .build();

        assert_eq!(response.response.code, UCXLResponseCode::Ok);
        assert_eq!(response.response.message, "Test success");
        assert!(response.response.data.is_some());
        assert!(response.response.details.is_some());
    }

    #[test]
    fn test_serialization() {
        let error_response = UCXLErrorBuilder::new(UCXLErrorCode::InvalidAddress).build();
        let serialized = serde_json::to_string(&error_response).unwrap();
        let deserialized: UCXLErrorResponse = serde_json::from_str(&serialized).unwrap();
        assert_eq!(error_response, deserialized);
    }
}