Documentation
use http_api_client_endpoint::{
    http::{Error as HttpError, StatusCode},
    Body, Response,
};
use serde::de::DeserializeOwned;
use serde_json::Error as SerdeJsonError;
use url::ParseError as UrlParseError;

use crate::objects::oauth::ResponseErrorBody;

//
//
//
#[derive(Debug, Clone)]
pub enum EndpointRet<T>
where
    T: core::fmt::Debug + Clone,
{
    Ok(T),
    Other((StatusCode, Result<ResponseErrorBody, Body>)),
}

//
//
//
#[derive(Debug)]
pub enum EndpointError {
    MakeRequestUrlFailed(UrlParseError),
    MakeRequestFailed(HttpError),
    DeResponseBodyFailed(SerdeJsonError),
}
impl core::fmt::Display for EndpointError {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "{self:?}")
    }
}
impl std::error::Error for EndpointError {}

//
//
//
pub fn endpoint_parse_response<T>(response: Response<Body>) -> Result<EndpointRet<T>, EndpointError>
where
    T: core::fmt::Debug + Clone + DeserializeOwned,
{
    let status = response.status();
    #[allow(clippy::single_match)]
    match status {
        StatusCode::OK => {
            use crate::objects::oauth::Message;

            #[allow(clippy::single_match)]
            match serde_json::from_slice::<Message>(response.body()) {
                Ok(Message::Success) => {
                    let ok_json = serde_json::from_slice::<T>(response.body())
                        .map_err(EndpointError::DeResponseBodyFailed)?;

                    return Ok(EndpointRet::Ok(ok_json));
                }
                _ => {}
            }
        }
        _ => {}
    }

    match serde_json::from_slice::<ResponseErrorBody>(response.body()) {
        Ok(err_json) => Ok(EndpointRet::Other((status, Ok(err_json)))),
        Err(_) => Ok(EndpointRet::Other((
            status,
            Err(response.body().to_owned()),
        ))),
    }
}

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

    #[test]
    fn test_endpoint_parse_response() -> Result<(), Box<dyn std::error::Error>> {
        let resp_body = include_str!("../../../tests/response_body_files/oauth/refresh_token__err_with_expired_refresh_token.json");
        let resp = Response::builder()
            .status(StatusCode::OK)
            .body(resp_body.as_bytes().to_vec())?;

        match endpoint_parse_response::<()>(resp) {
            Ok(EndpointRet::Other((status_code, Ok(err_body)))) => {
                assert_eq!(status_code, StatusCode::OK);
                assert_eq!(err_body.data.error_code, 10010);
            }
            x => panic!("{x:?}"),
        }

        Ok(())
    }
}