use axum::{
  Json,
  http::StatusCode,
  response::{IntoResponse, Response},
};
use serde::{Deserialize, Serialize};
use std::fmt;

pub type Result<T> = std::result::Result<T, Error>;

#[derive(Debug, Deserialize, Serialize)]
pub struct Error {
  pub status: u16,
  pub details: String,
}

impl Error {
  pub fn new(status: u16, details: String) -> Self {
    Self { status, details }
  }

  pub fn not_found(details: String) -> Self {
    Self::new(404, details)
  }

  pub fn internal_server_error(details: String) -> Self {
    Self::new(500, details)
  }
}

impl fmt::Display for Error {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    f.write_str(self.details.as_str())
  }
}

impl std::error::Error for Error {
  fn description(&self) -> &str {
    &self.details
  }
}

impl IntoResponse for Error {
  fn into_response(self) -> Response {
    let status = StatusCode::from_u16(self.status).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
    let body = Json(serde_json::json!({
      "error": {
        "status": self.status,
        "details": self.details,
      }
    }));
    (status, body).into_response()
  }
}

// --- Conversions from upstream crate errors ---

impl From<siren_core::error::Error> for Error {
  fn from(error: siren_core::error::Error) -> Self {
    Self::new(error.status, error.details)
  }
}

impl From<siren_bot::error::Error> for Error {
  fn from(error: siren_bot::error::Error) -> Self {
    Self::new(error.status, error.details)
  }
}

// --- Conversions from external crate errors ---

impl From<StatusCode> for Error {
  fn from(status: StatusCode) -> Self {
    Error {
      status: status.as_u16(),
      details: status
        .canonical_reason()
        .unwrap_or("Unknown error")
        .to_string(),
    }
  }
}

impl From<reqwest::Error> for Error {
  fn from(error: reqwest::Error) -> Self {
    Self::new(500, format!("HTTP client error: {}", error))
  }
}

impl From<serde_json::Error> for Error {
  fn from(error: serde_json::Error) -> Self {
    Self::new(500, format!("JSON error: {}", error))
  }
}

impl From<jsonwebtoken::errors::Error> for Error {
  fn from(error: jsonwebtoken::errors::Error) -> Self {
    match error.kind() {
      jsonwebtoken::errors::ErrorKind::ExpiredSignature => {
        Self::new(401, "Token expired".to_string())
      }
      jsonwebtoken::errors::ErrorKind::InvalidToken => Self::new(401, "Invalid token".to_string()),
      _ => Self::new(500, format!("JWT error: {}", error)),
    }
  }
}

// Direct conversions for types used in API handlers that bypass the data abstraction layer

impl From<sqlx::Error> for Error {
  fn from(error: sqlx::Error) -> Self {
    let core_err: siren_core::error::Error = error.into();
    core_err.into()
  }
}

impl From<redis::RedisError> for Error {
  fn from(error: redis::RedisError) -> Self {
    let core_err: siren_core::error::Error = error.into();
    core_err.into()
  }
}

impl From<std::io::Error> for Error {
  fn from(error: std::io::Error) -> Self {
    Self::new(500, format!("IO error: {}", error))
  }
}
