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

namespace WriteTextAI\WriteTextAI\Model;

use WriteTextAI\WriteTextAI\Model\ApiManager;
use WriteTextAI\WriteTextAI\Model\OptionSource\Filter\Fields;
use WriteTextAI\WriteTextAI\Helper\Store as StoreHelper;
use WriteTextAI\WriteTextAI\Model\UserFeedbackManager;

class GeneratedTextOptimized
{
    /**
     * Maximum retries for failed API calls
     */
    public const MAX_RETRIES = 3;
    /**
     * @var ApiManager
     */
    protected $apiManager;

    /**
     * @var StoreHelper
     */
    protected $storeHelper;

    /**
     * @var UserFeedbackManager
     */
    protected $userFeedbackManager;

    /**
     * Constructor
     *
     * @param ApiManager $apiManager
     * @param StoreHelper $storeHelper
     * @param UserFeedbackManager $userFeedbackManager
     */
    public function __construct(
        ApiManager $apiManager,
        StoreHelper $storeHelper,
        UserFeedbackManager $userFeedbackManager
    ) {
        $this->apiManager = $apiManager;
        $this->storeHelper = $storeHelper;
        $this->userFeedbackManager = $userFeedbackManager;
    }

    /**
     * Save text
     *
     * @param int $storeId
     * @param int $productId
     * @param string $pageTitle
     * @param string $pageDescription
     * @param string $productDescription
     * @param string $productShortDescription
     * @param string $openGraph
     * @param array $selectedFields
     * @param bool $isReviewed
     * @param bool $isAutomatic
     * @return void
     */
    public function saveText(
        $storeId,
        $productId,
        $pageTitle,
        $pageDescription,
        $productDescription,
        $productShortDescription,
        $openGraph,
        $selectedFields,
        $isReviewed = true,
        $isAutomatic = false
    ) {
        $ids = $this->getTextIds($productId, $storeId);

        $fields = [];

        if (in_array('page_title', $selectedFields) && isset($ids['page_title'])
            && !empty($pageTitle)) {
            $fields[] = [
                "textId" => $ids['page_title'],
                "field" => Fields::PAGE_TITLE,
                "value" => $pageTitle,
                "publish" => true,
                "reviewed" => $isReviewed,
                "platform" => "Magento",
                "isAutomatic" => $isAutomatic
            ];
        }
        if (in_array('page_description', $selectedFields) && isset($ids['page_description'])
            && !empty($pageDescription)) {
            $fields[] = [
                "textId" => $ids['page_description'],
                "field" => Fields::PAGE_DESCRIPTION,
                "value" => $pageDescription,
                "publish" => true,
                "reviewed" => $isReviewed,
                "platform" => "Magento",
                "isAutomatic" => $isAutomatic
            ];
        }
        if (in_array('product_description', $selectedFields) && isset($ids['product_description'])
            && !empty($productDescription)) {
            $fields[] = [
                "textId" => $ids['product_description'],
                "field" => Fields::PRODUCT_DESCRIPTION,
                "value" => $productDescription,
                "publish" => true,
                "reviewed" => $isReviewed,
                "platform" => "Magento",
                "isAutomatic" => $isAutomatic
            ];
        }
        if (in_array('short_product_description', $selectedFields) && isset($ids['short_product_description'])
            && !empty($productShortDescription)) {
            $fields[] = [
                "textId" => $ids['short_product_description'],
                "field" => Fields::EXCERPT,
                "value" => $productShortDescription,
                "publish" => true,
                "reviewed" => $isReviewed,
                "platform" => "Magento",
                "isAutomatic" => $isAutomatic
            ];
        }
        if (in_array('open_graph', $selectedFields) && isset($ids['open_graph'])
            && !empty($openGraph)) {
            $fields[] = [
                "textId" => $ids['open_graph'],
                "field" => Fields::OPEN_GRAPH,
                "value" => $openGraph,
                "publish" => true,
                "reviewed" => $isReviewed,
                "platform" => "Magento",
                "isAutomatic" => $isAutomatic
            ];
        }

        $language = $this->storeHelper->getFormattedLanguage($storeId);

        $params = [
            'type' => 'Product',
            'language' => $language,
            'recordId' => $productId,
            'storeId' => $storeId,
            'fields' => $fields
        ];

        if (!empty($fields)) {
            $this->apiCallWithRetry(function () use ($params) {
                return $this->apiManager->save($params);
            });
        }
    }
    
    /**
     * Get text ids
     *
     * @param int $productId
     * @param int $storeId
     * @return array
     */
    public function getTextIds($productId, $storeId)
    {
        $item = [];
        
        $generated = $this->getTextIdsFromGenerated($productId, $storeId);
        if (isset($generated[$productId])) {
            if (isset($generated[$productId][Fields::PAGE_TITLE])) {
                $item['page_title'] = $generated[$productId][Fields::PAGE_TITLE];
            }
            if (isset($generated[$productId][Fields::PAGE_DESCRIPTION])) {
                $item['page_description'] = $generated[$productId][Fields::PAGE_DESCRIPTION];
            }
            if (isset($generated[$productId][Fields::PRODUCT_DESCRIPTION])) {
                $item['product_description'] = $generated[$productId][Fields::PRODUCT_DESCRIPTION];
            }
            if (isset($generated[$productId][Fields::EXCERPT])) {
                $item['short_product_description'] = $generated[$productId][Fields::EXCERPT];
            }
            if (isset($generated[$productId][Fields::OPEN_GRAPH])) {
                $item['open_graph'] = $generated[$productId][Fields::OPEN_GRAPH];
            }
        }

        return $item;
    }

    /**
     * Get text ids from generated
     *
     * @param int $productId
     * @param int $storeId
     * @param string $type
     * @return array
     */
    public function getTextIdsFromGenerated($productId, $storeId, $type = 'Product')
    {
        $response = $this->getGenerated($productId, $storeId, $type);
        $generated = [];

        if (isset($response['records'])) {
            foreach ($response['records'] as $record) {
                $generated = $this->processRecord($record, $storeId, $generated);
            }
        }

        return $generated;
    }

    /**
     * Returns raw data from api
     *
     * @param string $ids
     * @param int $storeId
     * @param string $type
     * @return mixed[]
     */
    public function getGenerated($ids, $storeId, $type = 'Product')
    {
        $language = $this->storeHelper->getFormattedLanguage($storeId);

        $params = [
            "type" => $type,
            "storeId" => $storeId,
            "language" => $language,
            "recordId" => $ids,
            "includeUpdateHistory" => "true"
        ];

        $response = $this->apiCallWithRetry(function () use ($params) {
            return $this->apiManager->getGenerated($params);
        });

        if ($response === null) {
            $response = [];
        }

        return $response;
    }

    /**
     * Process record
     *
     * @param array $record
     * @param int $storeId
     * @param array $generated
     * @return array
     */
    public function processRecord($record, $storeId, $generated)
    {
        foreach ($record['stores'] as $store) {
            if ($store['storeId'] == $storeId) {
                $generated = $this->processStore($store, $record, $generated);
            }
        }

        return $generated;
    }

    /**
     * Process store
     *
     * @param array $store
     * @param array $record
     * @param array $generated
     * @return array
     */
    public function processStore($store, $record, $generated)
    {
        if (isset($store['texts']) && !empty($store['texts'])) {
            if (isset($store['texts'][0]['id'])) {
                $generated[$record['recordId']][$store['field']] = $store['texts'][0]['id'];
            }
        }

        return $generated;
    }

    /**
     * Execute API call with retry logic
     *
     * @param callable $apiCall
     * @param int $maxRetries
     * @return mixed|null
     */
    private function apiCallWithRetry($apiCall, $maxRetries = self::MAX_RETRIES)
    {
        $attempt = 0;
        $lastException = null;
        
        // Clear DNS cache at the start
        if (function_exists('dns_get_record')) {
            clearstatcache(true);
        }
        
        // Clean up any existing cURL connections
        $this->apiManager->cleanupPool();
        
        while ($attempt < $maxRetries) {
            try {
                $attempt++;
                
                // Execute the API call
                $result = $apiCall();
                
                // If successful, return the result
                return $result;
            } catch (\Exception $e) {
                $lastException = $e;
                $errorMessage = $e->getMessage();
                
                // Check for specific SSL/connection errors
                if (strpos($errorMessage, 'certificate verify locations') !== false ||
                    strpos($errorMessage, 'getaddrinfo() thread failed') !== false ||
                    strpos($errorMessage, 'SSL') !== false ||
                    strpos($errorMessage, 'cURL') !== false) {
                    // Exponential backoff with jitter
                    $waitTime = min(pow(2, $attempt) * 1000000, 10000000); // Max 10 seconds
                    $waitTime += rand(0, 1000000); // Add up to 1 second jitter
                    
                    usleep($waitTime);
                    
                    // Force garbage collection to free resources
                    gc_collect_cycles();
                } elseif ($attempt < $maxRetries) {
                    // For other errors, use shorter delay
                    usleep(500000); // 0.5 seconds
                } else {
                    // Final attempt failed, log and break
                    break;
                }
            }
        }
        
        // All retries exhausted
        return null;
    }

    /**
     * Process generated text by store id
     *
     * @param string $ids
     * @param int $storeId
     * @param bool $isTransfer
     * @param array $text
     * @return mixed[]
     */
    public function getGeneratedByStoreId($ids, $storeId, $isTransfer = false, $text = [])
    {
        $response = !empty($text) ? $text : $this->getGenerated($ids, $storeId);

        $generated = [];

        if (!isset($response['records'])) {
            return $generated;
        }

        foreach ($response['records'] as $record) {
            foreach ($record['stores'] as $store) {
                if ($store['storeId'] == $storeId) {
                    $generatedValue = $this->getGeneratedValue($store);

                    if ($isTransfer) {
                        $history = $this->getTextHistory($store);
                        $transferred = isset($history['publish']) && isset($history['reviewed']) ? (
                            $history['publish'] && $history['reviewed'] ? true : false
                        ) : false;
                        $generated[$record['recordId']]['status'][$store['field']] = $transferred;
                    }
                    
                    if ($generatedValue !== null) {
                        $generated[$record['recordId']][$store['field']] = $generatedValue;
                    }
                }
            }
        }

        return $generated;
    }

    /**
     * Get generated value
     *
     * @param array $store
     *
     * @return string|null
     */
    public function getGeneratedValue($store)
    {
        if (!isset($store['texts']) || empty($store['texts'])) {
            return null;
        }

        if (isset($store['texts'][0]['value']) && $store['texts'][0]['value'] !== null) {
            return $store['texts'][0]['value'];
        }

        if (isset($store['texts'][0]['outputs']) && !empty($store['texts'][0]['outputs'])) {
            return $store['texts'][0]['outputs'][0];
        }

        return null;
    }

    /**
     * Get history
     *
     * @param array $store
     *
     * @return string|null
     */
    public function getTextHistory($store)
    {
        if (!isset($store['texts']) || empty($store['texts'])) {
            return null;
        }

        if (isset($store['texts'][0]['history']) && !empty($store['texts'][0]['history'])) {
            return $store['texts'][0]['history'][0];
        }

        return null;
    }
}
