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

namespace WriteTextAI\WriteTextAI\Ui\DataProvider\Products;

use Magento\Framework\Api\FilterBuilder;
use Magento\Framework\Api\Search\ReportingInterface;
use Magento\Framework\Api\Search\SearchCriteriaBuilder;
use Magento\Framework\Api\Search\FilterGroupBuilder;
use Magento\Framework\App\RequestInterface;
use Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider;
use WriteTextAI\WriteTextAI\Model\ApiManager;
use WriteTextAI\WriteTextAI\Model\Api\Session as ApiSession;
use WriteTextAI\WriteTextAI\Model\Api\Keywords;
use WriteTextAI\WriteTextAI\Model\GeneratedText;
use WriteTextAI\WriteTextAI\Model\OptionSource\Filter\Categories as CategoriesFilterOptions;
use WriteTextAI\WriteTextAI\Model\OptionSource\Filter\Fields as Fields;
use Magento\Backend\Model\UrlInterface;
use WriteTextAI\WriteTextAI\Model\OptionSource\Filter\AiStatus;
use WriteTextAI\WriteTextAI\Helper\Filters as FiltersHelper;
use Magento\Catalog\Helper\Output as OutputHelper;
use WriteTextAI\WriteTextAI\Helper\Data as HelperData;
use WriteTextAI\WriteTextAI\Api\Data\AiProductInterface;
use WriteTextAI\WriteTextAI\ViewModel\Premium;
use WriteTextAI\WriteTextAI\Helper\Fields as FieldHelper;
use Magento\Store\Model\Store;
use Magento\Framework\Stdlib\DateTime\TimezoneInterface;
use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory as ProductCollectionFactory;
use Magento\Catalog\Model\Product\Attribute\Source\Status as ProductStatus;
use WriteTextAI\WriteTextAI\Helper\Store as StoreHelper;
use WriteTextAI\WriteTextAI\Helper\Grid as GridHelper;
use WriteTextAI\WriteTextAI\Model\Magento\User as MagentoUser;
use WriteTextAI\WriteTextAI\Model\Config\Source\AutomaticTextOptimization;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Catalog\Model\ResourceModel\Product\Gallery as ProductGallery;
use WriteTextAI\WriteTextAI\WtaiTrait\PremiumDataTrait;

class ListingDataProvider extends DataProvider
{
    use PremiumDataTrait;

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

    /**
     * @var ApiManager
     */
    protected $apiManager;

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

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

    /**
     * @var FiltersHelper
     */
    protected $filtersHelper;

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

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

    /**
     * @var FilterGroupBuilder
     */
    protected $filterGroupBuilder;

    /**
     * @var Premium
     */
    protected $premium;

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

    /**
     * @var TimezoneInterface
     */
    protected $timezone;

    /**
     * @var ProductCollectionFactory
     */
    protected $productCollectionFactory;

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

    /**
     * @var GridHelper
     */
    protected $gridHelper;

    /**
     * @var MagentoUser
     */
    protected $magentoUser;

    /**
     * @var Keywords
     */
    protected $keywords;

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

    /**
     * @var ProductGallery
     */
    protected $productGalleryResource;

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

    /**
     * Constructor
     *
     * @param string $name
     * @param string $primaryFieldName
     * @param string $requestFieldName
     * @param ReportingInterface $reporting
     * @param SearchCriteriaBuilder $searchCriteriaBuilder
     * @param RequestInterface $request
     * @param FilterBuilder $filterBuilder
     * @param GeneratedText $generatedText
     * @param ApiManager $apiManager
     * @param ApiSession $apiSession
     * @param UrlInterface $urlBuilder
     * @param FiltersHelper $filtersHelper
     * @param OutputHelper $outputHelper
     * @param HelperData $helperData
     * @param FilterGroupBuilder $filterGroupBuilder
     * @param Premium $premium
     * @param FieldHelper $fieldHelper
     * @param TimezoneInterface $timezone
     * @param ProductCollectionFactory $productCollectionFactory
     * @param StoreHelper $storeHelper
     * @param GridHelper $gridHelper
     * @param MagentoUser $magentoUser
     * @param Keywords $keywords
     * @param StoreManagerInterface $storeManager
     * @param ProductGallery $productGalleryResource
     * @param array $meta
     * @param array $data
     */
    public function __construct(
        $name,
        $primaryFieldName,
        $requestFieldName,
        ReportingInterface $reporting,
        SearchCriteriaBuilder $searchCriteriaBuilder,
        RequestInterface $request,
        FilterBuilder $filterBuilder,
        GeneratedText $generatedText,
        ApiManager $apiManager,
        ApiSession $apiSession,
        UrlInterface $urlBuilder,
        FiltersHelper $filtersHelper,
        OutputHelper $outputHelper,
        HelperData $helperData,
        FilterGroupBuilder $filterGroupBuilder,
        Premium $premium,
        FieldHelper $fieldHelper,
        TimezoneInterface $timezone,
        ProductCollectionFactory $productCollectionFactory,
        StoreHelper $storeHelper,
        GridHelper $gridHelper,
        MagentoUser $magentoUser,
        Keywords $keywords,
        StoreManagerInterface $storeManager,
        ProductGallery $productGalleryResource,
        array $meta = [],
        array $data = []
    ) {
        $this->generatedText = $generatedText;
        $this->apiManager = $apiManager;
        $this->apiSession = $apiSession;
        $this->urlBuilder = $urlBuilder;
        $this->filtersHelper = $filtersHelper;
        $this->outputHelper = $outputHelper;
        $this->helperData = $helperData;
        $this->filterGroupBuilder = $filterGroupBuilder;
        $this->premium = $premium;
        $this->fieldHelper = $fieldHelper;
        $this->timezone = $timezone;
        $this->productCollectionFactory = $productCollectionFactory;
        $this->storeHelper = $storeHelper;
        $this->gridHelper = $gridHelper;
        $this->magentoUser = $magentoUser;
        $this->keywords = $keywords;
        $this->storeManager = $storeManager;
        $this->productGalleryResource = $productGalleryResource;
        parent::__construct(
            $name,
            $primaryFieldName,
            $requestFieldName,
            $reporting,
            $searchCriteriaBuilder,
            $request,
            $filterBuilder,
            $meta,
            $data
        );
    }

    // phpcs:disable Generic.Metrics.NestingLevel.TooHigh
    /**
     * @inheritdoc
     */
    public function addFilter(\Magento\Framework\Api\Filter $filter)
    {
        $filters = $this->request->getParam('filters');
        
        $storeId = !isset($filters['store_id']) ? Store::DEFAULT_STORE_ID : $filters['store_id'];

        if ($filter->getField() == 'writetextai_status') {
            if (!isset($filters['writetextai_status'])) {
                $filters['writetextai_status'] = [
                    'ai_fields' => [
                        'page title',
                        'page description',
                        'product description',
                        'excerpt',
                        'open graph text',
                        'image_alt_text'
                    ],
                    'status' => 'all',
                    'no_activity_days' => 7,
                    'auto_text_optimizations' => AutomaticTextOptimization::SHOW_ALL,
                    'templates_fields' => AiStatus::TEMPLATES_SHOW_ALL,
                    'filter_by' => 'text_status',
                    'alt_text_field' => 'image_alt_text'
                ];
            }
            $filters = $filters['writetextai_status'];
            $filters['store_id'] = $storeId;
            $status = $filters['status'];

            switch ($filters['filter_by']) {
                case 'text_status':
                    switch ($status) {
                        case AiStatus::WRITETEXTAI:
                            if (!isset($filters['ai_fields']) || !isset($filters['ai_status'])) {
                                break;
                            }
                            
                            $selectedFields = $filters['ai_fields'];
                                
                            $imageAltTextSelected = in_array(AiStatus::IMAGE_ALT_TEXT, $filters['ai_fields']);
                            if ($imageAltTextSelected) {
                                $productIdsThroughImageAltText = $this->getFilterByImageAltText($filters);
                                $selectedFields = array_filter($filters['ai_fields'], function ($field) {
                                    return $field !== AiStatus::IMAGE_ALT_TEXT;
                                });
                            }

                            $productIds = [];
                            if (!empty($selectedFields)) {
                                $productIds = $this->getFilterByAiStatus($filters);
                            }

                            if (!empty($productIdsThroughImageAltText)) {
                                $productIds = array_unique(
                                    array_merge($productIds, $productIdsThroughImageAltText)
                                );
                            }

                            $filter = $this->filterBuilder->setField('entity_id')
                                ->setValue($productIds)
                                ->setConditionType('in')
                                ->create();
                            $this->searchCriteriaBuilder->addFilter($filter);
                            break;
                        case AiStatus::NO_ACTIVITY:
                            if (isset($filters['no_activity_days'])) {
                                $dates = $this->filtersHelper->getLastNumberOfDays($filters['no_activity_days']);
                                $actions = [
                                    AiProductInterface::GENERATED_AT,
                                    AiProductInterface::TRANSFERRED_AT,
                                    AiProductInterface::EDITED_AT,
                                    AiProductInterface::REVIEWED_AT
                                ];
                                foreach ($actions as $action) {
                                    $filterLt = $this->filterBuilder->setField($action)
                                        ->setValue($dates['start_date'])
                                        ->setConditionType('lt')
                                        ->create();
                                    $filterNull = $this->filterBuilder->setField($action)
                                        ->setConditionType('null')
                                        ->create();
                                    $filtergroup = $this->filterGroupBuilder
                                        ->addFilter($filterLt)
                                        ->addFilter($filterNull)
                                        ->create();
                                    $this->searchCriteriaBuilder->addFilterGroup($filtergroup);
                                }
                            }
                            break;
                        case AiStatus::NOT_GENERATED:
                            if (isset($filters['ai_fields'])) {
                                $filters['ai_status'] = [
                                    AiStatus::GENERATED,
                                    AiStatus::TRANSFERRED,
                                    AiStatus::EDITED,
                                    AiStatus::REVIEWED,
                                    AiStatus::FOR_TRANSFERRING
                                ];

                                $selectedFields = $filters['ai_fields'];

                                $imageAltTextSelected = in_array(AiStatus::IMAGE_ALT_TEXT, $filters['ai_fields']);
                                if ($imageAltTextSelected) {
                                    $productIdsThroughImageAltText = $this->getFilterByImageAltText($filters);
                                    $selectedFields = array_filter($filters['ai_fields'], function ($field) {
                                        return $field !== AiStatus::IMAGE_ALT_TEXT;
                                    });
                                }

                                $productIds = [];
                                if (!empty($selectedFields)) {
                                    $productIds = $this->getFilterByAiStatus($filters);
                                }

                                if (!empty($productIdsThroughImageAltText)) {
                                    $productIds = array_unique(
                                        array_merge($productIds, $productIdsThroughImageAltText)
                                    );
                                }
                                if (!empty($productIds)) {
                                    $filter = $this->filterBuilder->setField('entity_id')
                                        ->setValue($productIds)
                                        ->setConditionType('nin')
                                        ->create();
                                    $this->searchCriteriaBuilder->addFilter($filter);
                                }
                            }
                            break;
                        case 'review_status':
                            if (isset($filters['review_status'])) {
                                $productIds = $this->getFilterByReviewStatus($filters);
                                $filter = $this->filterBuilder->setField('entity_id')
                                    ->setValue($productIds)
                                    ->setConditionType('in')
                                    ->create();
                                $this->searchCriteriaBuilder->addFilter($filter);
                            }
                            break;
                    }
                    break;
                case 'keyword_status':
                    if (isset($filters['auto_text_optimizations']) &&
                        $filters['auto_text_optimizations'] !== AutomaticTextOptimization::SHOW_ALL
                    ) {
                        if ($filters['auto_text_optimizations'] !== AutomaticTextOptimization::NOT_OPTIMIZED) {
                            $productIds = $this->getFilterByKeywordState($filters);
                            $filter = $this->filterBuilder->setField('entity_id')
                                ->setValue($productIds)
                                ->setConditionType('in')
                                ->create();
                            $this->searchCriteriaBuilder->addFilter($filter);
                        } else {
                            $productIds = $this->getFilterByKeywordState($filters, true);
                            if (!empty($productIds)) {
                                $filter = $this->filterBuilder->setField('entity_id')
                                    ->setValue($productIds)
                                    ->setConditionType('nin')
                                    ->create();
                                $this->searchCriteriaBuilder->addFilter($filter);
                            }
                        }
                    }
                    break;
                case 'templates':
                    $this->applyGeneratedStatusFilter(
                        $filters,
                        'templates_fields',
                        'template_selected',
                        AiStatus::TEMPLATES_SHOW_ALL,
                        AiStatus::TEMPLATES_ALL,
                        'templateId'
                    );
                    break;
            }
        } elseif ($filter->getField() == 'category_ids') {
            $categoryIds = $filter->getValue();

            if (is_string($categoryIds)) {
                $categoryIds = array_filter(array_map('trim', explode(',', $categoryIds)));
            }

            if (!is_array($categoryIds)) {
                $categoryIds = [$categoryIds];
            }

            $categoryIds = array_values(array_filter($categoryIds, function ($value) {
                return $value !== null && $value !== '';
            }));

            if (!empty($categoryIds)) {
                $filterGroupBuilder = clone $this->filterGroupBuilder;
                $includeNoCategory = false;

                $categoryIds = array_values(array_filter($categoryIds, function ($value) use (&$includeNoCategory) {
                    if ($value === CategoriesFilterOptions::NO_CATEGORY_VALUE) {
                        $includeNoCategory = true;
                        return false;
                    }
                    return true;
                }));

                if ($includeNoCategory) {
                    $nullFilter = $this->filterBuilder->setField('category_ids')
                        ->setConditionType('null')
                        ->create();
                    $filterGroupBuilder->addFilter($nullFilter);
                }

                foreach ($categoryIds as $categoryId) {
                    $finsetFilter = $this->filterBuilder->setField('category_ids')
                        ->setValue($categoryId)
                        ->setConditionType('finset')
                        ->create();
                    $filterGroupBuilder->addFilter($finsetFilter);
                }
                $filterGroup = $filterGroupBuilder->create();
                $this->searchCriteriaBuilder->addFilterGroup($filterGroup);
            }
        } elseif ($filter->getField() == 'store_id' && (int)$storeId !== Store::DEFAULT_STORE_ID) {
            $website = $this->storeHelper->getWebsiteByStoreId($storeId);
            $filterWebsite = $this->filterBuilder->setField('websites')
                ->setValue($website->getId())
                ->setConditionType('finset')
                ->create();
            $this->searchCriteriaBuilder->addFilter($filterWebsite);
            $this->searchCriteriaBuilder->addFilter($filter);
        } elseif ($filter->getField() == 'record_ids') {
            $recordIds = explode(',', $filters['record_ids'] ?? '');
            $filter = $this->filterBuilder->setField('entity_id')
                ->setValue($recordIds)
                ->setConditionType('in')
                ->create();
            $this->searchCriteriaBuilder->addFilter($filter);
        } elseif ($filter->getField() == 'request_id') {
            $recordIds = $this->getFilterByRequestId($filters);
            $filter = $this->filterBuilder->setField('entity_id')
                ->setValue($recordIds)
                ->setConditionType('in')
                ->create();
            $this->searchCriteriaBuilder->addFilter($filter);
        } else {
            $this->searchCriteriaBuilder->addFilter($filter);
        }
    }

    /**
     * Get data
     */
    public function getData()
    {
        $isXmlHttpRequest = $this->request->isXmlHttpRequest();
        $params = $this->request->getParams();

        $storeId = $this->getStoreIdAndFilter($params);

        $data = parent::getData();

        $data['current_user'] = $this->apiSession->getCurrentUserData();

        /**
         * If not ajax request, return the basic getData
         *
         * This case is for initial page load. No need to process
         * since there will be another getData when the grid load
         */
        if (!$isXmlHttpRequest) {
            $products = [];
            foreach ($data['items'] as &$item) {
                $entityId = $item['entity_id'];
                if (!isset($products[$entityId])) {
                    $item['websites'] = [];
                    $item['name'] = '';
                    continue;
                }
            }
            return $data;
        }
        
        $customOpenGraph = $this->helperData->getCustomOpenGraph();

        $data['custom_open_graph'] = $customOpenGraph;

        $data['grid_settings'] = $this->gridHelper->getGridSettings();

        $data['ids'] = $this->getAllIds();

        $data['users'] = $this->magentoUser->getUsers();

        /** Start premium check */
        $premiumData = $this->collectPremiumData();
        $data = array_merge($data, $premiumData);
        $hasProAccess = $this->premium->getHasProAccess();
        /** End premium check */
        if (empty($data['items'])) {
            if ($this->errorMessage) {
                $data['error_message'] = $this->errorMessage;
            }
            return $data;
        }
        
        $productIds = array_column($data['items'], 'entity_id');

        $generatedTextRaw = $this->generatedText->getGenerated(implode(",", $productIds ?? []), $storeId, 'Product');
        $generated = $this->generatedText->getGeneratedByStoreId(
            implode(",", $productIds ?? []),
            $storeId,
            false,
            $generatedTextRaw
        );
        $generatedDates = $this->generatedText->getRecordsLatestTextDate(
            implode(",", $productIds ?? []),
            $storeId,
            'Product',
            $generatedTextRaw
        );
        $includeEditedDatesPayload = true;
        $editedDates = $this->generatedText->getRecordsEditedDates(
            implode(",", $categoryIds ?? []),
            $storeId,
            'Product',
            $generatedTextRaw,
            $includeEditedDatesPayload
        );
        if ($includeEditedDatesPayload && isset($editedDates['_payload'])) {
            $data['wtai_debug_edited_dates_payload'] = $editedDates['_payload'];
            unset($editedDates['_payload']);
        }
        $optimizationList = $result = $this->keywords->getOptimizationList(
            $storeId,
            [],
            null,
            'Product',
            $productIds
        );

        $collectedProducts = $this->collectProducts($storeId, $productIds);

        $products = $collectedProducts['products'];

        $languageCode = $this->storeHelper->getLanguageCode($storeId);

        $generatedKeys = [
            Fields::PAGE_TITLE => 'ai_page_title',
            Fields::PAGE_DESCRIPTION => 'ai_page_description',
            Fields::PRODUCT_DESCRIPTION => 'ai_product_description',
            Fields::EXCERPT => 'ai_short_product_description',
            Fields::OPEN_GRAPH => 'ai_open_graph',
        ];

        $defaultKeys = [
            'product_name' => 'default_product_name',
            'visibility' => 'default_visibility',
            'status' => 'default_status',
            'mg_page_title' => 'default_mg_page_title',
            'mg_page_description' => 'default_mg_page_description',
            'mg_product_description' => 'default_mg_product_description',
            'mg_short_product_description' => 'default_mg_short_product_description',
            'thumbnail' => 'default_thumbnail'
        ];

        $mappingSettings = $this->helperData->getMappingSettings();

        $keys = [
            'product_name' => 'name',
            'visibility' => 'visibility',
            'status' => 'status',
            'thumbnail' => 'thumbnail',
            'mg_page_title' => $mappingSettings['page_title'],
            'mg_page_description' => $mappingSettings['page_description'],
            'mg_product_description' => $mappingSettings['product_description'],
            'mg_short_product_description' => $mappingSettings['short_product_description']
        ];

        $locale = $this->apiSession->getCurrentUser()->getInterfaceLocale();

        $canShowViewUrl = ((int) $storeId !== (int) Store::DEFAULT_STORE_ID);

        foreach ($data['items'] as &$item) {
            $entityId = $item['entity_id'];

            $product = $products[$entityId];

            if (isset($generated[$entityId])) {
                foreach ($generatedKeys as $generatedKey => $itemKey) {
                    if (isset($generated[$entityId][$generatedKey])) {
                        $item[$itemKey] = $generated[$entityId][$generatedKey];
                    }
                }
            }

            // handle store switcher
            foreach ($defaultKeys as $itemKey => $defaultKey) {
                $isUsingDefault = $this->fieldHelper->isUsingDefaultValues($product, $storeId, $keys[$itemKey]);

                if ($storeId !== Store::DEFAULT_STORE_ID && $isUsingDefault) {
                    $item[$itemKey] = $item[$defaultKey];
                }
            }

            if ($customOpenGraph && !$item['mg_open_graph']) {
                $isUsingDefault = $this->fieldHelper->isUsingDefaultValues(
                    $product,
                    $storeId,
                    $mappingSettings['open_graph']
                );
                
                if ($storeId !== Store::DEFAULT_STORE_ID && $isUsingDefault) {
                    $item['mg_open_graph'] = $item['default_mg_open_graph'];
                }
            }

            // This operation runs a significant number of queries, approximately 250 or more.
            $item = $this->decodeHtmlEntities($product, $item, $mappingSettings);

            $magentoStatus = (int) ($item['status'] ?? $product->getStatus());
            $isProductDisabled = ($magentoStatus === ProductStatus::STATUS_DISABLED);

            if ($canShowViewUrl && !$isProductDisabled) {
                $frontendStoreId = $this->storeHelper->getFrontendStoreId($storeId);
                $frontendWebsiteId = (int) $this->storeManager->getStore($frontendStoreId)->getWebsiteId();

                $useStoreSwitchUrl = ((int) $storeId === (int) Store::DEFAULT_STORE_ID);
                if (!$useStoreSwitchUrl) {
                    try {
                        $defaultStoreView = $this->storeManager->getDefaultStoreView();
                        if ($defaultStoreView) {
                            $selectedStore = $this->storeManager->getStore((int) $frontendStoreId);

                            $defaultGroupId = (int) $defaultStoreView->getGroupId();
                            $selectedGroupId = (int) $selectedStore->getGroupId();

                            if ($defaultGroupId
                                && $selectedGroupId
                                && $defaultGroupId !== $selectedGroupId
                            ) {
                                $defaultBaseUrl =
                                    (string) $this->storeHelper->getFrontendStoreBaseUrl(
                                        (int) $defaultStoreView->getId()
                                    );
                                $selectedBaseUrl =
                                    (string) $this->storeHelper->getFrontendStoreBaseUrl((int) $frontendStoreId);

                                //phpcs:disable Magento2.Functions.DiscouragedFunction.Discouraged
                                $defaultHost = (string) (parse_url($defaultBaseUrl, PHP_URL_HOST) ?? '');
                                $selectedHost = (string) (parse_url($selectedBaseUrl, PHP_URL_HOST) ?? '');
                                //phpcs:enable Magento2.Functions.DiscouragedFunction.Discouraged

                                if ($defaultHost !== '' && $selectedHost !== '' && $defaultHost === $selectedHost) {
                                    $useStoreSwitchUrl = true;
                                }
                            }
                        }
                    } catch (\Exception $e) {
                        $useStoreSwitchUrl = false;
                    }
                }

                $_product = $product;
                $viewUrl = $_product->setStoreId($frontendStoreId)
                    ->setWebsiteId($frontendWebsiteId)
                    ->getUrlInStore();

                $viewUrl = $useStoreSwitchUrl
                    ? $this->normalizeFrontendUrl((int) $frontendStoreId, (string) $viewUrl)
                    : $viewUrl;
                $item['view_link'] = $useStoreSwitchUrl
                    ? $this->buildFrontendStoreSwitchUrl((int) $frontendStoreId, (string) $viewUrl)
                    : $viewUrl;
            } else {
                $item['view_link'] = '';
            }

            $item['language'] = $languageCode;

            $item['websites'] = explode(',', $item['websites'] ?? '');
            
            $item['edit_url'] = $this->urlBuilder->getUrl(
                'wtai/edit/index',
                ['id' => $item['entity_id'], 'store_id' => $storeId]
            );

            if (isset($generatedDates[$entityId])) {
                $staticGDate = new \DateTime($generatedDates[$entityId]);
                $formattedGDate = $this->timezone->formatDate(
                    $staticGDate,
                    \IntlDateFormatter::SHORT,
                    $locale
                );
                $item['generated_at'] = $formattedGDate;
            }

            if (isset($item['transferred_at'])) {
                $staticTDate = new \DateTime($item['transferred_at']);
                $formattedTDate = $this->timezone->formatDate(
                    $staticTDate,
                    \IntlDateFormatter::SHORT,
                    $locale
                );
                $item['transferred_at'] = $formattedTDate;
            }
            
            if (isset($editedDates[$entityId]['edited_at']) && $editedDates[$entityId]['edited_at']) {
                $staticEDate = new \DateTime($editedDates[$entityId]['edited_at']);
                $formattedEDate = $this->timezone->formatDate(
                    $staticEDate,
                    \IntlDateFormatter::SHORT,
                    $locale
                );
                $item['edited_at'] = $formattedEDate;
            } else {
                $item['edited_at'] = '';
            }

            $item['target_keywords'] = $this->getTargetKeywords($entityId, $storeId, $optimizationList['result'] ?? []);
            $item['traffic_potential'] = $this->getTrafficPotential(
                $entityId,
                $storeId,
                $hasProAccess,
                $optimizationList['result'] ?? []
            );
            $item['optimization_status'] = $this->getOptimizationDataStatus(
                $entityId,
                $storeId,
                $optimizationList['result'] ?? []
            );

            $item['name'] = $item['product_name']; // Used as alt for grid thumbnail
        }

        if ($this->errorMessage) {
            $data['error_message'] = $this->errorMessage;
        }

        return $data;
    }
    // phpcs:enable Generic.Metrics.NestingLevel.TooHigh

    /**
     * Build frontend store switch URL
     *
     * @param int $storeId
     * @param string $targetUrl
     * @return string
     */
    private function buildFrontendStoreSwitchUrl($storeId, $targetUrl)
    {
        $storeCode = $this->storeHelper->getFrontendStoreCode($storeId);
        $baseUrl = rtrim($this->storeHelper->getFrontendStoreBaseUrl($storeId), '/');

        return $baseUrl
            . '/stores/store/switch'
            . '?___store=' . urlencode($storeCode)
            . '&uenc=' . urlencode(base64_encode($targetUrl));
    }
    /**
     * Normalize frontend URL
     *
     * @param int $storeId
     * @param string $url
     * @return string
     */
    private function normalizeFrontendUrl($storeId, $url)
    {
        $baseUrl = rtrim($this->storeHelper->getFrontendStoreBaseUrl($storeId), '/');
        if ($baseUrl === '') {
            return $url;
        }
        // phpcs:disable Magento2.Functions.DiscouragedFunction.Discouraged
        $baseParts = parse_url($baseUrl);
        if (is_array($baseParts) && !empty($baseParts['scheme']) && !empty($baseParts['host'])) {
            $cleanBaseUrl = $baseParts['scheme'] . '://' . $baseParts['host'];
            if (!empty($baseParts['port'])) {
                $cleanBaseUrl .= ':' . $baseParts['port'];
            }
            $baseUrl = $cleanBaseUrl;
        }

        $parts = parse_url($url);
        if (!is_array($parts) || empty($parts['path'])) {
            return $url;
        }
        // phpcs:enable Magento2.Functions.DiscouragedFunction.Discouraged

        $path = (string) $parts['path'];
        $backendFrontName = (string) $this->urlBuilder->getAreaFrontName();
        if ($backendFrontName !== '') {
            $frontName = trim($backendFrontName, '/');
            $needles = [
                '/' . $frontName . '/',
                '/index.php/' . $frontName . '/',
                '/' . $frontName,
                '/index.php/' . $frontName,
            ];

            foreach ($needles as $needle) {
                if (strpos($path, $needle) === 0) {
                    $path = '/' . ltrim(substr($path, strlen($needle)), '/');
                    break;
                }
            }
        }

        $normalized = $baseUrl . '/' . ltrim($path, '/');
        if (!empty($parts['query'])) {
            $normalized .= '?' . $parts['query'];
        }
        if (!empty($parts['fragment'])) {
            $normalized .= '#' . $parts['fragment'];
        }

        return $normalized;
    }

    // phpcs:enable Generic.Metrics.NestingLevel.TooHigh,Generic.Files.LineLength,Magento2.Functions.DiscouragedFunction.Discouraged

    /**
     * Get traffic potential
     *
     * @param int $entityId
     * @param int $storeId
     * @param bool $hasProAccess
     * @param array $optimizationList
     *
     * @return string
     */
    private function getTrafficPotential($entityId, $storeId, $hasProAccess, $optimizationList)
    {
        if (!$hasProAccess) {
            return __('Data available in Pro');
        }

        $optimizationData = array_filter(
            $optimizationList,
            function ($item) use ($entityId) {
                return isset($item['recordId']) && $item['recordId'] === $entityId;
            }
        );
        $optimizationData = reset($optimizationData) ?: null;

        if (isset($optimizationData['trafficPotentialDisplay'])) {
            return $optimizationData['trafficPotentialDisplay'] ?? __('No data');
        }

        return __('Pending analysis');
    }

    /**
     * Get target keywords
     *
     * @param int $entityId
     * @param int $storeId
     * @param array $optimizationList
     *
     * @return string
     */
    private function getTargetKeywords($entityId, $storeId, $optimizationList)
    {
        $optimizationData = array_filter(
            $optimizationList,
            function ($item) use ($entityId) {
                return isset($item['recordId']) && $item['recordId'] === $entityId;
            }
        );
        $optimizationData = reset($optimizationData) ?: null;

        if (isset($optimizationData['optimizingKeywords'])) {
            if (empty($optimizationData['optimizingKeywords'])) {
                return __('No data');
            }
            return implode(', ', $optimizationData['optimizingKeywords']);
        }

        return __('Pending analysis');
    }

    /**
     * Get optimization data status
     *
     * @param int $entityId
     * @param int $storeId
     * @param array $optimizationList
     *
     * @return string
     */
    private function getOptimizationDataStatus($entityId, $storeId, $optimizationList)
    {
        $optimizationData = array_filter(
            $optimizationList,
            function ($item) use ($entityId) {
                return isset($item['recordId']) && $item['recordId'] === $entityId;
            }
        );
        $optimizationData = reset($optimizationData) ?: null;

        if (isset($optimizationData['status'])) {
            return $optimizationData['status'];
        }

        return null;
    }

    /**
     * Collect products
     *
     * @param int $storeId
     * @param array $productIds
     *
     * @return array
     */
    private function collectProducts($storeId, $productIds)
    {
        $collection = $this->productCollectionFactory->create();
        $collection->setStoreId($storeId);
        $collection->addFieldToFilter('entity_id', ['in' => $productIds]);

        $products = [];
        foreach ($collection as $product) {
            $products[$product->getId()] = $product;
        }

        return [
            'products' => $products
        ];
    }

    /**
     * Get store id and filter
     *
     * @param array $params
     * @return int
     */
    private function getStoreIdAndFilter($params)
    {
        $filters = $params['filters'] ?? [];

        if (!isset($filters['store_id'])) {
            $storeId = Store::DEFAULT_STORE_ID;
            $this->addFilter(
                $this->filterBuilder->setField('store_id')
                    ->setValue($storeId)
                    ->setConditionType('eq')
                    ->create()
            );
        } else {
            $storeId = $filters['store_id'];
        }

        // Set default writetextai_status filter when placeholder is true
        if (isset($filters['writetextai_status']) &&
            isset($filters['writetextai_status']['initial']) &&
            $filters['writetextai_status']['initial'] === true) {
            $defaultWritetextaiStatus = [
                'ai_fields' => [
                    'page title',
                    'page description',
                    'product description',
                    'excerpt',
                    'open graph text',
                    'image_alt_text'
                ],
                'status' => 'all',
                'no_activity_days' => 7,
                'auto_text_optimizations' => AutomaticTextOptimization::SHOW_ALL,
                'templates_fields' => AiStatus::TEMPLATES_SHOW_ALL,
                'filter_by' => 'text_status',
                'alt_text_field' => 'image_alt_text'
            ];
            
            // Add the default writetextai_status filter
            $this->addFilter(
                $this->filterBuilder->setField('writetextai_status')
                    ->setValue(json_encode($defaultWritetextaiStatus))
                    ->setConditionType('eq')
                    ->create()
            );
        }

        return $storeId;
    }

    /**
     * Decode html entities
     *
     * @param \Magento\Catalog\Model\Product $product
     * @param array $item
     * @param array $mappingSettings
     * @return array
     */
    private function decodeHtmlEntities($product, $item, $mappingSettings)
    {
        $keys = [
            'product_name' => 'name',
            'mg_page_title' => 'page_title',
            'mg_page_description' => 'page_description',
            'mg_product_description' => 'product_description',
            'mg_short_product_description' => 'short_product_description',
            'mg_open_graph' => 'open_graph',
            'ai_page_title' => 'page_title',
            'ai_page_description' => 'page_description',
            'ai_product_description' => 'product_description',
            'ai_short_product_description' => 'short_product_description',
            'ai_open_graph' => 'open_graph',
        ];

        foreach ($keys as $key => $mapping) {
            if (isset($item[$key])) {
                $item[$key] = $this->outputHelper->productAttribute(
                    $product,
                    $item[$key],
                    $mappingSettings[$mapping] ?? $mapping
                );
            }
        }

        return $item;
    }

    /**
     * Apply generated status filter (templates or AI models)
     *
     * @param array $filters
     * @param string $fieldsKey
     * @param string $selectedKey
     * @param string $showAllValue
     * @param string $allValue
     * @param string $paramKey
     *
     * @return void
     */
    private function applyGeneratedStatusFilter(
        $filters,
        $fieldsKey,
        $selectedKey,
        $showAllValue,
        $allValue,
        $paramKey
    ) {
        if (isset($filters[$fieldsKey]) &&
            $filters[$fieldsKey] !== $showAllValue &&
            isset($filters[$selectedKey]) &&
            $filters[$selectedKey] !== $allValue &&
            $filters[$selectedKey] !== null &&
            $filters[$selectedKey] !== ''
        ) {
            $productIds = $this->getFilterByGeneratedStatus(
                $filters,
                $fieldsKey,
                $selectedKey,
                $paramKey
            );
            $filter = $this->filterBuilder->setField('entity_id')
                ->setValue($productIds)
                ->setConditionType('in')
                ->create();
            $this->searchCriteriaBuilder->addFilter($filter);
        }
    }

    /**
     * Get filter by generated status (generic method for templates and AI models)
     *
     * @param array $filters
     * @param string $fieldsKey
     * @param string $selectedKey
     * @param string $paramKey
     *
     * @return array
     */
    private function getFilterByGeneratedStatus($filters, $fieldsKey, $selectedKey, $paramKey)
    {
        $storeId = !isset($filters['store_id']) ? Store::DEFAULT_STORE_ID : $filters['store_id'];
        $language = $this->storeHelper->getFormattedLanguage($storeId);

        $params = [
            "type" => "Product",
            "storeId" => $storeId,
            "language" => $language,
            "fields" => [$filters[$fieldsKey]],
            $paramKey => $filters[$selectedKey]
        ];
        
        $result = $this->apiManager->getGeneratedStatus($params);
        $recordIds = array_column($result['records'], 'recordId');
        if (isset($result['continuationToken'])) {
            do {
                $params['continuationToken'] = $result['continuationToken'];
                $result = $this->apiManager->getGeneratedStatus($params);
                $recordIds = array_merge($recordIds, array_column($result['records'], 'recordId'));
            } while (isset($result['continuationToken']));
        }

        return $recordIds;
    }

    /**
     * Get filter by keyword state
     *
     * @param array $filters
     * @param bool $isNotOptimized
     *
     * @return array
     */
    public function getFilterByKeywordState($filters, $isNotOptimized = false)
    {
        $storeId = !isset($filters['store_id']) ? Store::DEFAULT_STORE_ID : $filters['store_id'];

        $statuses = [];
        if (!$isNotOptimized) {
            $reviewStatus = $filters['keyword_analysis'];
            if (is_array($reviewStatus)) {
                foreach ($reviewStatus as $status) {
                    switch ($status) {
                        case AutomaticTextOptimization::GENERATED:
                            $statuses[] = 'Generated';
                            break;
                        case AutomaticTextOptimization::FOR_GENERATION:
                            $statuses[] = 'ForGeneration';
                            break;
                        case AutomaticTextOptimization::PUBLISHED:
                            $statuses[] = 'Published';
                            break;
                    }
                }
            }
        } else {
            $statuses[] = 'Generated';
            $statuses[] = 'ForGeneration';
            $statuses[] = 'Published';
        }

        $result = $this->keywords->getKeywordState($storeId, $statuses);
        $recordIds = array_column($result['keywords'], 'recordId');
        if (isset($result['continuationToken'])) {
            do {
                $result = $this->keywords->getKeywordState($storeId, $statuses, $result['continuationToken']);
                $recordIds = array_merge($recordIds, array_column($result['keywords'], 'recordId'));
            } while (isset($result['continuationToken']));
        }

        return $recordIds;
    }

    /**
     * Get filter by WriteText.ai status
     *
     * @param array $filters
     * @return array
     */
    public function getFilterByReviewStatus($filters)
    {
        $storeId = !isset($filters['store_id']) ? Store::DEFAULT_STORE_ID : $filters['store_id'];
        
        if ((int)$storeId !== Store::DEFAULT_STORE_ID) {
            return $this->getReviewStatusFilterByStoreId($filters, $storeId);
        } else {
            $stores = $this->storeManager->getStores();
            $recordIds = [];
            // phpcs:disable Magento2.Performance.ForeachArrayMerge.ForeachArrayMerge
            foreach ($stores as $store) {
                $recordIds = array_merge($recordIds, $this->getReviewStatusFilterByStoreId($filters, $store->getId()));
            }
            // phpcs:enable Magento2.Performance.ForeachArrayMerge.ForeachArrayMerge
        }

        return $recordIds;
    }

    /**
     * Get review status filter by store id
     *
     * @param array $filters
     * @param int $storeId
     *
     * @return array
     */
    private function getReviewStatusFilterByStoreId($filters, $storeId)
    {
        $language = $this->storeHelper->getRegionIndependentLanguage($storeId);

        $statuses = [];
        $reviewStatus = $filters['review_status'];
        if (in_array('for_rewrite', $reviewStatus)) {
            $statuses[] = 'EditForRewrite';
            $statuses[] = 'EditForRewriteAndCorrection';
        }
        if (in_array('fact_check', $reviewStatus)) {
            $statuses[] = 'EditForCorrection';
            $statuses[] = 'EditForRewriteAndCorrection';
        }
        if (in_array('for_rewrite', $reviewStatus) && in_array('fact_check', $reviewStatus)) {
            $statuses[] = 'EditForRewriteAndCorrection';
        }

        $params = [
            "type" => "Product",
            "storeId" => $storeId,
            "language" => $language,
            "fields" => $filters['ai_fields'],
            "status" => $statuses
        ];

        $result = $this->apiManager->filterReviewStatus($params);
        $recordIds = array_column($result['records'], 'recordId');
        if (isset($result['continuationToken'])) {
            do {
                $params['continuationToken'] = $result['continuationToken'];
                $result = $this->apiManager->filterReviewStatus($params);
                $recordIds = array_merge($recordIds, array_column($result['records'], 'recordId'));
            } while (isset($result['continuationToken']));
        }

        return $recordIds;
    }

    /**
     * Get filter by WriteText.ai status
     *
     * @param array $filters
     * @return array
     */
    public function getFilterByAiStatus($filters)
    {
        $storeId = !isset($filters['store_id']) ? Store::DEFAULT_STORE_ID : $filters['store_id'];
        $language = $this->storeHelper->getFormattedLanguage($storeId);

        $params = [
            "type" => "Product",
            "storeId" => $storeId,
            "language" => $language,
            "fields" => $filters['ai_fields'],
            "status" => $filters['ai_status']
        ];

        if (isset($filters['start_date'])) {
            $params['startDate'] = $filters['start_date'];
        }

        if (isset($filters['end_date'])) {
            $params['endDate'] = $filters['end_date'];
        }
        
        $result = $this->apiManager->getGeneratedStatus($params);
        $recordIds = array_column($result['records'], 'recordId');
        if (isset($result['continuationToken'])) {
            do {
                $params['continuationToken'] = $result['continuationToken'];
                $result = $this->apiManager->getGeneratedStatus($params);
                $recordIds = array_merge($recordIds, array_column($result['records'], 'recordId'));
            } while (isset($result['continuationToken']));
        }

        return $recordIds;
    }

    /**
     * Get filter by image alt text
     *
     * @param array $filters
     * @return array
     */
    public function getFilterByImageAltText($filters)
    {
        $storeId = !isset($filters['store_id']) ? Store::DEFAULT_STORE_ID : $filters['store_id'];
        $language = $this->storeHelper->getFormattedLanguage($storeId);

        $params = [
            "type" => "Product",
            "storeId" => $storeId,
            "language" => $language,
            "status" => $filters['ai_status']
        ];

        if (isset($filters['start_date'])) {
            $params['startDate'] = $filters['start_date'];
        }

        if (isset($filters['end_date'])) {
            $params['endDate'] = $filters['end_date'];
        }
        $result = $this->apiManager->getImageAltTextStatus($params);
        $recordIds = array_column($result['images'], 'imageId');
        if (isset($result['continuationToken'])) {
            do {
                $params['continuationToken'] = $result['continuationToken'];
                $result = $this->apiManager->getImageAltTextStatus($params);
                $recordIds = array_merge($recordIds, array_column($result['images'], 'imageId'));
            } while (isset($result['continuationToken']));
        }

        $productIdsThroughImageAltText = $this->fetchProductIdsByImageIds($recordIds, $storeId);
        return $productIdsThroughImageAltText;
    }

    /**
     * Get all ids in grid preserving sort and filter
     *
     * @return array
     */
    protected function getAllIds()
    {
        $ids = [];
        $collection = clone $this->getSearchResult();
        $collection->load();
        $select = $collection->getSelect()
            ->reset(\Magento\Framework\DB\Select::COLUMNS)
            ->columns(['entity_id'])
            ->reset(\Magento\Framework\DB\Select::LIMIT_COUNT)
            ->reset(\Magento\Framework\DB\Select::LIMIT_OFFSET);

        $connection = $this->getSearchResult()->getResource()->getConnection();
        $ids = $connection->fetchCol($select);

        return $ids;
    }

    /**
     * Fetch product ids by image ids
     *
     * @param array $imageIds
     * @param int $storeId
     * @return array
     */
    private function fetchProductIdsByImageIds($imageIds, $storeId)
    {

        /** @var \Magento\Framework\DB\Adapter\AdapterInterface $connection */
        $connection = $this->productGalleryResource->getConnection();

        /**
         * Always get data from default store since we need the ids and
         * possible that the image is not available on the store
         */
        $selectValue = $connection->select()
            ->from($this->productGalleryResource->getTable(ProductGallery::GALLERY_VALUE_TABLE))
            ->where('value_id IN (?)', $imageIds)
            ->where('store_id = ?', Store::DEFAULT_STORE_ID);
    
        $imageValueData = $connection->fetchAll($selectValue);

        $entityIds = [];
        if ($imageValueData) {
            $entityIds = array_column($imageValueData, 'entity_id');
            $entityIds = array_unique($entityIds);
        }

        return $entityIds;
    }

    /**
     * Get filter by request id
     *
     * @param array $filters
     * @return array
     */
    private function getFilterByRequestId($filters)
    {
        $result = [];
        $ids = [];
        try {
            $requestId = $filters['request_id'];
            $textGenerateStatus = isset($filters['text_generation_status']) ? $filters['text_generation_status'] : '';
            $result = $this->apiManager->getBulkRequestById($requestId);
            if (!empty($result)) {
                $resultStatus = $result['status'];
                if ($textGenerateStatus === 'success') {
                    $completedIds = $result['completedIds'] ?? [];
                    $failedIds = $result['failedIds'] ?? [];
                    $skippedIds = $result['skippedIds'] ?? [];
                    $completedIds = array_values(array_diff($completedIds, $failedIds));
                    $completedIds = array_values(array_diff($completedIds, $skippedIds));
                    $ids = $completedIds;
                } elseif ($textGenerateStatus === 'skipped') {
                    $ids = $result['skippedIds'] ?? [];
                } elseif ($textGenerateStatus === 'failed') {
                    if ($resultStatus === 'Pending' ||
                        $resultStatus === 'Running' ||
                        $resultStatus === 'Cancelling' ||
                        $resultStatus === 'Cancelled') {
                        $ids = $result['failedIds'] ?? [];
                    } elseif ($resultStatus === 'Completed' ||
                        $resultStatus === 'Failed' ||
                        $resultStatus === 'TimedOut') {
                        $ids = array_merge(
                            $result['failedIds'] ?? [],
                            $result['queuedIds'] ?? [],
                            $result['runningIds'] ?? []
                        );
                    }
                } elseif ($textGenerateStatus === 'failed-transfer') {
                    $ids = $result['failedTransferIds'] ?? [];
                } else {
                    $ids = array_merge(
                        $result['completedIds'] ?? [],
                        $result['failedIds'] ?? [],
                        $result['queuedIds'] ?? [],
                        $result['runningIds'] ?? []
                    );
                }
            }
        } catch (\WriteTextAI\WriteTextAI\Exception\ApiException $e) {
            if ($e->getCode() === 404) {
                $message =  __('The list of products you’re trying to view cannot be retrieved.'
                . ' This may happen if the link has expired or if there’s a typo in the URL.'
                . ' Please recheck the link. If the issue continues, the list no longer exists.');
                $this->errorMessage = $message;
                $ids = [];
            }
        } catch (\Exception $e) {
            $message = __(
                'A system error has occurred. Please try again. If the issue persists,'
                . ' please contact our support team at support@writetext.ai.'
            );
            $this->errorMessage = $message;
            $ids = [];
        }
        return $ids;
    }
}
