Documentation
use crate::{
  http::status::StatusCode,
  parse::{AutoParse, CAutoParse, ParseWebResult},
};
use serde::{Deserialize, Serialize};

use std::{
  any::Any,
  borrow::Cow,
  collections::HashMap,
  ffi::{c_char, CStr, CString},
};

/// 返回的标准接口
#[derive(Serialize, Debug, Clone, Deserialize)]
pub struct WebResult<T> {
  /// 编码
  pub code: u16,
  /// 编码消息
  pub msg: Cow<'static, str>,
  /// 主体
  pub data: T,
}
impl<T: Serialize> WebResult<T> {
  /// 转换成WebResult再序列化
  pub fn to_s(res: &Result<T>) -> Result<String> {
    Ok(serde_json::to_string(&res.res_web()?)?)
  }
}
impl From<&'static str> for Error {
  fn from(s: &'static str) -> Self {
    Self::Str(Cow::Borrowed(s))
  }
}
impl From<String> for Error {
  fn from(s: String) -> Self {
    Self::Str(Cow::Owned(s))
  }
}

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

impl<T> From<Result<T>> for CResult<T> {
  fn from(result: Result<T>) -> Self {
    match result {
      Result::Ok(value) => CResult::Ok(value),
      Result::Err(error) => CResult::Err(CError::cstr_err(error.to_string())),
    }
  }
}
impl<T> From<CResult<T>> for Result<T> {
  fn from(result: CResult<T>) -> Self {
    match result {
      CResult::Ok(value) => Result::Ok(value),
      CResult::Err(error) => Result::Err(Error::String(
        unsafe { CStr::from_ptr(error.to_string()) }
          .to_str()
          .unwrap_or_default()
          .to_string(),
      )),
    }
  }
}

/// 未处理std::ops::Try ?
#[repr(C)]
#[derive(Debug)]
pub enum CResult<T> {
  ///
  Ok(T),
  ///
  Err(CError),
}
/// Http share error
#[derive(thiserror::Error, Debug)]
pub enum Error {
  /// BadRequest `400 Bad Request`
  #[error("400 拒绝请求")]
  BadRequest,
  /// 已存在
  #[error("Exists: {0}")]
  Exists(Cow<'static, str>),
  /// SerdeJson `400 Bad Request`
  #[error("400 SerdeJson错误: {0}")]
  SerdeJson(#[from] serde_json::Error),
  /// Result `400 Bad Request`
  #[error("Str: {0}")]
  Str(Cow<'static, str>),
  ///
  #[error("String: {0}")]
  String(String),
  /// Excel `400 Bad Request`
  #[error("regex error: {0}")]
  Regex(#[from] regex::Error),
  /// Return `401 Unauthorized`
  #[error("authentication required: {0}")]
  Unauthorized(&'static str),
  /// Return `403 Forbidden`
  #[error("user may not perform that action")]
  Forbidden,
  /// Return `404 Not Found`
  #[error("request path not found: {0}")]
  NotFound(Cow<'static, str>),
  /// Return `404 Not Found`
  #[error("Option<{0}>")]
  Option(Cow<'static, str>),
  /// 不支持
  #[error("Not support: {0}")]
  Unsupport(Cow<'static, str>),
  /// Return `422 Unprocessable Entity`
  #[error("error in the request body: {errors:?}")]
  UnprocessableEntity {
    ///
    errors: HashMap<Cow<'static, str>, Vec<Cow<'static, str>>>,
  },
  ///
  #[error("TryFromSlice: {0}")]
  TryFromSlice(#[from] std::array::TryFromSliceError),
  /// IO return `500 INTERNAL_SERVER_ERROR`
  #[error("an error occurred with the std io: {0}")]
  Io(#[from] std::io::Error),
  /// Env
  #[error("env var error : {0}")]
  Env(#[from] std::env::VarError),
  ///
  #[error("Empty")]
  Empty,
  /// Std error return `500 INTERNAL_SERVER_ERROR`
  #[error("an error occurred with the std Error: {0}")]
  Std(#[from] Box<dyn std::error::Error + Send + Sync>),
  /// Parse int error return 400
  #[error("an error occurred with the parse Error: {0}")]
  ParseNumber(#[from] std::num::ParseIntError),
  /// Parse float error return 400
  #[error("an error occurred with the parse Error: {0}")]
  ParseFloatError(#[from] std::num::ParseFloatError),
  /// Return `502 Internal Server Error` on an `anyhow::Error`.
  #[error("Log: {0}")]
  Log(Cow<'static, str>),
  /// Nul
  #[error("CString: {0}")]
  Nul(#[from] std::ffi::NulError),
  /// Any
  #[error("Any Error: ")]
  Any {
    ///
    err: Box<dyn std::any::Any + Send + Sync>,
  },
}
impl Error {
  /// Convenient constructor for `Self::UnprocessableEntity`.
  pub fn unprocessable_entity<K, V>(errors: impl IntoIterator<Item = (K, V)>) -> Self
  where
    K: Into<Cow<'static, str>>,
    V: Into<Cow<'static, str>>,
  {
    Self::UnprocessableEntity {
      errors: errors
        .into_iter()
        .map(|(key, val)| (key.into(), vec![val.into()]))
        .collect(),
    }
  }
  /// 任何类型
  pub fn any(err: Box<dyn Any + Send + Sync>) -> Self {
    Self::Any { err }
  }
  /// 状态码获取
  pub fn status_code(&self) -> StatusCode {
    match self {
      Self::BadRequest
      | Self::Str(..)
      | Self::SerdeJson(..)
      | Self::ParseNumber(_)
      | Self::Regex(_)
      | Self::Env(_)
      | Self::Log(_)
      | Self::String(_)
      | Self::Exists(_)
      | Self::Empty
      | Self::Nul(_)
      | Self::Option(_)
      | Self::TryFromSlice(_)
      | Self::Unsupport(_)
      | Self::ParseFloatError(_) => StatusCode::BAD_REQUEST,
      Self::Unauthorized(_) => StatusCode::UNAUTHORIZED,
      Self::Forbidden => StatusCode::FORBIDDEN,
      Self::NotFound(_) => StatusCode::NOT_FOUND,
      Self::UnprocessableEntity { .. } => StatusCode::UNPROCESSABLE_ENTITY,
      Self::Io(_) | Self::Std(_) | Self::Any { .. } => StatusCode::INTERNAL_SERVER_ERROR,
    }
  }
}

/// Http share error
#[repr(C)]
#[derive(thiserror::Error, Debug)]
pub enum CError {
  /// BadRequest `400 Bad Request`
  #[error("C 400 拒绝请求")]
  BadRequest,
  /// Result `400 Bad Request`
  #[error("C Str: ")]
  Str(*const c_char),
  /// 不支持
  #[error("C Not Support: ")]
  Unsupport(*const c_char),
}
impl CError {
  /// C str
  pub fn cstr<S: AsRef<str>>(s: S) -> *const c_char {
    CString::new(s.as_ref()).unwrap_or_default().into_raw()
  }
  /// str to CError
  pub fn cstr_err<S: AsRef<str>>(s: S) -> CError {
    Self::Str(Self::cstr(s))
  }
  /// 状态码获取
  pub fn status_code(&self) -> StatusCode {
    match self {
      Self::BadRequest | Self::Str(..) | Self::Unsupport(_) => StatusCode::BAD_REQUEST,
    }
  }
}
impl CError {
  /// 序列化
  pub unsafe extern "C" fn to_string(&self) -> *const c_char {
    match self {
      Self::BadRequest => format!("C BadRequest"),
      Self::Str(x) => format!("C Str<{}>", x.c_to_string()),
      Self::Unsupport(x) => format!("C Unsupport<{}>", x.c_to_string()),
    }
    .to_c_char()
  }
}