<?php

declare(strict_types=1);

namespace GlobalPayments\Api\ServiceConfigs\Gateways;

use GlobalPayments\Api\ConfiguredServices;
use GlobalPayments\Api\Entities\Enums\{
    DataResidency,
    Environment,
    GatewayProvider,
    Secure3dVersion,
    ServiceEndpoints
};
use GlobalPayments\Api\Entities\Exceptions\ConfigurationException;
use GlobalPayments\Api\Entities\GpApi\{AccessTokenInfo, GpApiSessionInfo};
use GlobalPayments\Api\Gateways\{GpApiConnector, IAccessTokenProvider};

class GpApiConfig extends GatewayConfig
{
    public const QA_ENVIRONMENT = 'QA';

    public ?string $appId = null;
    public ?string $appKey = null;
    public ?string $siteId = null;
    public ?string $licenseId = null;
    public ?string $deviceId = null;
    public ?string $username = null;
    public ?string $password = null;
    public ?string $secretApiKey = null;
    
    /**
     * @var AccessTokenInfo
    */
    public ?AccessTokenInfo $accessTokenInfo = null;
    
    /**
     * @var string
    */
    public string $country = 'US';

    /**
     * @var string
    */
    public string $dataResidency = DataResidency::NONE;

    /**
     * @var string
    */
    public ?string $channel = null;

    /**
     * @var int
    */
    public ?int $secondsToExpire = null;

    public ?bool $restrictedToken = null;

    /**
     * @var string
    */
    public ?string $intervalToExpire = null;

    /**
     * @var string
    */
    public ?string $methodNotificationUrl = null;

    /**
     * @var string
    */
    public ?string $challengeNotificationUrl = null;

    /**
     * @var string
    */
    public ?string $merchantContactUrl = null;

    /**
     * @var array
    */
    public ?array $permissions = null;

    /**
     * @var string
    */
    public ?string $merchantId = null;

    /**
     * @var string
    */
    public ?string $deviceCurrency = null;

    /**
     * @var string
    */
    public string $statusUrl;

    /**
     * @var string
    */
    public string $transactionAccountName;

    /**
     * @var IAccessTokenProvider
    */
    public IAccessTokenProvider $accessTokenProvider;

    public function __construct()
    {
        $this->gatewayProvider = GatewayProvider::GP_API;
        $this->accessTokenProvider = new \GlobalPayments\Api\Entities\GpApi\GpApiSessionInfo();
    }

    public function configureContainer(ConfiguredServices $services)
    {
        if (empty($this->serviceUrl)) {
            $this->serviceUrl = $this->resolveServiceUrl();
        }
        if (!isset($this->accessTokenProvider)) {
            $this->accessTokenProvider = new GpApiSessionInfo();
        }

        $gateway = new GpApiConnector($this);
        $gateway->serviceUrl = $this->serviceUrl;
        $gateway->requestLogger = $this->requestLogger;
        $gateway->webProxy = $this->webProxy;
        $gateway->dynamicHeaders = $this->dynamicHeaders;
        $gateway->environment = $this->environment;

        $services->gatewayConnector = $gateway;
        $services->reportingService = $gateway;
        $services->fraudService = $gateway;
        $services->fileProcessingService = $gateway;
        $services->recurringConnector = $gateway;
        $services->installmentService = $gateway;

        $services->setOpenBankingProvider($gateway);
        $services->setPayFacProvider($gateway);
        $services->setSecure3dProvider(Secure3dVersion::ONE, $gateway);
        $services->setSecure3dProvider(Secure3dVersion::TWO, $gateway);
    }

    public function resolveServiceUrl(): string
    {
        if (!empty($this->serviceUrl)) {
            return $this->serviceUrl;
        }

        if ($this->dataResidency == DataResidency::EU) {
            if ($this->environment == Environment::PRODUCTION) {
                return ServiceEndpoints::GP_API_PRODUCTION_EU;
            }

            return ($this->environment === self::QA_ENVIRONMENT) ?
                ServiceEndpoints::GP_API_QA_EU : ServiceEndpoints::GP_API_TEST_EU;
        }

        return ($this->environment == Environment::PRODUCTION) ?
            ServiceEndpoints::GP_API_PRODUCTION : ServiceEndpoints::GP_API_TEST;
    }

    public function validate(): void
    {
        parent::validate();

        if ($this->environment !== Environment::TEST &&
            $this->environment !== Environment::PRODUCTION &&
            $this->environment !== self::QA_ENVIRONMENT) {
            throw new ConfigurationException(
                'Environment must be one of TEST, PRODUCTION, or QA for GP API'
            );
        }
        
        if (!empty($this->accessTokenInfo)) {
            return;
        }
        
        $hasGpApiCredentials = !empty($this->appId) && !empty($this->appKey);
        $hasPorticoCredentials = !empty($this->deviceId) && !empty($this->siteId) && 
            !empty($this->licenseId) && !empty($this->username) && !empty($this->password);
        $hasSecretApiKey = !empty($this->secretApiKey);
        
        if (!$hasGpApiCredentials && !$hasPorticoCredentials && !$hasSecretApiKey) {
            throw new ConfigurationException(
                'AccessTokenInfo or (AppId and AppKey) or (Portico 5-point credentials: deviceId, siteId, licenseId, username, password) or SecretApiKey must be provided'
            );
        }
        
        if ((!empty($this->deviceId) || !empty($this->siteId) || !empty($this->licenseId) || 
             !empty($this->username) || !empty($this->password)) && !$hasPorticoCredentials) {
            throw new ConfigurationException(
                'When using Portico credentials, all 5 fields must be provided: deviceId, siteId, licenseId, username, and password'
            );
        }
    }
}