use std::sync::PoisonError;

/// Error enum for kproc
#[derive(thiserror::Error, Debug)]
#[allow(missing_docs)]
#[non_exhaustive]
pub enum Error
{
  #[cfg(feature = "simple-api")]
  #[error("Serialization error: {0}")]
  JsonSerializationError(#[from] serde_json::Error),
  #[error("SendError in streams communication, most likely end of stream: {0}.")]
  SendError(String),
  #[error("IO Error: {0}.")]
  IOError(#[from] std::io::Error),
  #[error("Deserialization error: {0}")]
  DeserializationError(String),
  #[cfg(feature = "ollama")]
  #[error("Ollama error: {0}")]
  OllamaError(#[from] ollama_rs::error::OllamaError),
  #[error("Poison (Mutex/RwLock) error: {0}")]
  PoisonError(String),
  #[error("{0}")]
  FuturesMpscSendError(#[from] futures::channel::mpsc::SendError),
  #[cfg(feature = "image")]
  #[error("{0}")]
  ValueError(#[from] kproc_values::Error),
  #[cfg(feature = "llama.cpp")]
  #[error("{0}")]
  HfApiError(#[from] hf_hub::api::sync::ApiError),
  #[cfg(feature = "candle")]
  #[error("{0}")]
  HfTokioApiError(#[from] hf_hub::api::tokio::ApiError),
  #[error("Invalid HugginFace uri.")]
  HfInvalidUri,
  #[cfg(feature = "llama.cpp")]
  #[error("{0}")]
  LlamaCpp(#[from] llama_cpp_2::LLamaCppError),
  #[cfg(feature = "llama.cpp")]
  #[error("{0}")]
  LlamaModelLoad(#[from] llama_cpp_2::LlamaModelLoadError),
  #[cfg(feature = "llama.cpp")]
  #[error("{0}")]
  LlamaContextLoad(#[from] llama_cpp_2::LlamaContextLoadError),
  #[cfg(feature = "llama.cpp")]
  #[error("{0}")]
  LlamaStringToToken(#[from] llama_cpp_2::StringToTokenError),
  #[cfg(feature = "llama.cpp")]
  #[error("{0}")]
  LlamaBatchAddError(#[from] llama_cpp_2::llama_batch::BatchAddError),
  #[cfg(feature = "llama.cpp")]
  #[error("{0}")]
  LlamaDecodeError(#[from] llama_cpp_2::DecodeError),
  #[cfg(feature = "llama.cpp")]
  #[error("{0}")]
  LlamaTokenToString(#[from] llama_cpp_2::TokenToStringError),
  #[cfg(feature = "template")]
  #[error("{0}")]
  MinijinjaError(#[from] minijinja::Error),
  #[cfg(feature = "llama.cpp")]
  #[error("{0}")]
  CCUtilsServerError(#[from] ccutils::servers::ServerError),
  #[cfg(feature = "llama.cpp")]
  #[error("Invalid JSON Grammar")]
  InvalidJsonGrammar,
  #[cfg(feature = "simple-api")]
  #[error("HTTP Request error: {0}")]
  HttpError(String),
  #[cfg(feature = "simple-api")]
  #[error("Error in API usage: code: {code} message: {message} type: {error_type}")]
  SimpleApiError
  {
    code: u32,
    message: String,
    error_type: String,
  },
  #[cfg(feature = "candle")]
  #[error("Error in candle: {0}")]
  CandleError(#[from] candle_core::Error),
  #[error("Model was not properly defined.")]
  UndefinedModel,
  #[error("Unknwon end-of-stream token '{0}'.")]
  UnknownEndOfStream(String),
  #[error("Other error: {0}")]
  Other(String),
}

impl<T> From<PoisonError<T>> for Error
{
  fn from(value: PoisonError<T>) -> Self
  {
    Error::PoisonError(value.to_string())
  }
}

impl From<Box<dyn std::error::Error + Send + Sync>> for Error
{
  fn from(err: Box<dyn std::error::Error + Send + Sync>) -> Self
  {
    Error::Other(err.to_string())
  }
}
