// Copyright 2024 RustFS Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Swift error types and responses

use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use std::fmt;

/// Swift-specific error type
#[derive(Debug)]
pub enum SwiftError {
    /// 400 Bad Request
    BadRequest(String),
    /// 401 Unauthorized
    Unauthorized(String),
    /// 403 Forbidden
    Forbidden(String),
    /// 404 Not Found
    NotFound(String),
    /// 409 Conflict
    Conflict(String),
    /// 411 Length Required
    LengthRequired(String),
    /// 413 Request Entity Too Large (Payload Too Large)
    RequestEntityTooLarge(String),
    /// 422 Unprocessable Entity
    UnprocessableEntity(String),
    /// 429 Too Many Requests
    TooManyRequests { retry_after: u64, limit: u32, reset: u64 },
    /// 500 Internal Server Error
    InternalServerError(String),
    /// 501 Not Implemented
    NotImplemented(String),
    /// 503 Service Unavailable
    ServiceUnavailable(String),
}

impl fmt::Display for SwiftError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            SwiftError::BadRequest(msg) => write!(f, "Bad Request: {}", msg),
            SwiftError::Unauthorized(msg) => write!(f, "Unauthorized: {}", msg),
            SwiftError::Forbidden(msg) => write!(f, "Forbidden: {}", msg),
            SwiftError::NotFound(msg) => write!(f, "Not Found: {}", msg),
            SwiftError::Conflict(msg) => write!(f, "Conflict: {}", msg),
            SwiftError::LengthRequired(msg) => write!(f, "Length Required: {}", msg),
            SwiftError::RequestEntityTooLarge(msg) => write!(f, "Request Entity Too Large: {}", msg),
            SwiftError::UnprocessableEntity(msg) => write!(f, "Unprocessable Entity: {}", msg),
            SwiftError::TooManyRequests { retry_after, .. } => {
                write!(f, "Too Many Requests: retry after {} seconds", retry_after)
            }
            SwiftError::InternalServerError(msg) => write!(f, "Internal Server Error: {}", msg),
            SwiftError::NotImplemented(msg) => write!(f, "Not Implemented: {}", msg),
            SwiftError::ServiceUnavailable(msg) => write!(f, "Service Unavailable: {}", msg),
        }
    }
}

impl std::error::Error for SwiftError {}

impl SwiftError {
    fn status_code(&self) -> StatusCode {
        match self {
            SwiftError::BadRequest(_) => StatusCode::BAD_REQUEST,
            SwiftError::Unauthorized(_) => StatusCode::UNAUTHORIZED,
            SwiftError::Forbidden(_) => StatusCode::FORBIDDEN,
            SwiftError::NotFound(_) => StatusCode::NOT_FOUND,
            SwiftError::Conflict(_) => StatusCode::CONFLICT,
            SwiftError::LengthRequired(_) => StatusCode::LENGTH_REQUIRED,
            SwiftError::RequestEntityTooLarge(_) => StatusCode::PAYLOAD_TOO_LARGE,
            SwiftError::UnprocessableEntity(_) => StatusCode::UNPROCESSABLE_ENTITY,
            SwiftError::TooManyRequests { .. } => StatusCode::TOO_MANY_REQUESTS,
            SwiftError::InternalServerError(_) => StatusCode::INTERNAL_SERVER_ERROR,
            SwiftError::NotImplemented(_) => StatusCode::NOT_IMPLEMENTED,
            SwiftError::ServiceUnavailable(_) => StatusCode::SERVICE_UNAVAILABLE,
        }
    }

    fn generate_trans_id() -> String {
        use std::time::{SystemTime, UNIX_EPOCH};
        let timestamp = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap_or_else(|_| std::time::Duration::from_secs(0))
            .as_micros();
        format!("tx{:x}", timestamp)
    }
}

impl IntoResponse for SwiftError {
    fn into_response(self) -> Response {
        let trans_id = Self::generate_trans_id();
        let status = self.status_code();

        // Handle TooManyRequests specially to include rate limit headers
        if let SwiftError::TooManyRequests {
            retry_after,
            limit,
            reset,
        } = &self
        {
            return (
                status,
                [
                    ("content-type", "text/plain; charset=utf-8".to_string()),
                    ("x-trans-id", trans_id.clone()),
                    ("x-openstack-request-id", trans_id),
                    ("x-ratelimit-limit", limit.to_string()),
                    ("x-ratelimit-remaining", "0".to_string()),
                    ("x-ratelimit-reset", reset.to_string()),
                    ("retry-after", retry_after.to_string()),
                ],
                self.to_string(),
            )
                .into_response();
        }

        let body = self.to_string();

        (
            status,
            [
                ("content-type", "text/plain; charset=utf-8"),
                ("x-trans-id", trans_id.as_str()),
                ("x-openstack-request-id", trans_id.as_str()),
            ],
            body,
        )
            .into_response()
    }
}

/// Result type for Swift operations
pub type SwiftResult<T> = Result<T, SwiftError>;
