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 From<siren_core::error::Error> for Error {
  fn from(error: siren_core::error::Error) -> Self {
    Self::new(error.status, error.details)
  }
}

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

impl From<songbird::error::JoinError> for Error {
  fn from(error: songbird::error::JoinError) -> Self {
    use std::error::Error as StdError;
    let details = match error.source() {
      Some(source) => format!("Unable to join channel: {} ({})", error, source),
      None => format!("Unable to join channel: {}", error),
    };
    Self::new(500, details)
  }
}

impl From<songbird::tracks::ControlError> for Error {
  fn from(error: songbird::tracks::ControlError) -> Self {
    Self::new(500, format!("Track control error: {}", error))
  }
}

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

impl From<std::string::FromUtf8Error> for Error {
  fn from(error: std::string::FromUtf8Error) -> Self {
    Self::new(500, format!("UTF-8 error: {}", error))
  }
}

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))
  }
}
