<?php

namespace Fifa\ConnectId\Sdk\Environment;

use Fifa\ConnectId\Sdk\Api\ApiClient;
use Fifa\ConnectId\Sdk\Api\Configuration;
use Fifa\ConnectId\Sdk\Api\Resource;
use Fifa\ConnectId\Sdk\Authentication\ConnectIdAuthenticationResourceBuilder;
use Fifa\ConnectServiceBus\Sdk\Authentication\Model\AuthenticationInformation;
use Fifa\ConnectServiceBus\Sdk\Authentication\Model\ClientCredentials;
use Fifa\ConnectServiceBus\Sdk\Authentication\TokenProvider;
use Fifa\ConnectServiceBus\Sdk\Environment\Environment as ConnectServiceBusEnvironment;
use Fifa\ConnectServiceBus\Sdk\Environment\EnvironmentInterface as ConnectServiceBusEnvironmentInterface;
use Fifa\ConnectServiceBus\Sdk\Environment\EnvironmentSettings;

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

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

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

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

    /**
     * @var ConnectServiceBusEnvironmentInterface
     */
    private $serviceBusEnvironment;

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

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

    /**
     * Constructor
     *
     * @param string $tenant
     * @param $hashingServiceUrl
     * @param $idDirectoryUrl
     * @param ConnectServiceBusEnvironmentInterface $serviceBusEnvironment
     */
    public function __construct(string $tenant, $hashingServiceUrl, $idDirectoryUrl, ConnectServiceBusEnvironmentInterface $serviceBusEnvironment)
    {
        $this->hashingServiceUrl = $hashingServiceUrl;
        $this->idDirectoryUrl = $idDirectoryUrl;
        $this->tenant = $tenant;
        $this->serviceBusEnvironment = $serviceBusEnvironment;
    }

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

    /**
     * Returns Api Resource for hashing service
     *
     * @param ClientCredentials $clientCredentials
     * @return Resource\HashApi
     */
    public function getHashingApiResource(ClientCredentials $clientCredentials): Resource\HashApi
    {
        $client = $this->getApiClient($this->hashingServiceUrl, $this->getAuthenticationInformation($this->hashingServiceUrl, $clientCredentials));
        return new Resource\HashApi($client);
    }

    /**
     * Returns Api Resource for person service
     *
     * @param ClientCredentials $clientCredentials
     * @return Resource\PersonApi
     */
    public function getPersonApiResource(ClientCredentials $clientCredentials): Resource\PersonApi
    {
        $client = $this->getApiClient($this->idDirectoryUrl, $this->getAuthenticationInformation($this->idDirectoryUrl, $clientCredentials));
        return new Resource\PersonApi($client);
    }

    /**
     * Returns Api Resource for organisation service
     *
     * @param ClientCredentials $clientCredentials
     * @return Resource\OrganisationApi
     */
    public function getOrganisationApiResource(ClientCredentials $clientCredentials): Resource\OrganisationApi
    {
        $client = $this->getApiClient($this->idDirectoryUrl, $this->getAuthenticationInformation($this->idDirectoryUrl, $clientCredentials));
        return new Resource\OrganisationApi($client);
    }

    /**
     * Returns Api Resource for Facility service
     *
     * @param ClientCredentials $clientCredentials
     * @return Resource\FacilityApi
     */
    public function getFacilityApiResource(ClientCredentials $clientCredentials): Resource\FacilityApi
    {
        $client = $this->getApiClient($this->idDirectoryUrl, $this->getAuthenticationInformation($this->idDirectoryUrl, $clientCredentials));
        return new Resource\FacilityApi($client);
    }

    /**
     * Returns Api Resource for Data Holder service
     *
     * @param ClientCredentials $clientCredentials
     * @return Resource\DataHolderApi
     */
    public function getDataHolderApiResource(ClientCredentials $clientCredentials): Resource\DataHolderApi
    {
        $client = $this->getApiClient($this->idDirectoryUrl, $this->getAuthenticationInformation($this->idDirectoryUrl, $clientCredentials));
        return new Resource\DataHolderApi($client);
    }

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

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

    /**
     * Returns Test Environment
     *
     * @param EnvironmentSettings|null $settings
     * @return EnvironmentInterface
     */
    public static function Test(EnvironmentSettings $settings = null)
    {
        return self::create('test', $settings);
    }

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

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

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

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

        $serviceUrl = 'https://fci-apim-%s.azure-api.net/%s';

        $hashingUrl = sprintf($serviceUrl, $envCode, 'hashing');
        $idDirectoryUrl = sprintf($serviceUrl, $envCode, 'iddirectory');
        $connectServiceBusEnv = self::createServiceBusEnvironment($envCode, $settings);

        return new Environment(self::DEFAULT_TENANT, $hashingUrl, $idDirectoryUrl, $connectServiceBusEnv);
    }

    /**
     * @param string $envCode
     * @param EnvironmentSettings|null $settings
     * @return ConnectServiceBusEnvironmentInterface
     */
    private static function createServiceBusEnvironment(string $envCode, EnvironmentSettings $settings = null): ConnectServiceBusEnvironmentInterface
    {
        switch ($envCode) {
            case 'prep':
                return ConnectServiceBusEnvironment::PreProduction($settings);
            case 'prod':
                return ConnectServiceBusEnvironment::Production($settings);
            default:
                return ConnectServiceBusEnvironment::create($envCode, $settings);
        }
    }

    /**
     * Getter for IdDirectoryUrl
     *
     * @return string
     */
    public function getIdDirectoryUrl(): string
    {
        return $this->idDirectoryUrl;
    }

    /**
     * Getter for HashingServiceUrl
     *
     * @return string
     */
    public function getHashingServiceUrl(): string
    {
        return $this->hashingServiceUrl;
    }

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

    /**
     * Getter for service bus environment
     *
     * @return ConnectServiceBusEnvironmentInterface
     */
    public function getServiceBusEnvironment(): ConnectServiceBusEnvironmentInterface
    {
        return $this->serviceBusEnvironment;
    }

    /**
     * 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 $timeoutInSeconds
     * @return EnvironmentInterface
     */
    public function setCurlConnectionTimeout($timeoutInSeconds): EnvironmentInterface
    {
        $this->curlConnectionTimeout = $timeoutInSeconds;
        return $this;
    }

    /**
     * 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(
            new ConnectIdAuthenticationResourceBuilder(), $clientCredentials, $serviceUri, $this->tenant
        );
    }

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

    /**
     * @param string $url
     * @param AuthenticationInformation $authenticationInformation
     * @return Configuration
     */
    private function getApiConfiguration(string $url, AuthenticationInformation $authenticationInformation)
    {
        $config = new ApiClientConfiguration(new TokenProvider($authenticationInformation, $this->getServiceBusEnvironment()->getCacheStorage()));
        $config->setHost($url)
            ->setCurlTimeout($this->curlTimeout)
            ->setCurlConnectTimeout($this->curlConnectionTimeout);

        return $config;
    }
}