//! Error types for AT-Jet

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

/// Result type alias for AT-Jet operations
pub type Result<T> = std::result::Result<T, JetError>;

/// Error types for AT-Jet
#[derive(Error, Debug)]
pub enum JetError {
  /// Failed to decode protobuf message
  #[error("Failed to decode protobuf: {0}")]
  ProtobufDecode(#[from] prost::DecodeError),

  /// Failed to encode protobuf message
  #[error("Failed to encode protobuf: {0}")]
  ProtobufEncode(#[from] prost::EncodeError),

  /// Invalid content type
  #[error("Invalid content type: expected {expected}, got {actual}")]
  InvalidContentType { expected: String, actual: String },

  /// Missing content type header
  #[error("Missing Content-Type header")]
  MissingContentType,

  /// Request body too large
  #[error("Request body too large: {size} bytes (max: {max})")]
  BodyTooLarge { size: usize, max: usize },

  /// HTTP client error
  #[error("HTTP client error: {0}")]
  HttpClient(#[from] reqwest::Error),

  /// JSON serialization/deserialization error
  #[error("JSON error: {0}")]
  Json(#[from] serde_json::Error),

  /// IO error
  #[error("IO error: {0}")]
  Io(#[from] std::io::Error),

  /// Server bind error
  #[error("Failed to bind server: {0}")]
  ServerBind(String),

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

  /// Bad request
  #[error("Bad request: {0}")]
  BadRequest(String),

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

  /// Unauthorized
  #[error("Unauthorized: {0}")]
  Unauthorized(String),

  /// Forbidden
  #[error("Forbidden: {0}")]
  Forbidden(String),
}

impl JetError {
  /// Get HTTP status code for this error
  pub fn status_code(&self) -> StatusCode {
    match self {
      | JetError::ProtobufDecode(_) => StatusCode::BAD_REQUEST,
      | JetError::ProtobufEncode(_) => StatusCode::INTERNAL_SERVER_ERROR,
      | JetError::InvalidContentType { .. } => StatusCode::UNSUPPORTED_MEDIA_TYPE,
      | JetError::MissingContentType => StatusCode::BAD_REQUEST,
      | JetError::BodyTooLarge { .. } => StatusCode::PAYLOAD_TOO_LARGE,
      | JetError::HttpClient(_) => StatusCode::BAD_GATEWAY,
      | JetError::Json(_) => StatusCode::BAD_REQUEST,
      | JetError::Io(_) => StatusCode::INTERNAL_SERVER_ERROR,
      | JetError::ServerBind(_) => StatusCode::INTERNAL_SERVER_ERROR,
      | JetError::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR,
      | JetError::BadRequest(_) => StatusCode::BAD_REQUEST,
      | JetError::NotFound(_) => StatusCode::NOT_FOUND,
      | JetError::Unauthorized(_) => StatusCode::UNAUTHORIZED,
      | JetError::Forbidden(_) => StatusCode::FORBIDDEN,
    }
  }
}

impl IntoResponse for JetError {
  fn into_response(self) -> Response {
    let status = self.status_code();
    let body = self.to_string();

    (status, body).into_response()
  }
}
