//! Error types for the StemeDB API.

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

use crate::dto::ErrorResponse;

/// API-level errors.
#[derive(Debug, Error)]
pub enum ApiError {
    /// Invalid hex encoding in request.
    #[error("Invalid hex encoding: {0}")]
    InvalidHex(String),

    /// Hash has incorrect byte length.
    #[error("Invalid hash length: expected {expected}, got {actual}")]
    InvalidHashLength {
        /// Expected length in bytes.
        expected: usize,
        /// Actual length in bytes.
        actual: usize,
    },

    /// Write-ahead log error.
    #[error("WAL error: {0}")]
    Wal(#[from] stemedb_wal::QuarantineError),

    /// Storage backend error.
    #[error("Storage error: {0}")]
    Storage(#[from] stemedb_storage::StorageError),

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

    /// Ingestion pipeline error.
    #[error("Ingest error: {0}")]
    Ingest(#[from] stemedb_ingest::error::IngestError),

    /// Query execution error.
    #[error("Query error: {0}")]
    Query(#[from] stemedb_query::QueryError),

    /// Invalid request parameters.
    #[error("Invalid request: {0}")]
    InvalidRequest(String),

    /// Resource not found.
    #[error("Not found: {0}")]
    NotFound(String),

    /// Resource conflict (e.g., already superseded).
    #[error("Conflict: {0}")]
    Conflict(String),

    /// Internal server error.
    #[error("Internal error: {0}")]
    Internal(String),

    /// Missing or invalid API key.
    #[error("Unauthorized: {0}")]
    Unauthorized(String),

    /// Valid API key but insufficient permissions.
    #[error("Forbidden: {0}")]
    Forbidden(String),

    /// Rate limit exceeded.
    #[error("Rate limit exceeded: {0}")]
    RateLimited(String),
}

impl IntoResponse for ApiError {
    fn into_response(self) -> Response {
        let (status, code, message) = match self {
            ApiError::InvalidHex(ref msg) => (StatusCode::BAD_REQUEST, "INVALID_HEX", msg.clone()),
            ApiError::InvalidHashLength { .. } => {
                (StatusCode::BAD_REQUEST, "INVALID_HASH_LENGTH", self.to_string())
            }
            ApiError::InvalidRequest(ref msg) => {
                (StatusCode::BAD_REQUEST, "INVALID_REQUEST", msg.clone())
            }
            ApiError::NotFound(ref msg) => (StatusCode::NOT_FOUND, "NOT_FOUND", msg.clone()),
            ApiError::Wal(_) => (StatusCode::INTERNAL_SERVER_ERROR, "WAL_ERROR", self.to_string()),
            ApiError::Storage(_) => {
                (StatusCode::INTERNAL_SERVER_ERROR, "STORAGE_ERROR", self.to_string())
            }
            ApiError::Serialization(_) => {
                (StatusCode::INTERNAL_SERVER_ERROR, "SERIALIZATION_ERROR", self.to_string())
            }
            ApiError::Ingest(_) => {
                (StatusCode::INTERNAL_SERVER_ERROR, "INGEST_ERROR", self.to_string())
            }
            ApiError::Query(_) => {
                (StatusCode::INTERNAL_SERVER_ERROR, "QUERY_ERROR", self.to_string())
            }
            ApiError::Conflict(ref msg) => (StatusCode::CONFLICT, "CONFLICT", msg.clone()),
            ApiError::Internal(ref msg) => {
                (StatusCode::INTERNAL_SERVER_ERROR, "INTERNAL_ERROR", msg.clone())
            }
            ApiError::Unauthorized(ref msg) => {
                (StatusCode::UNAUTHORIZED, "UNAUTHORIZED", msg.clone())
            }
            ApiError::Forbidden(ref msg) => (StatusCode::FORBIDDEN, "FORBIDDEN", msg.clone()),
            ApiError::RateLimited(ref msg) => {
                (StatusCode::TOO_MANY_REQUESTS, "RATE_LIMITED", msg.clone())
            }
        };

        let error_response = ErrorResponse { error: message, code: code.to_string() };

        (status, Json(error_response)).into_response()
    }
}

/// Result type for API operations.
pub type Result<T> = std::result::Result<T, ApiError>;
