use std::{any::Any, time::Duration};

use async_trait::async_trait;

use crate::{
    debug_log,
    network::{
        access_token_source::AccessTokenSource,
        core_network_service::CoreNetworkService,
        network_error::NetworkError,
        network_manager::{NETWORK_CALL_ATTEMPTS_NUMBER_UNCRITICAL, NetworkManagerImpl},
        query_builder::{QueryBuilder, QueryParam},
        request::{HttpMethod, RequestBuilder},
        response::Response,
        url_provider::UrlProvider,
    },
};

#[async_trait]
#[cfg_attr(test, mockall::automock)]
pub(crate) trait AutomationApiNetworkManager: Send + Sync + Any {
    async fn fetch_access_jwt(&self, client_id: &str, client_secret: &str) -> Result<Response, NetworkError>;
}

const GRANT_TYPE: &str = "client_credentials";
const CONTENT_TYPE: &str = "application/x-www-form-urlencoded";

#[async_trait]
impl<C, U, A> AutomationApiNetworkManager for NetworkManagerImpl<C, U, A>
where
    C: CoreNetworkService,
    U: UrlProvider,
    A: AccessTokenSource,
{
    async fn fetch_access_jwt(&self, client_id: &str, client_secret: &str) -> Result<Response, NetworkError> {
        //~ [42126]
        debug_log!("CALL NetworkManagerImpl::fetch_access_jwt()");
        let body = QueryBuilder::with_params(vec![
            (QueryParam::GrantType, GRANT_TYPE.into()),
            (QueryParam::ClientId, client_id.into()),
            (QueryParam::ClientSecret, client_secret.into()),
        ])
        .build();
        let req_builder = RequestBuilder::default()
            .method(HttpMethod::POST)
            .url(self.url_provider.make_access_token_url())
            .header((reqwest::header::CONTENT_TYPE.as_str(), CONTENT_TYPE.to_owned()))
            .body(body.into_bytes())
            .redact_body(true);
        let result =
            self.call(req_builder, None, NETWORK_CALL_ATTEMPTS_NUMBER_UNCRITICAL, Duration::ZERO, (false, false)).await;
        debug_log!("RET NetworkManagerImpl::fetch_access_jwt() → {:?}", result);
        result
    }
}
