<?php
namespace Fifa\ConnectServiceBus\Sdk\Environment;

use Fifa\ConnectServiceBus\Sdk\Api\ApiClient;
use Fifa\ConnectServiceBus\Sdk\Api\Configuration;
use Fifa\ConnectServiceBus\Sdk\Api\Resource\CertificateApi;
use Fifa\ConnectServiceBus\Sdk\Api\Resource\MessageApi;
use Fifa\ConnectServiceBus\Sdk\Authentication\CacheStorageInterface;
use Fifa\ConnectServiceBus\Sdk\Authentication\Model\AuthenticationInformation;
use Fifa\ConnectServiceBus\Sdk\Authentication\Model\ClientCredentials;
use Fifa\ConnectServiceBus\Sdk\Authentication\TokenProvider;

/**
 * Class Environment
 * @package Fifa\ConnectServiceBus\Sdk\Environment
 */
class Environment implements EnvironmentInterface
{
    /**
     * @var string
     */
    const DEFAULT_TENANT = 'fifaconnect.onmicrosoft.com';

    /**
     * @var string
     */
    private $serviceUrl;

    /**
     * @var string
     */
    private $tenant;

    /**
     * @var EnvironmentSettings
     */
    private $settings;

    /**
     * @var int
     */
    private $curlTimeout = 90;

    /**
     * @var int
     */
    private $curlConnectionTimeout = 90;

    /**
     * Constructor
     *
     * @param string $tenant
     * @param string $serviceUrl
     * @param EnvironmentSettings|null $settings
     */
    public function __construct(string $tenant, string $serviceUrl, ?EnvironmentSettings $settings = null)
    {
        if (is_null($settings)) {
            $settings = new EnvironmentSettings();
        }

        $this->serviceUrl = $serviceUrl;
        $this->tenant = $tenant;
        $this->settings = $settings;
    }

    /**
     * String representation of the object
     *
     * @return string
     */
    public function __toString()
    {
        return 'ServiceUrl: ' . $this->serviceUrl . ', Tenant: ' . $this->tenant;
    }

    /**
     * Getter for service url
     *
     * @return string
     */
    public function getServiceUrl(): string
    {
        return $this->serviceUrl;
    }

    /**
     * Getter for tenant
     *
     * @return string
     */
    public function getTenant(): string
    {
        return $this->tenant;
    }

    /**
     * Returns beta environment instance
     *
     * @param EnvironmentSettings|null $settings
     * @return EnvironmentInterface
     */
    public static function Beta(?EnvironmentSettings $settings = null): EnvironmentInterface
    {
        return self::create('beta', $settings);
    }

    /**
     * Returns integration environment instance
     *
     * @param EnvironmentSettings|null $settings
     * @return EnvironmentInterface
     */
    public static function Integration(?EnvironmentSettings $settings = null): EnvironmentInterface
    {
        return self::create('int', $settings);
    }

    /**
     * Returns hotfix environment instance
     *
     * @param EnvironmentSettings|null $settings
     * @return EnvironmentInterface
     */
    public static function Hotfix(?EnvironmentSettings $settings = null): EnvironmentInterface
    {
        return self::create('hfix', $settings);
    }

    /**
     * Returns PreProduction environment instance
     *
     * @param EnvironmentSettings|null $settings
     * @return EnvironmentInterface
     */
    public static function PreProduction(?EnvironmentSettings $settings = null): EnvironmentInterface
    {
        return self::create('prep', $settings);
    }

    /**
     * Returns production environment instance
     *
     * @param EnvironmentSettings|null $settings
     * @return EnvironmentInterface
     */
    public static function Production(?EnvironmentSettings $settings = null): EnvironmentInterface
    {
        return self::create('prod', $settings);
    }

    /**
     * @param string $envCode
     * @param EnvironmentSettings|null $settings
     * @return EnvironmentInterface
     */
    public static function create(string $envCode, ?EnvironmentSettings $settings = null): EnvironmentInterface
    {
        $envCode = strtolower($envCode);

        return new Environment(self::DEFAULT_TENANT, sprintf('https://fci-apim-%s.azure-api.net/bus', $envCode), $settings);
    }

    /**
     * Creates and returns a message Api object
     *
     * @param ClientCredentials $clientCredentials
     * @return MessageApi
     */
    public function getMessageApi(ClientCredentials $clientCredentials): MessageApi
    {
        return new MessageApi($this->getApiClient(
            $this->serviceUrl, $this->getAuthenticationInformation($this->serviceUrl, $clientCredentials)
        ));
    }

    /**
     * Creates and returns a certificate Api object
     *
     * @param ClientCredentials $clientCredentials
     * @return CertificateApi
     */
    public function getCertificateApi(ClientCredentials $clientCredentials): CertificateApi
    {
        return new CertificateApi($this->getApiClient(
            $this->serviceUrl, $this->getAuthenticationInformation($this->serviceUrl, $clientCredentials)
        ));
    }

    /**
     * Getter for cache storage
     *
     * @return CacheStorageInterface
     */
    public function getCacheStorage(): CacheStorageInterface
    {
        return $this->settings->getCacheStorage();
    }

    /**
     * Setter for Curl timeout (CURLOPT_TIMEOUT)
     *
     * @param int $timeoutInSeconds
     * @return EnvironmentInterface
     */
    public function setCurlTimeout(int $timeoutInSeconds): EnvironmentInterface
    {
        $this->curlTimeout = $timeoutInSeconds;
        return $this;
    }

    /**
     * Setter for Curl connection timeout (CURLOPT_CONNECTTIMEOUT)
     *
     * @param int $timeoutInSeconds
     * @return EnvironmentInterface
     */
    public function setCurlConnectionTimeout(int $timeoutInSeconds): EnvironmentInterface
    {
        $this->curlConnectionTimeout = $timeoutInSeconds;
        return $this;
    }

    /**
     * Returns Api Client
     *
     * @param string $url
     * @param AuthenticationInformation $authenticationInformation
     * @return ApiClient
     */
    private function getApiClient(string $url, AuthenticationInformation $authenticationInformation): ApiClient
    {
        return new ApiClient($this->getApiConfiguration($url, $authenticationInformation));
    }

    /**
     * Returns the configuration object for Api Client
     *
     * @param string $url
     * @param AuthenticationInformation $authenticationInformation
     * @return Configuration
     */
    private function getApiConfiguration(string $url, AuthenticationInformation $authenticationInformation): Configuration
    {
        $config = new ApiClientConfiguration(new TokenProvider($authenticationInformation, $this->settings->getCacheStorage()));
        $config->setHost($url)
            ->setCurlTimeout($this->curlTimeout)
            ->setCurlConnectTimeout($this->curlConnectionTimeout);

        return $config;
    }

    /**
     * Creates and returns an instance of AuthenticationInformation class
     *
     * @param string $serviceUri
     * @param ClientCredentials $clientCredentials
     * @return AuthenticationInformation
     */
    private function getAuthenticationInformation(string $serviceUri, ClientCredentials $clientCredentials): AuthenticationInformation
    {
        return new AuthenticationInformation(
            $this->settings->getAuthenticationResourceBuilder(), $clientCredentials, $serviceUri, $this->tenant
        );
    }
}