<?php

declare(strict_types=1);

/*
 * PaypalServerSdkLib
 *
 * This file was automatically generated by APIMATIC v3.0 ( https://www.apimatic.io ).
 */

namespace PaypalServerSdkLib;

use Core\ClientBuilder;
use Core\Utils\CoreHelper;
use PaypalServerSdkLib\Authentication\ClientCredentialsAuthCredentialsBuilder;
use PaypalServerSdkLib\Authentication\ClientCredentialsAuthManager;
use PaypalServerSdkLib\Controllers\OAuthAuthorizationController;
use PaypalServerSdkLib\Controllers\OrdersController;
use PaypalServerSdkLib\Controllers\PaymentsController;
use PaypalServerSdkLib\Controllers\SubscriptionsController;
use PaypalServerSdkLib\Controllers\TransactionSearchController;
use PaypalServerSdkLib\Controllers\VaultController;
use PaypalServerSdkLib\Logging\LoggingConfigurationBuilder;
use PaypalServerSdkLib\Logging\RequestLoggingConfigurationBuilder;
use PaypalServerSdkLib\Logging\ResponseLoggingConfigurationBuilder;
use PaypalServerSdkLib\Proxy\ProxyConfigurationBuilder;
use PaypalServerSdkLib\Utils\CompatibilityConverter;
use Unirest\Configuration;
use Unirest\HttpClient;

class PaypalServerSdkClient implements ConfigurationInterface
{
    private $orders;

    private $payments;

    private $vault;

    private $transactionSearch;

    private $subscriptions;

    private $oAuthAuthorization;

    private $clientCredentialsAuthManager;

    private $loggingConfigurationBuilder;

    private $proxyConfiguration;

    private $config;

    private $client;

    /**
     * @see PaypalServerSdkClientBuilder::init()
     * @see PaypalServerSdkClientBuilder::build()
     *
     * @param array $config
     */
    public function __construct(array $config = [])
    {
        $this->config = array_merge(ConfigurationDefaults::_ALL, CoreHelper::clone($config));
        $this->clientCredentialsAuthManager = new ClientCredentialsAuthManager($this->config);
        $loggingConfiguration = null;
        if ($this->config['loggingConfiguration'] instanceof LoggingConfigurationBuilder) {
            $this->loggingConfigurationBuilder = $this->config['loggingConfiguration'];
            $loggingConfiguration = $this->loggingConfigurationBuilder->build();
        }
        $this->proxyConfiguration = $this->config['proxyConfiguration'] ?? ConfigurationDefaults::PROXY_CONFIGURATION;
        $this->client = ClientBuilder::init(
            new HttpClient(Configuration::init($this)->proxyConfiguration($this->proxyConfiguration))
        )
            ->converter(new CompatibilityConverter())
            ->jsonHelper(ApiHelper::getJsonHelper())
            ->apiCallback($this->config['httpCallback'] ?? null)
            ->userAgent('PayPal REST API PHP SDK, Version: 2.0.0, on OS {os-info}')
            ->serverUrls(self::ENVIRONMENT_MAP[$this->getEnvironment()], Server::DEFAULT_)
            ->authManagers(['Oauth2' => $this->clientCredentialsAuthManager])
            ->loggingConfiguration($loggingConfiguration)
            ->build();
        $this->clientCredentialsAuthManager->setClient($this->client);
    }

    /**
     * Create a builder with the current client's configurations.
     *
     * @return PaypalServerSdkClientBuilder PaypalServerSdkClientBuilder instance
     */
    public function toBuilder(): PaypalServerSdkClientBuilder
    {
        $builder = PaypalServerSdkClientBuilder::init()
            ->timeout($this->getTimeout())
            ->enableRetries($this->shouldEnableRetries())
            ->numberOfRetries($this->getNumberOfRetries())
            ->retryInterval($this->getRetryInterval())
            ->backOffFactor($this->getBackOffFactor())
            ->maximumRetryWaitTime($this->getMaximumRetryWaitTime())
            ->retryOnTimeout($this->shouldRetryOnTimeout())
            ->httpStatusCodesToRetry($this->getHttpStatusCodesToRetry())
            ->httpMethodsToRetry($this->getHttpMethodsToRetry())
            ->environment($this->getEnvironment())
            ->httpCallback($this->config['httpCallback'] ?? null)
            ->proxyConfiguration($this->getProxyConfigurationBuilder());

        $clientCredentialsAuth = $this->getClientCredentialsAuthCredentialsBuilder();
        if ($clientCredentialsAuth != null) {
            $builder->clientCredentialsAuthCredentials($clientCredentialsAuth);
        }
        $loggingConfigurationBuilder = $this->getLoggingConfigurationBuilder();
        if ($loggingConfigurationBuilder != null) {
            $builder->loggingConfiguration($loggingConfigurationBuilder);
        }
        return $builder;
    }

    public function getTimeout(): int
    {
        return $this->config['timeout'] ?? ConfigurationDefaults::TIMEOUT;
    }

    public function shouldEnableRetries(): bool
    {
        return $this->config['enableRetries'] ?? ConfigurationDefaults::ENABLE_RETRIES;
    }

    public function getNumberOfRetries(): int
    {
        return $this->config['numberOfRetries'] ?? ConfigurationDefaults::NUMBER_OF_RETRIES;
    }

    public function getRetryInterval(): float
    {
        return $this->config['retryInterval'] ?? ConfigurationDefaults::RETRY_INTERVAL;
    }

    public function getBackOffFactor(): float
    {
        return $this->config['backOffFactor'] ?? ConfigurationDefaults::BACK_OFF_FACTOR;
    }

    public function getMaximumRetryWaitTime(): int
    {
        return $this->config['maximumRetryWaitTime'] ?? ConfigurationDefaults::MAXIMUM_RETRY_WAIT_TIME;
    }

    public function shouldRetryOnTimeout(): bool
    {
        return $this->config['retryOnTimeout'] ?? ConfigurationDefaults::RETRY_ON_TIMEOUT;
    }

    public function getHttpStatusCodesToRetry(): array
    {
        return $this->config['httpStatusCodesToRetry'] ?? ConfigurationDefaults::HTTP_STATUS_CODES_TO_RETRY;
    }

    public function getHttpMethodsToRetry(): array
    {
        return $this->config['httpMethodsToRetry'] ?? ConfigurationDefaults::HTTP_METHODS_TO_RETRY;
    }

    public function getEnvironment(): string
    {
        return $this->config['environment'] ?? ConfigurationDefaults::ENVIRONMENT;
    }

    public function getClientCredentialsAuth(): ClientCredentialsAuth
    {
        return $this->clientCredentialsAuthManager;
    }

    public function getClientCredentialsAuthCredentialsBuilder(): ?ClientCredentialsAuthCredentialsBuilder
    {
        if (
            empty($this->clientCredentialsAuthManager->getOAuthClientId()) &&
            empty($this->clientCredentialsAuthManager->getOAuthClientSecret())
        ) {
            return null;
        }
        return ClientCredentialsAuthCredentialsBuilder::init(
            $this->clientCredentialsAuthManager->getOAuthClientId(),
            $this->clientCredentialsAuthManager->getOAuthClientSecret()
        )->oAuthToken($this->clientCredentialsAuthManager->getOAuthToken());
    }

    public function getLoggingConfigurationBuilder(): ?LoggingConfigurationBuilder
    {
        if (is_null($this->loggingConfigurationBuilder)) {
            return null;
        }
        $config = $this->loggingConfigurationBuilder->getConfiguration();
        return LoggingConfigurationBuilder::init()
            ->level($config['level'])
            ->logger($config['logger'])
            ->maskSensitiveHeaders($config['maskSensitiveHeaders'])
            ->requestConfiguration(RequestLoggingConfigurationBuilder::init()
                ->includeQueryInPath($config['requestConfiguration']['includeQueryInPath'])
                ->body($config['requestConfiguration']['body'])
                ->headers($config['requestConfiguration']['headers'])
                ->includeHeaders(...$config['requestConfiguration']['includeHeaders'])
                ->excludeHeaders(...$config['requestConfiguration']['excludeHeaders'])
                ->unmaskHeaders(...$config['requestConfiguration']['unmaskHeaders']))
            ->responseConfiguration(ResponseLoggingConfigurationBuilder::init()
                ->body($config['responseConfiguration']['body'])
                ->headers($config['responseConfiguration']['headers'])
                ->includeHeaders(...$config['responseConfiguration']['includeHeaders'])
                ->excludeHeaders(...$config['responseConfiguration']['excludeHeaders'])
                ->unmaskHeaders(...$config['responseConfiguration']['unmaskHeaders']));
    }

    /**
     * Get the proxy configuration builder
     */
    public function getProxyConfigurationBuilder(): ProxyConfigurationBuilder
    {
        return ProxyConfigurationBuilder::init($this->proxyConfiguration['address'])
            ->port($this->proxyConfiguration['port'])
            ->tunnel($this->proxyConfiguration['tunnel'])
            ->auth($this->proxyConfiguration['auth']['user'], $this->proxyConfiguration['auth']['pass'])
            ->authMethod($this->proxyConfiguration['auth']['method']);
    }

    /**
     * Get the client configuration as an associative array
     *
     * @see PaypalServerSdkClientBuilder::getConfiguration()
     */
    public function getConfiguration(): array
    {
        return $this->toBuilder()->getConfiguration();
    }

    /**
     * Clone this client and override given configuration options
     *
     * @see PaypalServerSdkClientBuilder::build()
     */
    public function withConfiguration(array $config): self
    {
        return new self(array_merge($this->config, $config));
    }

    /**
     * Get the base uri for a given server in the current environment.
     *
     * @param string $server Server name
     *
     * @return string Base URI
     */
    public function getBaseUri(string $server = Server::DEFAULT_): string
    {
        return $this->client->getGlobalRequest($server)->getQueryUrl();
    }

    /**
     * Returns Orders Controller
     */
    public function getOrdersController(): OrdersController
    {
        if ($this->orders == null) {
            $this->orders = new OrdersController($this->client);
        }
        return $this->orders;
    }

    /**
     * Returns Payments Controller
     */
    public function getPaymentsController(): PaymentsController
    {
        if ($this->payments == null) {
            $this->payments = new PaymentsController($this->client);
        }
        return $this->payments;
    }

    /**
     * Returns Vault Controller
     */
    public function getVaultController(): VaultController
    {
        if ($this->vault == null) {
            $this->vault = new VaultController($this->client);
        }
        return $this->vault;
    }

    /**
     * Returns Transaction Search Controller
     */
    public function getTransactionSearchController(): TransactionSearchController
    {
        if ($this->transactionSearch == null) {
            $this->transactionSearch = new TransactionSearchController($this->client);
        }
        return $this->transactionSearch;
    }

    /**
     * Returns Subscriptions Controller
     */
    public function getSubscriptionsController(): SubscriptionsController
    {
        if ($this->subscriptions == null) {
            $this->subscriptions = new SubscriptionsController($this->client);
        }
        return $this->subscriptions;
    }

    /**
     * Returns O Auth Authorization Controller
     */
    public function getOAuthAuthorizationController(): OAuthAuthorizationController
    {
        if ($this->oAuthAuthorization == null) {
            $this->oAuthAuthorization = new OAuthAuthorizationController($this->client);
        }
        return $this->oAuthAuthorization;
    }

    /**
     * A map of all base urls used in different environments and servers
     *
     * @var array
     */
    private const ENVIRONMENT_MAP = [
        Environment::PRODUCTION => [Server::DEFAULT_ => 'https://api-m.paypal.com'],
        Environment::SANDBOX => [Server::DEFAULT_ => 'https://api-m.sandbox.paypal.com']
    ];
}
