use axum::{
    Json,
    http::{HeaderMap, HeaderValue, StatusCode},
    response::{IntoResponse, Response},
};
use thiserror::Error;

use crate::cache::REFLEX_STATUS_HEADER;
use crate::scoring::ScoringError;

#[derive(Debug, Error)]
pub enum GatewayError {
    #[error("invalid request: {0}")]
    InvalidRequest(String),

    #[error("cache lookup failed: {0}")]
    CacheLookupFailed(String),

    #[error("scoring failed: {0}")]
    ScoringFailed(#[from] ScoringError),

    #[error("provider error: {0}")]
    ProviderError(String),

    #[error("serialization failed: {0}")]
    SerializationFailed(String),

    #[error("storage error: {0}")]
    StorageError(String),

    #[error("embedding failed: {0}")]
    EmbeddingFailed(String),

    #[error("internal error: {0}")]
    InternalError(String),
}

#[derive(serde::Serialize)]
pub struct ErrorResponse {
    pub error: String,
    pub code: u16,
}

impl IntoResponse for GatewayError {
    fn into_response(self) -> Response {
        let (status, error_message, reflex_status) = match &self {
            GatewayError::InvalidRequest(_) => {
                (StatusCode::BAD_REQUEST, self.to_string(), "invalid_request")
            }
            GatewayError::CacheLookupFailed(_) => (
                StatusCode::INTERNAL_SERVER_ERROR,
                self.to_string(),
                "lookup_error",
            ),
            GatewayError::ScoringFailed(_) => (
                StatusCode::INTERNAL_SERVER_ERROR,
                self.to_string(),
                "scoring_error",
            ),
            GatewayError::ProviderError(_) => {
                (StatusCode::BAD_GATEWAY, self.to_string(), "provider_error")
            }
            GatewayError::SerializationFailed(_) => (
                StatusCode::INTERNAL_SERVER_ERROR,
                self.to_string(),
                "serialization_error",
            ),
            GatewayError::StorageError(_) => (
                StatusCode::INTERNAL_SERVER_ERROR,
                self.to_string(),
                "storage_error",
            ),
            GatewayError::EmbeddingFailed(_) => (
                StatusCode::INTERNAL_SERVER_ERROR,
                self.to_string(),
                "embedding_error",
            ),
            GatewayError::InternalError(_) => (
                StatusCode::INTERNAL_SERVER_ERROR,
                self.to_string(),
                "internal_error",
            ),
        };

        let mut headers = HeaderMap::new();
        headers.insert(
            REFLEX_STATUS_HEADER,
            HeaderValue::from_str(reflex_status).unwrap_or(HeaderValue::from_static("error")),
        );

        let body = Json(ErrorResponse {
            error: error_message,
            code: status.as_u16(),
        });

        (status, headers, body).into_response()
    }
}
