use anyhow::anyhow;
use axum::{
    Json,
    response::{IntoResponse, Response},
};
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
pub struct Resp<T> {
    pub error: Option<String>,
    pub data: Option<T>,
}

impl<T> Resp<T> {
    pub fn data(data: T) -> Json<Self> {
        Json(Resp {
            error: None,
            data: Some(data),
        })
    }
}

impl<String> Resp<String> {
    pub fn msg<T: Into<String>>(data: T) -> Json<Self> {
        Json(Resp {
            error: None,
            data: Some(data.into()),
        })
    }
}

impl Resp<()> {
    pub fn fail<E: ToString>(err: E) -> Json<Self> {
        Json(Resp {
            error: Some(err.to_string()),
            data: None,
        })
    }
}

pub struct RError(anyhow::Error);

impl RError {
    pub fn new<T: Into<String>>(t: T) -> Self {
        Self(anyhow!(t.into()))
    }
}

impl IntoResponse for RError {
    fn into_response(self) -> Response {
        Resp::fail(self.0).into_response()
    }
}

impl<E> From<E> for RError
where
    E: Into<anyhow::Error>,
{
    fn from(value: E) -> Self {
        Self(value.into())
    }
}

pub type JsonRes<T> = Result<Json<Resp<T>>, RError>;

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_data() {
        #[derive(Debug)]
        struct A {
            _a: String,
        }

        let res = Resp::data(A { _a: "a".into() });
        println!("{:?}", res);
    }

    #[test]
    fn test_msg() {
        let res = Resp::<String>::msg("ok");
        println!("{:?}", res);
    }

    #[test]
    fn test_fail() {
        let res = Resp::fail(std::io::ErrorKind::NotFound);
        println!("{:?}", res);
    }
}
