use super::*;

#[cfg(feature = "_async")]
use super::retry::retry_method_async;
#[cfg(feature = "_blocking")]
use super::retry::retry_method_blocking;

/// Raw Telegram API calling layer for async clients.
#[cfg(feature = "_async")]
#[derive(Clone)]
pub struct RawApi {
    client: Client,
}

#[cfg(feature = "_async")]
impl RawApi {
    pub(crate) fn new(client: Client) -> Self {
        Self { client }
    }

    /// Calls any Telegram method with JSON payload.
    pub async fn call_json<R, P>(&self, method: &str, payload: &P) -> Result<R>
    where
        R: DeserializeOwned,
        P: Serialize + ?Sized,
    {
        self.client.call_method(method, payload).await
    }

    /// Calls JSON method with method-scoped retry policy.
    pub async fn call_json_with_retry<R, P>(
        &self,
        method: &str,
        payload: &P,
        retry: RetryConfig,
    ) -> Result<R>
    where
        R: DeserializeOwned,
        P: Serialize + ?Sized,
    {
        retry_method_async(method, &retry, || async {
            self.client
                .call_method_without_transport_retry(method, payload)
                .await
        })
        .await
    }

    /// Calls any Telegram method without payload.
    pub async fn call_no_params<R>(&self, method: &str) -> Result<R>
    where
        R: DeserializeOwned,
    {
        self.client.call_method_no_params(method).await
    }

    /// Calls no-params method with method-scoped retry policy.
    pub async fn call_no_params_with_retry<R>(&self, method: &str, retry: RetryConfig) -> Result<R>
    where
        R: DeserializeOwned,
    {
        retry_method_async(method, &retry, || async {
            self.client
                .call_method_no_params_without_transport_retry(method)
                .await
        })
        .await
    }

    /// Calls any Telegram method with a multipart file part.
    pub async fn call_multipart<R, P>(
        &self,
        method: &str,
        payload: &P,
        file_field_name: &str,
        file: &UploadFile,
    ) -> Result<R>
    where
        R: DeserializeOwned,
        P: Serialize + ?Sized,
    {
        self.client
            .call_method_multipart(method, payload, file_field_name, file)
            .await
    }

    /// Calls any Telegram method with multiple multipart file parts.
    pub async fn call_multipart_files<R, P>(
        &self,
        method: &str,
        payload: &P,
        skip_fields: &[&str],
        files: &[UploadPart],
    ) -> Result<R>
    where
        R: DeserializeOwned,
        P: Serialize + ?Sized,
    {
        self.client
            .call_method_multipart_files(method, payload, skip_fields, files)
            .await
    }

    /// Calls multipart method with method-scoped retry policy.
    pub async fn call_multipart_with_retry<R, P>(
        &self,
        method: &str,
        payload: &P,
        file_field_name: &str,
        file: &UploadFile,
        retry: RetryConfig,
    ) -> Result<R>
    where
        R: DeserializeOwned,
        P: Serialize + ?Sized,
    {
        retry_method_async(method, &retry, || async {
            self.client
                .call_method_multipart_without_transport_retry(
                    method,
                    payload,
                    file_field_name,
                    file,
                )
                .await
        })
        .await
    }

    /// Calls multi-file multipart method with method-scoped retry policy.
    pub async fn call_multipart_files_with_retry<R, P>(
        &self,
        method: &str,
        payload: &P,
        skip_fields: &[&str],
        files: &[UploadPart],
        retry: RetryConfig,
    ) -> Result<R>
    where
        R: DeserializeOwned,
        P: Serialize + ?Sized,
    {
        retry_method_async(method, &retry, || async {
            self.client
                .call_method_multipart_files_without_transport_retry(
                    method,
                    payload,
                    skip_fields,
                    files,
                )
                .await
        })
        .await
    }
}

/// Raw Telegram API calling layer for blocking clients.
#[cfg(feature = "_blocking")]
#[derive(Clone)]
pub struct BlockingRawApi {
    client: BlockingClient,
}

#[cfg(feature = "_blocking")]
impl BlockingRawApi {
    pub(crate) fn new(client: BlockingClient) -> Self {
        Self { client }
    }

    /// Calls any Telegram method with JSON payload.
    pub fn call_json<R, P>(&self, method: &str, payload: &P) -> Result<R>
    where
        R: DeserializeOwned,
        P: Serialize + ?Sized,
    {
        self.client.call_method(method, payload)
    }

    /// Calls JSON method with method-scoped retry policy.
    pub fn call_json_with_retry<R, P>(
        &self,
        method: &str,
        payload: &P,
        retry: RetryConfig,
    ) -> Result<R>
    where
        R: DeserializeOwned,
        P: Serialize + ?Sized,
    {
        retry_method_blocking(method, &retry, || {
            self.client
                .call_method_without_transport_retry(method, payload)
        })
    }

    /// Calls any Telegram method without payload.
    pub fn call_no_params<R>(&self, method: &str) -> Result<R>
    where
        R: DeserializeOwned,
    {
        self.client.call_method_no_params(method)
    }

    /// Calls no-params method with method-scoped retry policy.
    pub fn call_no_params_with_retry<R>(&self, method: &str, retry: RetryConfig) -> Result<R>
    where
        R: DeserializeOwned,
    {
        retry_method_blocking(method, &retry, || {
            self.client
                .call_method_no_params_without_transport_retry(method)
        })
    }

    /// Calls any Telegram method with a multipart file part.
    pub fn call_multipart<R, P>(
        &self,
        method: &str,
        payload: &P,
        file_field_name: &str,
        file: &UploadFile,
    ) -> Result<R>
    where
        R: DeserializeOwned,
        P: Serialize + ?Sized,
    {
        self.client
            .call_method_multipart(method, payload, file_field_name, file)
    }

    /// Calls any Telegram method with multiple multipart file parts.
    pub fn call_multipart_files<R, P>(
        &self,
        method: &str,
        payload: &P,
        skip_fields: &[&str],
        files: &[UploadPart],
    ) -> Result<R>
    where
        R: DeserializeOwned,
        P: Serialize + ?Sized,
    {
        self.client
            .call_method_multipart_files(method, payload, skip_fields, files)
    }

    /// Calls multipart method with method-scoped retry policy.
    pub fn call_multipart_with_retry<R, P>(
        &self,
        method: &str,
        payload: &P,
        file_field_name: &str,
        file: &UploadFile,
        retry: RetryConfig,
    ) -> Result<R>
    where
        R: DeserializeOwned,
        P: Serialize + ?Sized,
    {
        retry_method_blocking(method, &retry, || {
            self.client.call_method_multipart_without_transport_retry(
                method,
                payload,
                file_field_name,
                file,
            )
        })
    }

    /// Calls multi-file multipart method with method-scoped retry policy.
    pub fn call_multipart_files_with_retry<R, P>(
        &self,
        method: &str,
        payload: &P,
        skip_fields: &[&str],
        files: &[UploadPart],
        retry: RetryConfig,
    ) -> Result<R>
    where
        R: DeserializeOwned,
        P: Serialize + ?Sized,
    {
        retry_method_blocking(method, &retry, || {
            self.client
                .call_method_multipart_files_without_transport_retry(
                    method,
                    payload,
                    skip_fields,
                    files,
                )
        })
    }
}
