Documentation
use http_api_client_endpoint::{
    http::{
        header::{ACCEPT, USER_AGENT},
        Method,
    },
    Body, Endpoint, Request, Response,
};
use serde::{Deserialize, Serialize};
use url::Url;

use super::common::{endpoint_parse_response, EndpointError, EndpointRet};
use crate::objects::oauth::{AccessToken, Message};

//
pub const URL: &str = "https://open-api.tiktok.com/oauth/refresh_token/";
pub const GRANT_TYPE: &str = "refresh_token";

//
#[derive(Debug, Clone)]
pub struct RefreshTokenEndpoint {
    pub client_key: String,
    pub refresh_token: String,
}
impl RefreshTokenEndpoint {
    pub fn new(client_key: impl AsRef<str>, refresh_token: impl AsRef<str>) -> Self {
        Self {
            client_key: client_key.as_ref().into(),
            refresh_token: refresh_token.as_ref().into(),
        }
    }
}

impl Endpoint for RefreshTokenEndpoint {
    type RenderRequestError = EndpointError;

    type ParseResponseOutput = EndpointRet<RefreshTokenResponseBody>;
    type ParseResponseError = EndpointError;

    fn render_request(&self) -> Result<Request<Body>, Self::RenderRequestError> {
        let mut url = Url::parse(URL).map_err(EndpointError::MakeRequestUrlFailed)?;
        url.query_pairs_mut()
            .append_pair("client_key", &self.client_key)
            .append_pair("grant_type", GRANT_TYPE)
            .append_pair("refresh_token", &self.refresh_token);

        let request = Request::builder()
            .method(Method::POST)
            .uri(url.as_str())
            .header(USER_AGENT, "tiktok-api")
            .header(ACCEPT, "application/json")
            .body(vec![])
            .map_err(EndpointError::MakeRequestFailed)?;

        Ok(request)
    }

    fn parse_response(
        &self,
        response: Response<Body>,
    ) -> Result<Self::ParseResponseOutput, Self::ParseResponseError> {
        endpoint_parse_response(response)
    }
}

//
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RefreshTokenResponseBody {
    pub data: AccessToken,
    pub message: Message,
}

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

    #[test]
    fn test_render_request() {
        let req = RefreshTokenEndpoint::new("KEY", "TOKEN")
            .render_request()
            .unwrap();
        assert_eq!(req.method(), Method::POST);
        assert_eq!(req.uri(), "https://open-api.tiktok.com/oauth/refresh_token/?client_key=KEY&grant_type=refresh_token&refresh_token=TOKEN");
    }

    #[test]
    fn test_de_response_body() {
        match serde_json::from_str::<RefreshTokenResponseBody>(include_str!(
            "../../../tests/response_body_files/oauth/refresh_token.json"
        )) {
            Ok(ok_json) => {
                assert_eq!(ok_json.data.open_id, "_000fwZ23Mw4RY9cB4lDQyKCgQg4Ft6SyTuE");
                assert_eq!(ok_json.message, Message::Success);
            }
            x => panic!("{x:?}"),
        }
    }
}