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

namespace WriteTextAI\WriteTextAI\Controller\Adminhtml\Edit;

use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Framework\Controller\ResultFactory;
use WriteTextAI\WriteTextAI\Model\ApiManager;
use WriteTextAI\WriteTextAI\Model\Api\Keywords as ApiKeywords;
use WriteTextAI\WriteTextAI\Model\Api\Session as ApiSession;
use WriteTextAI\WriteTextAI\Model\GeneratedText;
use WriteTextAI\WriteTextAI\Model\KeywordIdeaFiltersManager;
use WriteTextAI\WriteTextAI\Model\UserFieldsSelectedManager;
use WriteTextAI\WriteTextAI\Model\GenerateOptionsManager;
use WriteTextAI\WriteTextAI\Model\AiProductManager;
use Magento\User\Model\ResourceModel\User\CollectionFactory;
use WriteTextAI\WriteTextAI\Helper\Data as HelperData;
use WriteTextAI\WriteTextAI\Helper\Fields as FieldHelper;
use WriteTextAI\WriteTextAI\Model\OptionSource\Filter\Stores as StoresFilter;
use Magento\Catalog\Model\Product\Visibility;
use WriteTextAI\WriteTextAI\Helper\Product as ProductHelper;
use WriteTextAI\WriteTextAI\Helper\Generate as GenerateHelper;
use WriteTextAI\WriteTextAI\ViewModel\Settings;
use WriteTextAI\WriteTextAI\ViewModel\UserFieldsSelected;
use WriteTextAI\WriteTextAI\Helper\ProductAttributes as ProductAttributesHelper;
use Magento\Catalog\Helper\Output as OutputHelper;
use WriteTextAI\WriteTextAI\Helper\ReviewStatus as ReviewHelper;
use WriteTextAI\WriteTextAI\Model\MarkReview;
use WriteTextAI\WriteTextAI\Helper\Store as StoreHelper;
use Magento\Catalog\Helper\Image as ImageHelper;
use WriteTextAI\WriteTextAI\Helper\Image as WtaImageHelper;
use WriteTextAI\WriteTextAI\Helper\Optimization as OptimizationHelper;

class Index extends Action
{
    /**
     * @var ApiManager
     */
    protected $apiManager;

    /**
     * @var ApiKeywords
     */
    protected $apiKeywords;

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

    /**
     * @var KeywordIdeaFiltersManager
     */
    protected $keywordIdeaFiltersManager;

    /**
     * @var UserFieldsSelected
     */
    protected $userFieldsSelected;

    /**
     * @var GenerateOptionsManager
     */
    protected $generateOptionsManager;

    /**
     * @var AiProductManager
     */
    protected $aiProductManager;

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

    /**
     * @var FieldHelper
     */
    protected $fieldHelper;

    /**
     * @var StoresFilter
     */
    protected $storesFilter;

    /**
     * @var ProductHelper
     */
    protected $productHelper;

    /**
     * @var GenerateHelper
     */
    protected $generateHelper;

    /**
     * @var Settings
     */
    protected $settings;

    /**
     * @var ProductAttributesHelper
     */
    protected $productAttributesHelper;

    /**
     * @var OutputHelper
     */
    protected $outputHelper;

    /**
     * @var ReviewHelper
     */
    protected $reviewHelper;

    /**
     * @var CollectionFactory
     */
    protected $userCollectionFactory;

    /**
     * @var MarkReview
     */
    protected $markReview;

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

    /**
     * @var array
     */
    protected $directivePatterns;

    /**
     * @var ImageHelper
     */
    protected $imageHelper;

    /**
     * @var WtaImageHelper
     */
    protected $wtaImageHelper;

    /**
     * @var OptimizationHelper
     */
    protected $optimizationHelper;

    /**
     * Constructor
     *
     * @param Context $context
     * @param ApiManager $apiManager
     * @param ApiKeywords $apiKeywords
     * @param ApiSession $apiSession
     * @param CollectionFactory $userCollectionFactory
     * @param GeneratedText $generatedText
     * @param KeywordIdeaFiltersManager $keywordIdeaFiltersManager
     * @param UserFieldsSelected $userFieldsSelected
     * @param GenerateOptionsManager $generateOptionsManager
     * @param AiProductManager $aiProductManager
     * @param HelperData $helper
     * @param FieldHelper $fieldHelper
     * @param StoresFilter $storesFilter
     * @param ProductHelper $productHelper
     * @param GenerateHelper $generateHelper
     * @param Settings $settings
     * @param ProductAttributesHelper $productAttributesHelper
     * @param OutputHelper $outputHelper
     * @param ReviewHelper $reviewHelper
     * @param MarkReview $markReview
     * @param StoreHelper $storeHelper
     * @param ImageHelper $imageHelper
     * @param WtaImageHelper $wtaImageHelper
     * @param OptimizationHelper $optimizationHelper
     * @param array $directivePatterns
     */
    public function __construct(
        Context $context,
        ApiManager $apiManager,
        ApiKeywords $apiKeywords,
        ApiSession $apiSession,
        CollectionFactory $userCollectionFactory,
        GeneratedText $generatedText,
        KeywordIdeaFiltersManager $keywordIdeaFiltersManager,
        UserFieldsSelected $userFieldsSelected,
        GenerateOptionsManager $generateOptionsManager,
        AiProductManager $aiProductManager,
        HelperData $helper,
        FieldHelper $fieldHelper,
        StoresFilter $storesFilter,
        ProductHelper $productHelper,
        GenerateHelper $generateHelper,
        Settings $settings,
        ProductAttributesHelper $productAttributesHelper,
        OutputHelper $outputHelper,
        ReviewHelper $reviewHelper,
        MarkReview $markReview,
        StoreHelper $storeHelper,
        ImageHelper $imageHelper,
        WtaImageHelper $wtaImageHelper,
        OptimizationHelper $optimizationHelper,
        $directivePatterns = []
    ) {
        $this->apiManager = $apiManager;
        $this->apiKeywords = $apiKeywords;
        $this->apiSession = $apiSession;
        $this->userCollectionFactory = $userCollectionFactory;
        $this->generatedText = $generatedText;
        $this->keywordIdeaFiltersManager = $keywordIdeaFiltersManager;
        $this->userFieldsSelected = $userFieldsSelected;
        $this->generateOptionsManager = $generateOptionsManager;
        $this->aiProductManager = $aiProductManager;
        $this->helper = $helper;
        $this->fieldHelper = $fieldHelper;
        $this->directivePatterns = $directivePatterns;
        $this->storesFilter = $storesFilter;
        $this->productHelper = $productHelper;
        $this->generateHelper = $generateHelper;
        $this->settings = $settings;
        $this->productAttributesHelper = $productAttributesHelper;
        $this->outputHelper = $outputHelper;
        $this->reviewHelper = $reviewHelper;
        $this->markReview = $markReview;
        $this->storeHelper = $storeHelper;
        $this->imageHelper = $imageHelper;
        $this->wtaImageHelper = $wtaImageHelper;
        $this->optimizationHelper = $optimizationHelper;
        parent::__construct($context);
    }

    /**
     * Execute
     *
     * @return \Magento\Framework\Controller\Result\Json
     */
    public function execute()
    {
        $response = $this->resultFactory->create(ResultFactory::TYPE_JSON);

        try {
            $request = $this->getRequest();
            $productId = $request->getParam('id');
            $storeId = $request->getParam('store_id');

            $product = $this->productHelper->getProduct($productId, $storeId);

            $productName = $this->outputHelper->productAttribute(
                $product,
                $product->getName(),
                'name'
            );

            $aiProduct = $this->aiProductManager->getProduct($productId, $storeId);
            $result = [
                'success' => true,
                'users' => $this->getUsers(),
                'stores' => array_values($this->storesFilter->toOptionArray())
            ];

            $productData = $this->getProductData(
                $product,
                $productId,
                $storeId,
                $productName
            );
            $textsData = $this->getTextsData(
                $product,
                $productId,
                $storeId
            );
            $userPreferences = $this->getUserPreferences();
            $attributesData = $this->getAttributesData(
                $aiProduct,
                $product
            );
            $additionalPrompt = $this->getAdditionalPrompt($aiProduct);
            $optimizationData = $this->apiKeywords->getOptimization(
                $storeId,
                $productId,
                'Product'
            );
            $result['optimization'] = $optimizationData;
            $selectedKeywords = $this->getSelectedKeywords(
                $optimizationData,
                $product->getName(),
                $storeId,
                $productId
            );
            $imagesData = $this->getImageData(
                $product,
                $productId,
                $storeId,
                $optimizationData
            );
            $settingsData = $this->getSettingsData(
                $storeId,
                $productId,
                $productName,
                $selectedKeywords
            );

            $result = array_merge(
                $result,
                $productData,
                $settingsData,
                $textsData,
                $imagesData,
                $userPreferences,
                $attributesData,
                $selectedKeywords,
                $additionalPrompt
            );
        } catch (\Exception $e) {
            $result = [
                'success' => false,
                'message' => __(
                    'A system error has occurred. Please try again. If the issue persists,'
                    . ' please contact our support team at support@writetext.ai.'
                ),
                'error' => $e->getMessage(),
                'trace' => $e->getTrace()
            ];
        }

        $response->setData($result);

        return $response;
    }

    /**
     * Get product data
     *
     * @param object $product
     * @param int $productId
     * @param int $storeId
     * @param string $productName
     *
     * @return array
     */
    private function getProductData(
        $product,
        $productId,
        $storeId,
        $productName
    ) {
        $currentUser = $this->apiSession->getCurrentUser();

        $locationCode = $this->keywordIdeaFiltersManager->getLocationCode($storeId);

        $frontendStoreId = $this->storeHelper->getFrontendStoreId($storeId);

        $_product = $product;

        $productUrl = $_product->setStoreId($frontendStoreId)->getUrlInStore();
        
        $languageCode = $this->storeHelper->getRegionIndependentLanguage($storeId);
        $languageCode = strtoupper($languageCode);

        return [
            'product_name' => $productName,
            'sku' => $product->getSku(),
            'product_id' => $productId,
            'visibility' => $product->getVisibility() != Visibility::VISIBILITY_NOT_VISIBLE,
            'store_id' => $storeId,
            'store_scope' => $this->storeHelper->getStoreScope($storeId),
            'link_preview_url' => $productUrl,
            'preview_changes_url' => $_product->setStoreId($frontendStoreId)->getUrlInStore([
                '_query' => [
                    'wtai-preview' => base64_encode($currentUser->getId())
                ]
            ]),
            'language' => $languageCode,
            'country' => $this->keywordIdeaFiltersManager->getCountryCode($storeId),
            'location' => $locationCode,
        ];
    }

    /**
     * Get user preferences
     *
     * @return array
     */
    private function getUserPreferences()
    {
        // $userDescMin = $this->userFieldsSelected->getUserFieldsSelected(
        //     UserFieldsSelectedManager::ACTION_TYPE_DESC_MIN,
        //     '',
        //     ''
        // );
        // $userDescMax = $this->userFieldsSelected->getUserFieldsSelected(
        //     UserFieldsSelectedManager::ACTION_TYPE_DESC_MAX,
        //     '',
        //     ''
        // );
        // $userExcMin = $this->userFieldsSelected->getUserFieldsSelected(
        //     UserFieldsSelectedManager::ACTION_TYPE_EXC_MIN,
        //     '',
        //     ''
        // );
        // $userExcMax = $this->userFieldsSelected->getUserFieldsSelected(
        //     UserFieldsSelectedManager::ACTION_TYPE_EXC_MAX,
        //     '',
        //     ''
        // );

        // if (!$userDescMin) {
            $userDescMin = $this->helper->getDescriptionSettings('min');
        // }
        // if (!$userDescMax) {
            $userDescMax = $this->helper->getDescriptionSettings('max');
        // }
        // if (!$userExcMin) {
            $userExcMin = $this->helper->getExcerptSettings('min');
        // }
        // if (!$userExcMax) {
            $userExcMax = $this->helper->getExcerptSettings('max');
        // }

        $searchIntent = $this->userFieldsSelected->getUserFieldsSelected(
            UserFieldsSelectedManager::ACTION_TYPE_SEARCH_INTENT,
            'product',
            ''
        );
        if ($searchIntent) {
            $searchIntent = explode(',', $searchIntent);
        } else {
            $searchIntent = explode(',', 'transactional');
        }

        return [
            'guide_steps' => filter_var($this->userFieldsSelected->getUserFieldsSelected(
                UserFieldsSelectedManager::ACTION_TYPE_GUIDE_STEPS,
                '',
                'edit'
            ), FILTER_VALIDATE_BOOLEAN),
            'selected_fields' => $this->userFieldsSelected->getUserFieldsSelected(
                UserFieldsSelectedManager::ACTION_TYPE_SINGLE_EDITOR_FIELDS,
                'product',
                'edit'
            ),
            'highlight_pronouns' => $this->userFieldsSelected->getUserFieldsSelected(
                UserFieldsSelectedManager::ACTION_TYPE_HIGHLIGHT_PRONOUNS,
                '',
                'edit'
            ),
            'custom_tone' => $this->userFieldsSelected->getUserFieldsSelected(
                UserFieldsSelectedManager::ACTION_TYPE_CUSTOM_TONE,
                '',
                ''
            ),
            'custom_style' => $this->userFieldsSelected->getUserFieldsSelected(
                UserFieldsSelectedManager::ACTION_TYPE_CUSTOM_STYLE,
                '',
                ''
            ),
            'gallery' => $this->userFieldsSelected->getUserFieldsSelected(
                UserFieldsSelectedManager::ACTION_TYPE_GALLERY,
                'product',
                'edit'
            ),
            'auto_generate_after_ko' => filter_var($this->userFieldsSelected->getUserFieldsSelected(
                UserFieldsSelectedManager::ACTION_TYPE_AUTO_GENERATE_AFTER_KO,
                '',
                'edit'
            ), FILTER_VALIDATE_BOOLEAN),
            'desc_min' => $userDescMin,
            'desc_max' => $userDescMax,
            'exc_min' => $userExcMin,
            'exc_max' => $userExcMax,
            'search_intent_selected' => $searchIntent
        ];
    }

    /**
     * Get attributes data
     *
     * @param object $aiProduct
     * @param object $product
     *
     * @return array
     */
    private function getAttributesData(
        $aiProduct,
        $product
    ) {
        return [
            'product_attributes' => $this->productAttributesHelper->getProductAttributes($product),
            'product_attributes_selected' => $this->productAttributesHelper
                ->getProductAttributesSelected($aiProduct),
            'other_details_checked' => (bool) $aiProduct->getOtherDetailsChecked(),
            'other_details' => $aiProduct->getOtherDetails()
        ];
    }

    /**
     * Get additional prompt
     *
     * @param object $aiProduct
     *
     * @return array
     */
    private function getAdditionalPrompt($aiProduct)
    {
        return [
            'additional_prompt' => $aiProduct->getAdditionalPrompt()
        ];
    }

    /**
     * Get settings parameters
     *
     * @param int $storeId
     * @param int $productId
     * @param string $productName
     * @param array $selectedKeywords
     *
     * @return array
     */
    private function getSettingsData($storeId, $productId, $productName, $selectedKeywords)
    {
        $tones = $this->generateHelper->updateDefault(
            $this->generateOptionsManager->getOptions('tones'),
            $this->userFieldsSelected->getUserFieldsSelected(
                UserFieldsSelectedManager::ACTION_TYPE_TONES,
                '',
                ''
            )
        );

        $userStyles = $this->userFieldsSelected->getUserFieldsSelected(
            UserFieldsSelectedManager::ACTION_TYPE_STYLE,
            '',
            ''
        );

        $styles = $this->generateHelper->updateDefault(
            $this->generateOptionsManager->getOptions('styles'),
            $userStyles ? [$userStyles] : []
        );

        $audiences = $this->generateHelper->updateDefault(
            $this->generateOptionsManager->getOptions('audience'),
            $this->userFieldsSelected->getUserFieldsSelected(
                UserFieldsSelectedManager::ACTION_TYPE_AUDIENCES,
                '',
                ''
            )
        );

        $audience = [];
        $keywords = [$productName];
        if (isset($selectedKeywords['selected_keywords'])) {
            $keywordsData = $selectedKeywords['selected_keywords'];
            if (isset($selectedKeywords['optimizing_keywords']) && !empty($selectedKeywords['optimizing_keywords'])) {
                $keywordsData = $selectedKeywords['optimizing_keywords'];
            }
            //make sure selected_keywords are all small letters
            $keywordsData = array_map('strtolower', $keywordsData);
            $keywords = array_merge($keywords, $keywordsData);
            $audience = $this->apiManager->getSuggestedAudience($storeId, $productId, $keywords);
        }

        $formalLanguageSupport = $this->settings->getSettings('formalLanguageSupport');

        $langCode = $this->storeHelper->getRegionIndependentLanguage($storeId);

        $formalLanguages = [];
        if (in_array($langCode, $formalLanguageSupport)) {
            $formalLanguages = $this->settings->getFormalLanguages($langCode);
        }

        return [
            'tones' => $tones,
            'styles' => $styles,
            'audience' => $audiences,
            'selected_audience' => $audience['selected'] ?? [],
            'suggested_audience' => $audience['values'] ?? [],
            'formal_language_support' => $formalLanguageSupport,
            'formal_languages' => $formalLanguages,
            'disallowed_combinations' => $this->settings->getSettings('disallowedCombinations'),
            'credits' => $this->settings->getSettings('credits'),
            'global_triggers' => $this->optimizationHelper->getGlobalTriggers('Product')
        ];
    }

    /**
     * Get texts parameters
     *
     * @param object $product
     * @param int $productId
     * @param int $storeId
     *
     * @return array
     */
    private function getTextsData($product, $productId, $storeId)
    {
        $generatedText = $this->generatedText->getGenerated($productId, $storeId);

        $mapping = $this->fieldHelper->getMappingSettingsValue();
        $customOpenGraph = $this->helper->getCustomOpenGraph();

        $pageTitle = $this->fieldHelper->getAttributesValues($product, 'page_title');
        $pageDescription = $this->fieldHelper->getAttributesValues($product, 'page_description');
        $productDescription = $this->fieldHelper->getAttributesValues($product, 'product_description');
        $shortProductDescription = $this->fieldHelper->getAttributesValues(
            $product,
            'short_product_description'
        );
        $openGraph = $this->fieldHelper->getAttributesValues($product, 'open_graph');

        return [
            'api_texts' => $generatedText,
            'text_fields' => $this->generatedText->getTextFields($productId, $storeId, false, $generatedText),
            'product_description' => $productDescription,
            'product_short_description' => $shortProductDescription,
            'product_meta_title' => $pageTitle,
            'product_meta_description' => $pageDescription,
            'open_graph' => $customOpenGraph ? $openGraph : '',
            'directiveRegex' => $this->getDirectivePatterns(),
            'product_description_directive_exists' => $this->fieldHelper->isDirectiveExists(
                $product,
                'product_description'
            ),
            'product_short_description_directive_exists' => $this->fieldHelper->isDirectiveExists(
                $product,
                'short_product_description'
            ),
            'textfields_statuses' => $this->generatedText->getTextFieldsStatuses(
                $productId,
                $storeId,
                $generatedText
            ),
            'use_default' => [
                'page_title' => $this->fieldHelper->isUsingDefaultValues(
                    $product,
                    $storeId,
                    $mapping['page_title']
                ),
                'page_description' => $this->fieldHelper->isUsingDefaultValues(
                    $product,
                    $storeId,
                    $mapping['page_description']
                ),
                'product_description' => $this->fieldHelper->isUsingDefaultValues(
                    $product,
                    $storeId,
                    $mapping['product_description']
                ),
                'short_product_description' => $this->fieldHelper->isUsingDefaultValues(
                    $product,
                    $storeId,
                    $mapping['short_product_description']
                ),
                'open_graph' => isset($mapping['open_graph']) ?
                    $this->fieldHelper->isUsingDefaultValues($product, $storeId, $mapping['open_graph']) : false
            ],
            'reviewed' => $this->markReview->getReviewed($productId, $storeId, $generatedText),
            'review_status' => $this->reviewHelper->getReviewStatus($productId, $storeId),
            'feedback' => $this->generatedText->getTextsFeedback($generatedText),
            'keywords_used' => $this->generatedText->getTextsKeywordsUsed($generatedText),
            'templates_used' => $this->generatedText->getTextFieldsTemplate($productId, $storeId, $generatedText)
        ];
    }

    /**
     * Get images parameters
     *
     * @param object $product
     * @param int $productId
     * @param int $storeId
     * @param array $optimizationData
     *
     * @return array
     */
    private function getImageData(
        $product,
        $productId,
        $storeId,
        $optimizationData
    ) {
        $language = $this->storeHelper->getFormattedLanguage($storeId);
        $origImageHelper = $this->imageHelper->init($product, 'product_listing_thumbnail_preview');
        $thumbnail = $origImageHelper->getUrl();

        return [
            'thumbnail' => $thumbnail,
            'thumbnail_id' => $this->wtaImageHelper->getImageValue($product),
            'images' => $this->productHelper->getImages($product, $storeId, $language),
            'api_thumbnail' => $optimizationData['imageId'] ?? ''
        ];
    }

    /**
     * Get directives regular expression
     *
     * @return array
     * @see \Magento\Framework\Filter\Template
     */
    private function getDirectivePatterns()
    {
        $patterns = [];
        foreach ($this->directivePatterns as $pattern) {
            $pattern = ltrim($pattern, '/');
            $pattern = rtrim($pattern, '/si');
            $patterns[] = $pattern;
        }

        $patterns[] = FieldHelper::MEDIA_PATTERN;
        return $patterns;
    }
    
    /**
     * Get users
     *
     * @return array
     */
    private function getUsers()
    {
        $collection = $this->userCollectionFactory->create();
        $users = [];

        foreach ($collection as $user) {
            $users[] = [
                'id' => $user->getUserId(),
                'name' => $user->getFirstname() . ' ' . $user->getLastname()
            ];
        }

        return $users;
    }

    /**
     * Get selected keywords
     *
     * @param array $optimizationData
     * @param string $productName
     * @param int $storeId
     * @param int $productId
     */
    private function getSelectedKeywords($optimizationData, $productName, $storeId, $productId)
    {
        /**
         * Get selected keywords for optimizationData
         */
        $keywords = [];
        $optimizingKeywords = [];
        if (empty($optimizationData)) {
            $invalidChars = '/[,\.!@%^*()={};~`<>?\\\\|]/';
            $productName = preg_replace($invalidChars, '', $productName);
            $seedKeywords = [$productName];
            $this->apiKeywords->saveManualKeywords($storeId, $productId, $seedKeywords, 'Product');
            $keywords = $seedKeywords;
        } else {
            $keywords = $optimizationData['manualKeywords'] ?? [];
            $optimizingKeywords = $optimizationData['optimizingKeywords'] ?? [];
        }

        return [
            'selected_keywords' => $keywords,
            'optimizing_keywords' => $optimizingKeywords
        ];
    }
}
