<?php
/**
 * @author 1902 Software
 * @copyright Copyright © 2023 1902 Software (https://1902software.com/magento/)
 * @package WriteTextAI_WriteTextAI
 */

namespace WriteTextAI\WriteTextAI\Model\Api;

use Magento\Framework\HTTP\Client\CurlFactory as CurlClientFactory;
use Magento\Framework\HTTP\Adapter\CurlFactory as CurlAdapterFactory;
use WriteTextAI\WriteTextAI\Helper\Data;
use WriteTextAI\WriteTextAI\Logger\Logger;
use WriteTextAI\WriteTextAI\Model\UserWebTokenManager;
use Magento\Framework\App\ProductMetadataInterface;
use Magento\Framework\Module\ModuleListInterface;
use WriteTextAI\WriteTextAI\Model\Api\Session as ApiSession;
use WriteTextAI\WriteTextAI\Model\Api\StatusCodeValidator;
use WriteTextAI\WriteTextAI\Model\Api\VersionValidator;
use WriteTextAI\WriteTextAI\Exception\UnauthorizedException;
use Laminas\Uri\UriFactory;
use WriteTextAI\WriteTextAI\Exception\VersionException;
use Magento\Framework\Message\ManagerInterface as MessageManager;
use WriteTextAI\WriteTextAI\Model\Api\CustomTypeRequestFactory as CustomTypeRequest;
use Magento\Backend\Model\UrlInterface;
use Magento\Backend\Helper\Data as BackendHelper;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Store\Model\ScopeInterface;
use Magento\Framework\Filesystem\Io\File;
use Magento\Framework\Filesystem\Driver\File as DriverInterface;

/**
 * @api
 */
class CurlBuilder
{
    public const MODULE_NAME = 'WriteTextAI_WriteTextAI';

    /**
     * @var CurlClientFactory
     */
    protected $curlClientFactory;

    /**
     * @var array Static cURL handle pool for connection reuse
     */
    private static $curlHandlePool = [];

    /**
     * @var int Maximum pool size
     */
    public const MAX_POOL_SIZE = 10;

    /**
     * @var int Connection timeout in seconds
     */
    public const CONNECTION_TIMEOUT = 30;

    /**
     * @var int Request timeout in seconds
     */
    public const REQUEST_TIMEOUT = 240;

    /**
     * @var Data
     */
    protected $helper;

    /**
     * @var Logger
     */
    protected $logger;

    /**
     * @var UserWebTokenManager
     */
    protected $userWebTokenManager;

    /**
     * @var ProductMetadataInterface
     */
    protected $productMetadata;

    /**
     * @var ModuleListInterface
     */
    protected $moduleList;

    /**
     * @var ApiSession
     */
    protected $apiSession;

    /**
     * @var StatusCodeValidator
     */
    protected $statusCodeValidator;

    /**
     * @var VersionValidator
     */
    protected $versionValidator;

    /**
     * @var MessageManager
     */
    protected $messageManager;

    /**
     * @var CustomTypeRequest
     */
    protected $customTypeRequest;

    /**
     * @var UrlInterface
     */
    protected $urlBuilder;

    /**
     * @var BackendHelper
     */
    protected $backendHelper;

    /**
     * @var StoreManagerInterface
     */
    protected $storeManager;

    /**
     * @var ScopeConfigInterface
     */
    protected $scopeConfig;

    /**
     * @var File
     */
    protected $file;

    /**
     * @var DriverInterface
     */
    protected $driver;

    /**
     * @var CurlAdapterFactory
     */
    protected $curlAdapterFactory;

    /**
     * @var string
     */
    protected $curlClient = '';

    /**
     * Constructor
     *
     * @param CurlClientFactory $curlClientFactory
     * @param CurlAdapterFactory $curlAdapterFactory
     * @param Data $helper
     * @param Logger $logger
     * @param UserWebTokenManager $userWebTokenManager
     * @param ProductMetadataInterface $productMetadata
     * @param ModuleListInterface $moduleList
     * @param ApiSession $apiSession
     * @param StatusCodeValidator $statusCodeValidator
     * @param VersionValidator $versionValidator
     * @param MessageManager $messageManager
     * @param CustomTypeRequest $customTypeRequest
     * @param UrlInterface $urlBuilder
     * @param BackendHelper $backendHelper
     * @param StoreManagerInterface $storeManager
     * @param ScopeConfigInterface $scopeConfig
     * @param File $file
     * @param DriverInterface $driver
     */
    public function __construct(
        CurlClientFactory $curlClientFactory,
        CurlAdapterFactory $curlAdapterFactory,
        Data $helper,
        Logger $logger,
        UserWebTokenManager $userWebTokenManager,
        ProductMetadataInterface $productMetadata,
        ModuleListInterface $moduleList,
        ApiSession $apiSession,
        StatusCodeValidator $statusCodeValidator,
        VersionValidator $versionValidator,
        MessageManager $messageManager,
        CustomTypeRequest $customTypeRequest,
        UrlInterface $urlBuilder,
        BackendHelper $backendHelper,
        StoreManagerInterface $storeManager,
        ScopeConfigInterface $scopeConfig,
        File $file,
        DriverInterface $driver
    ) {
        $this->curlClientFactory = $curlClientFactory;
        $this->curlAdapterFactory = $curlAdapterFactory;
        $this->helper = $helper;
        $this->logger = $logger;
        $this->userWebTokenManager = $userWebTokenManager;
        $this->productMetadata = $productMetadata;
        $this->moduleList = $moduleList;
        $this->apiSession = $apiSession;
        $this->statusCodeValidator = $statusCodeValidator;
        $this->versionValidator = $versionValidator;
        $this->messageManager = $messageManager;
        $this->customTypeRequest = $customTypeRequest;
        $this->urlBuilder = $urlBuilder;
        $this->backendHelper = $backendHelper;
        $this->storeManager = $storeManager;
        $this->scopeConfig = $scopeConfig;
        $this->file = $file;
        $this->driver = $driver;
    }

    /**
     * Get api auth url
     *
     * @return string
     */
    public function getApiAuthUrl()
    {
        return $this->helper->getApiAuthUrl();
    }

    /**
     * Get api web url
     *
     * @return string
     */
    public function getApiWebUrl()
    {
        return $this->helper->getApiWebUrl();
    }

    /**
     * Get saved region
     *
     * @return string
     */
    private function getSavedRegion()
    {
        return $this->helper->getRegion();
    }

    /**
     * Get client id
     *
     * @return string
     */
    public function getClientId()
    {
        return $this->helper->getClientId();
    }

    /**
     * Ping region
     *
     * @param string $region
     * @return string
     */
    public function pingRegion($region)
    {
        try {
            $curl = $this->curlAdapterFactory->create();
            // headers
            $headers = ["Cache-Control" => "no-cache"];
            // body
            $regionUrl = $region;
            if (substr((string)$regionUrl, 0, 4) !== "http") {
                $regionUrl = "https://{$region}";
            }
            $apiUrl = $regionUrl . '/text/ping';
            // log request
            $this->logger->logRequest($apiUrl);
            $startTime = microtime(true);
            // send request
            $curl->write(
                'GET',
                $apiUrl,
                '1.1',
                $headers
            );
            $rawResponse = $curl->read();
            $body = is_string($rawResponse) ? preg_split("#\r\n\r\n#", $rawResponse, 2)[1] ?? '' : '';
            $response = (float) $curl->getInfo(CURLINFO_TOTAL_TIME);
            if ($response <= 0) {
                $response = microtime(true) - $startTime;
            }
            // log response
            $this->logger->logResponse($curl->getInfo(CURLINFO_HTTP_CODE), $response);
            // close request
            $curl->close();
            // return response
            return $response;
        } catch (\Exception $e) {
            $this->logger->logResponseErrors($e->getMessage(), $apiUrl);
        }
    }

    /**
     * Get regions
     *
     * @return array
     */
    public function getRegions()
    {
        try {
            $curl = $this->curlClientFactory->create();
            // body
            $apiUrl = $this->getApiWebUrl() . 'web/regions';
            // log request
            $this->logger->logRequest($apiUrl);
            // send request
            $curl->get($apiUrl);
            // get response
            $response = json_decode($curl->getBody(), true);
            // log response
            $this->logger->logResponse($curl->getStatus(), $response);
            // return response
            return $response;
        } catch (\Exception $e) {
            $this->logger->logResponseErrors($e->getMessage(), $apiUrl);
        }
    }

    /**
     * Register domains
     *
     * @return array
     */
    public function registerDomains()
    {
        try {
            $body = [
                "domains" => []
            ];

            foreach ($this->storeManager->getStores() as $store) {
                $storeId = $store->getId();
                $baseUrl = $store->getBaseUrl();
                
                // Get country code from store config
                $countryCode = $this->scopeConfig->getValue(
                    'general/country/default',
                    ScopeInterface::SCOPE_STORE,
                    $storeId
                );
                
                if (!isset($body['domains']) ||
                    !in_array(['domain' => $baseUrl, 'countryCode' => $countryCode], $body['domains'])
                ) {
                    $body['domains'][] = [
                        "domain" => $baseUrl,
                        "countryCode" => $countryCode
                    ];
                }
            }

            return $this->getApiResponse('web/Domains/register', json_encode($body), 'post');
        } catch (\Exception $e) {
            // Skip registration if it fails
            return [];
        }
    }

    /**
     * Get a cURL handle from pool or create new one
     *
     * @param string $url
     * @return \Magento\Framework\HTTP\Client\Curl
     */
    private function getCurlHandle($url)
    {
        try {
            $uri = UriFactory::factory($url);
            $host = $uri->getHost();
        } catch (\Exception $e) {
            $host = 'default';
        }
        
        // Check if we have a handle for this host in the pool
        if (isset(self::$curlHandlePool[$host])) {
            $handle = self::$curlHandlePool[$host];
            // Reset the handle for new request
            $handle->setOption(CURLOPT_HTTPGET, false);
            $handle->setOption(CURLOPT_POST, false);
            $handle->setOption(CURLOPT_POSTFIELDS, null);
            $handle->setOption(CURLOPT_HTTPHEADER, []);
            return $handle;
        }
        
        // Create new handle
        $handle = $this->curlFactory->create();
        
        // Add to pool if under limit
        if (count(self::$curlHandlePool) < self::MAX_POOL_SIZE) {
            self::$curlHandlePool[$host] = $handle;
        } else {
            // Remove oldest handle if pool is full
            array_shift(self::$curlHandlePool);
            self::$curlHandlePool[$host] = $handle;
        }
        
        return $handle;
    }
    
    /**
     * Get cURL client with optimized settings
     *
     * @return \Magento\Framework\HTTP\Client\Curl
     */
    private function getCurlClientWithOptimizedSettings()
    {
        if ($this->curlClient) {
            return $this->curlClient;
        }
        $curl = $this->curlClientFactory->create();
        // Set optimized SSL options
        // $curl->setOption(CURLOPT_SSL_VERIFYPEER, true);
        // $curl->setOption(CURLOPT_SSL_VERIFYHOST, 2);
        
        // Try system CA bundle locations
        $caLocations = [
            '/etc/ssl/certs/ca-certificates.crt', // Debian/Ubuntu/Gentoo etc.
            '/etc/pki/tls/certs/ca-bundle.crt',   // Fedora/RHEL 6
            '/etc/ssl/ca-bundle.pem',              // OpenSUSE
            '/etc/pki/tls/cert.pem',               // OpenELEC
            '/etc/ssl/cert.pem',                   // Alpine Linux
            '/usr/local/share/certs/ca-root-nss.crt', // FreeBSD
            '/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem', // CentOS/RHEL 7
            '/etc/ssl/certs/ca-bundle.crt'         // Old CentOS/RHEL
        ];
        
        // foreach ($caLocations as $caLocation) {
        //     if (file_exists($caLocation) && is_readable($caLocation)) {
        //         $curl->setOption(CURLOPT_CAINFO, $caLocation);
        //         break;
        //     }
        // }
        
        // Connection and timeout settings
        $curl->setOption(CURLOPT_CONNECTTIMEOUT, self::CONNECTION_TIMEOUT);
        $curl->setOption(CURLOPT_TIMEOUT, self::REQUEST_TIMEOUT);
        
        $curl->setOption(CURLOPT_FORBID_REUSE, false);
        $curl->setOption(CURLOPT_FRESH_CONNECT, false);
        
        // DNS and connection optimization
        $curl->setOption(CURLOPT_DNS_CACHE_TIMEOUT, 600);
        $curl->setOption(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); // Force IPv4 to avoid IPv6 issues
        
        // Keep-alive and connection reuse
        $curl->setOption(CURLOPT_TCP_KEEPALIVE, 1);
        $curl->setOption(CURLOPT_TCP_KEEPIDLE, 120);
        $curl->setOption(CURLOPT_TCP_KEEPINTVL, 60);
        
        // Follow redirects
        $curl->setOption(CURLOPT_FOLLOWLOCATION, true);
        $curl->setOption(CURLOPT_MAXREDIRS, 5);
        
        $this->curlClient = $curl;
        return $curl;
    }
    
    /**
     * Clean up cURL handle pool
     */
    public function cleanupPool()
    {
        self::$curlHandlePool = [];
        gc_collect_cycles();
    }

    /**
     * Post request without auth
     *
     * @param string $apiPath
     * @param array $body
     * @param string $type
     *
     * @return array
     */
    public function sendPostWithoutAuth(
        string $apiPath,
        $body = [],
        $type = 'post'
    ) {
        try {
            $curl = $this->getCurlClientWithOptimizedSettings();
            $this->addHeaders(
                $curl,
                [
                    "Content-Type" => "application/json",
                ]
            );
            // body
            $apiUrl = $this->getApiWebUrl() . $apiPath;
            // log request
            $this->logger->logRequest($apiUrl, $body);
            // send request
            $curl->$type($apiUrl, $body);
            // get response
            $response = json_decode($curl->getBody(), true);
            // log response
            $this->logger->logResponse($curl->getStatus(), $response);
            // return response
            return $response;
        } catch (\Exception $e) {
            $this->logger->logResponseErrors($e->getMessage(), $apiUrl);
            throw $e;
        }
    }
    
    /**
     * Get user web token
     *
     * @param bool $renew
     * @return string
     */
    public function getUserWebToken($renew = false)
    {
        $user = $this->apiSession->getCurrentUser();
        if ($user) {
            $email = $user->getEmail();
            $userId = $user->getId();
            $firstName = $user->getFirstName();
            $lastName = $user->getLastName();

            if ($renew) {
                $this->userWebTokenManager->deleteUserWebToken($userId);
            }
        
            $webToken = $this->userWebTokenManager->getUserWebToken($userId);
            $getTokenEmail = $this->userWebTokenManager->getTokenEmail($userId);
            if (!$webToken || $getTokenEmail !== $email) {
                $webToken = $this->getWebToken($email, $userId, $firstName, $lastName);
                $this->registerDomains();
            }
        } else {
            $webToken = $this->getWebToken();
        }

        return $webToken;
    }

    /**
     * Get token time
     */
    public function getTokenTime()
    {
        try {
            $curl = $this->curlClientFactory->create();
            // get account token
            $accountToken = $this->helper->getAccountToken();
            // get connect token
            $connectToken = $this->getConnectToken($accountToken);
            // headers
            $platformVersion = $this->productMetadata->getVersion();
            $moduleVersion = $this->getModuleVersion();
            $this->addHeaders(
                $curl,
                [
                    "Cache-Control" => "no-cache",
                    "Authorization" => "Bearer " . $connectToken
                ]
            );
            // body
            $apiUrl = $this->getApiWebUrl() . 'web/token/time';

            // log request
            $this->logger->logRequest($apiUrl);
            // send request
            $curl->get($apiUrl);
            // validate response
            $this->statusCodeValidator->validate($curl, $apiUrl);
            // get response
            $response = json_decode($curl->getBody(), true);
            // log response
            $this->logger->logResponse($curl->getStatus(), $response);
            
            if (isset($response['scheduledVisitTime'])) {
                return $response['scheduledVisitTime'];
            }
            return null;
        } catch (\Exception $e) {
            throw new UnauthorizedException(__($e->getMessage()));
        }
    }

    /**
     * Send token time
     *
     * @param array $body
     * @return array
     */
    public function sendTokenTime($body = [])
    {
        try {
            $curl = $this->curlClientFactory->create();
            // get account token
            $accountToken = $this->helper->getAccountToken();
            // get connect token
            $connectToken = $this->getConnectToken($accountToken);
            // headers
            $platformVersion = $this->productMetadata->getVersion();
            $moduleVersion = $this->getModuleVersion();
            $this->addHeaders(
                $curl,
                [
                    "Cache-Control" => "no-cache",
                    "Authorization" => "Bearer " . $connectToken,
                    "WriteTextAI-PlatformVersion" => $platformVersion,
                    "WriteTextAI-PluginVersion" => $moduleVersion,
                    "WriteTextAI-PHPVersion" => phpversion()
                ]
            );
            // body
            $apiUrl = $this->getApiWebUrl() . 'web/Token/time';

            if ($body) {
                $apiUrl .= '?' . http_build_query($body);
            }

            // log request
            $this->logger->logRequest($apiUrl);

            // send request
            $curl->post($apiUrl, []);
            // validate response
            $this->statusCodeValidator->validate($curl, $apiUrl);
            // get response
            $response = json_decode($curl->getBody(), true);
            // log response
            $this->logger->logResponse($curl->getStatus(), $response);
            return isset($response['scheduledVisitTime']) ? $response['scheduledVisitTime'] : null;
        } catch (\Exception $e) {
            throw new UnauthorizedException(__($e->getMessage()));
        }
    }

    /**
     * Get api response
     *
     * @param string $apiPath
     * @param array $body
     * @param string $type
     * @param string $acceptLanguage
     *
     * @return mixed
     */
    public function getApiResponse(
        string $apiPath,
        $body = [],
        $type = 'get',
        $acceptLanguage = null
    ) {
        if (!$acceptLanguage) {
            $user = $this->apiSession->getCurrentUser();
            if ($user) {
                $acceptLanguage = $user->getInterfaceLocale();
            } else {
                $acceptLanguage = 'en-US';
            }
        }
        
        $acceptLanguage = str_replace('_', '-', $acceptLanguage);
        
        try {
            $curl = $this->getCurlClientWithOptimizedSettings();
            // get web token
            $webToken = $this->getUserWebToken();
            // headers
            $platformVersion = $this->productMetadata->getVersion();
            $moduleVersion = $this->getModuleVersion();
            $this->addHeaders(
                $curl,
                [
                    "Content-Type" => "application/json",
                    "Accept-Language" => $acceptLanguage,
                    "Authorization" => "Bearer " . $webToken,
                    "WriteTextAI-PlatformVersion" => $platformVersion,
                    "WriteTextAI-PluginVersion" => $moduleVersion,
                    "WriteTextAI-PHPVersion" => phpversion(),
                    "Expect" => ""
                ]
            );
            // body
            $apiUrl = $this->getSavedRegion() . $apiPath;
            if ($type === 'get' && !empty($body)) {
                $apiUrl .= '?' . http_build_query($body);
            }
            if ($type === 'delete' && !empty($body)) {
                if (is_array($body)) {
                    $apiUrl .= '?' . http_build_query($body);
                }
            }
            // log request
            $this->logger->logRequest($apiUrl, $body);
            $curl->$type($apiUrl, $body);
            // check response
            if ($curl->getStatus() === StatusCodeValidator::UNAUTHORIZED) {
                $this->handleUnauthorizedRequest($curl, $apiUrl, $body, $type);
            }
            // validate response
            $this->versionValidator->validate($curl, $moduleVersion, $apiUrl, $body);
            $this->statusCodeValidator->validate($curl, $apiUrl, $body);
            // get response
            $response = json_decode($curl->getBody(), true);
            // log response
            $this->logger->logResponse($curl->getStatus(), $response);
            // return response
            return $response;
        } catch (VersionException $e) {
            $this->messageManager->getMessages(true);
            $this->messageManager->addError($e->getMessage());
            throw $e;
        }
    }
    
    /**
     * Get api response custom mainly used for DELETE and PUT requests
     *
     * @param string $apiPath
     * @param array $body
     * @param string $type
     * @param string $acceptLanguage
     *
     * @return mixed
     */
    public function getApiResponseCustom(
        string $apiPath,
        $body = [],
        $type = 'get',
        $acceptLanguage = null
    ) {
        if (!$acceptLanguage) {
            $user = $this->apiSession->getCurrentUser();
            if ($user) {
                $acceptLanguage = $user->getInterfaceLocale();
            } else {
                $acceptLanguage = 'en-US';
            }
        }
        
        $acceptLanguage = str_replace('_', '-', $acceptLanguage);
        
        try {
            $curl = $this->customTypeRequest->create();
            // get web token
            $webToken = $this->getUserWebToken();
            // headers
            $platformVersion = $this->productMetadata->getVersion();
            $moduleVersion = $this->getModuleVersion();
            $this->addHeaders(
                $curl,
                [
                    "Content-Type" => "application/json",
                    "Accept-Language" => $acceptLanguage,
                    "Authorization" => "Bearer " . $webToken,
                    "WriteTextAI-PlatformVersion" => $platformVersion,
                    "WriteTextAI-PluginVersion" => $moduleVersion,
                    "WriteTextAI-PHPVersion" => phpversion(),
                    "Expect" => ""
                ]
            );
            // body
            $apiUrl = $this->getSavedRegion() . $apiPath;
            if ($type === 'get' && !empty($body)) {
                $apiUrl .= '?' . http_build_query($body);
            }
            if ($type === 'delete' && !empty($body)) {
                if (is_array($body)) {
                    $apiUrl .= '?' . http_build_query($body);
                }
            }
            // log request
            $this->logger->logRequest($apiUrl, $body);
            // send request
            $curl->$type($apiUrl, $body);
            // check response
            if ($curl->getStatus() === StatusCodeValidator::UNAUTHORIZED) {
                $this->handleUnauthorizedRequest($curl, $apiUrl, $body, $type);
            }
            // validate response
            $this->versionValidator->validate($curl, $moduleVersion, $apiUrl, $body);
            $this->statusCodeValidator->validate($curl, $apiUrl, $body);
            // get response
            $response = json_decode($curl->getBody(), true);
            // log response
            $this->logger->logResponse($curl->getStatus(), $response);
            // return response
            return $response;
        } catch (VersionException $e) {
            $this->messageManager->getMessages(true);
            $this->messageManager->addError($e->getMessage());
            throw $e;
        }
    }

    /**
     * Get web token
     *
     * @param string $email
     * @param string $userId
     * @param string $firstName
     * @param string $lastName
     *
     * @return string
     */
    public function getWebToken(
        $email = null,
        $userId = null,
        $firstName = null,
        $lastName = null
    ) {
        try {
            $curl = $this->curlClientFactory->create();
            // get account token
            $accountToken = $this->helper->getAccountToken();
            // get connect token
            $connectToken = $this->getConnectToken($accountToken);
            // headers
            $platformVersion = $this->productMetadata->getVersion();
            $moduleVersion = $this->getModuleVersion();
            $this->addHeaders(
                $curl,
                [
                    "Cache-Control" => "no-cache",
                    "Authorization" => "Bearer " . $connectToken,
                    "WriteTextAI-PlatformVersion" => $platformVersion,
                    "WriteTextAI-PluginVersion" => $moduleVersion,
                    "WriteTextAI-PHPVersion" => phpversion()
                ]
            );
            // body
            $apiUrl = $this->getApiWebUrl() . 'web/token';
            if ($email) {
                $backendFrontName = $this->backendHelper->getAreaFrontName();
                $params = [
                    'email' => $email,
                    'adminUrl' => $this->urlBuilder->getUrl() . $backendFrontName,
                ];
                if ($userId) {
                    $params['firstName'] = $firstName;
                    $params['lastName'] = $lastName;
                }

                $apiUrl .= '?' . http_build_query($params);
            }
            // log request
            $this->logger->logRequest($apiUrl);
            // send request
            $curl->get($apiUrl);
            // validate response
            $this->statusCodeValidator->validate($curl, $apiUrl);
            // get response
            $response = json_decode($curl->getBody(), true);
            // log response
            $this->logger->logResponse($curl->getStatus(), $response);
            // save web token
            if ($userId) {
                $this->userWebTokenManager->setUserWebToken($userId, $response['access_token'], $email);
            }
            // return web token
            return $response['access_token'];
        } catch (\Exception $e) {
            throw new UnauthorizedException(__($e->getMessage()));
        }
    }

    /**
     * Get connect token
     *
     * @param string $accountToken
     * @return string
     */
    public function getConnectToken($accountToken)
    {
        try {
            $curl = $this->curlClientFactory->create();
            // headers
            $platformVersion = $this->productMetadata->getVersion();
            $moduleVersion = $this->getModuleVersion();
            $this->addHeaders(
                $curl,
                [
                    "Cache-Control" => "no-cache",
                    "Content-Type" => "application/x-www-form-urlencoded",
                    "WriteTextAI-PlatformVersion" => $platformVersion,
                    "WriteTextAI-PluginVersion" => $moduleVersion,
                    "WriteTextAI-PHPVersion" => phpversion()
                ]
            );
            // body
            $apiUrl = $this->getApiAuthUrl() . 'connect/token';
            $body = [
                'grant_type' => 'refresh_token',
                'client_id' => $this->getClientId(),
                'refresh_token' => $accountToken
            ];
            // log request
            $this->logger->logRequest($apiUrl, $body);
            // send request
            $curl->post($apiUrl, $body);
            // validate response
            $this->statusCodeValidator->validate($curl, $apiUrl, $body);
            // get response
            $response = json_decode($curl->getBody(), true);
            // log response
            $this->logger->logResponse($curl->getStatus(), $response);
            // return connect token
            return $response['access_token'];
        } catch (\Exception $e) {
            throw new UnauthorizedException(__($e->getMessage()));
        }
    }

    /**
     * Add headers
     *
     * @param MagentoCurl $curl
     * @param array $params
     * @return void
     */
    protected function addHeaders($curl, $params)
    {
        foreach ($params as $key => $value) {
            $curl->addHeader($key, $value);
        }
    }

    /**
     * Handle unauthorized request
     *
     * @param MagentoCurl $curl
     * @param string $apiUrl
     * @param array $body
     * @param string $type
     * @return void
     */
    protected function handleUnauthorizedRequest($curl, $apiUrl, $body, $type)
    {
        // try again with new token
        $renew = true;
        $webToken = $this->getUserWebToken($renew);
        $curl->addHeader("Authorization", "Bearer " . $webToken);
        $curl->$type($apiUrl, $body);
    }

    /**
     * Get module version
     *
     * @return string
     */
    protected function getModuleVersion()
    {
        return $this->moduleList->getOne(self::MODULE_NAME)['setup_version'];
    }
}
