<?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 Magento\User\Model\UserFactory;
use Magento\User\Model\ResourceModel\User\CollectionFactory;
use Magento\Framework\Stdlib\DateTime\TimezoneInterface;
use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory as ProductCollectionFactory;
use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory as CategoryCollectionFactory;
use Magento\Catalog\Helper\Output as OutputHelper;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Catalog\Api\CategoryRepositoryInterface;
use Magento\Framework\Exception\NoSuchEntityException;
use WriteTextAI\WriteTextAI\Helper\Data as HelperData;
use WriteTextAI\WriteTextAI\Helper\Categories\Fields as CategoriesFieldsHelper;
use WriteTextAI\WriteTextAI\Model\Api\Session as ApiSession;
use WriteTextAI\WriteTextAI\Helper\Store as StoreHelper;
use WriteTextAI\WriteTextAI\Model\OptionSource\Filter\Fields;

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

    /**
     * @var UserFactory
     */
    protected $userFactory;

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

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

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

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

    /**
     * @var ProductRepositoryInterface
     */
    protected $productRepository;

    /**
     * @var CategoryRepositoryInterface
     */
    protected $categoryRepository;

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

    /**
     * @var CategoriesFieldsHelper
     */
    protected $categoriesFieldsHelper;

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

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

    /**
     * @var CategoryCollectionFactory
     */
    protected $categoryCollectionFactory;

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

    /**
     * @var array
     */
    protected $products = [];

    /**
     * @var array
     */
    protected $categories = [];

    /**
     * @var array
     */
    protected $entities = [];

    /**
     * Constructor
     *
     * @param Context $context
     * @param ApiManager $apiManager
     * @param UserFactory $userFactory
     * @param CollectionFactory $userCollectionFactory
     * @param TimezoneInterface $timezone
     * @param ProductCollectionFactory $productCollectionFactory
     * @param OutputHelper $outputHelper
     * @param ProductRepositoryInterface $productRepository
     * @param CategoryRepositoryInterface $categoryRepository
     * @param HelperData $helperData
     * @param CategoriesFieldsHelper $categoriesFieldsHelper
     * @param ApiSession $apiSession
     * @param StoreHelper $storeHelper
     * @param CategoryCollectionFactory $categoryCollectionFactory
     */
    public function __construct(
        Context $context,
        ApiManager $apiManager,
        UserFactory $userFactory,
        CollectionFactory $userCollectionFactory,
        TimezoneInterface $timezone,
        ProductCollectionFactory $productCollectionFactory,
        OutputHelper $outputHelper,
        ProductRepositoryInterface $productRepository,
        CategoryRepositoryInterface $categoryRepository,
        HelperData $helperData,
        CategoriesFieldsHelper $categoriesFieldsHelper,
        ApiSession $apiSession,
        StoreHelper $storeHelper,
        CategoryCollectionFactory $categoryCollectionFactory
    ) {
        $this->apiManager = $apiManager;
        $this->userFactory = $userFactory;
        $this->userCollectionFactory = $userCollectionFactory;
        $this->timezone = $timezone;
        $this->productCollectionFactory = $productCollectionFactory;
        $this->outputHelper = $outputHelper;
        $this->productRepository = $productRepository;
        $this->categoryRepository = $categoryRepository;
        $this->helperData = $helperData;
        $this->categoriesFieldsHelper = $categoriesFieldsHelper;
        $this->apiSession = $apiSession;
        $this->storeHelper = $storeHelper;
        $this->categoryCollectionFactory = $categoryCollectionFactory;
        parent::__construct($context);
    }

    /**
     * Execute action
     *
     * @return \Magento\Framework\Controller\Result\Json
     */
    public function execute()
    {
        $response = $this->resultFactory->create(ResultFactory::TYPE_JSON);
        $result = [
            'success' => true,
            'entries' => [],
            'next_page_token' => '',
        ];
        
        try {
            $user = $this->apiSession->getCurrentUser();
            $regionIndependentLanguage = $this->apiSession->getRegionIndependentLanguage();
            
            $params = $this->getParams();
            $history = $this->apiManager->getHistory($params, $regionIndependentLanguage);

            if (isset($history['histories']) &&
                is_array($history['histories']) &&
                !empty($history['histories'])
            ) {
                if ($params['type'] === 'Category') {
                    $this->collectCategories($history['histories']);
                } else {
                    $this->collectProducts($history['histories']);
                }
                $entries = $this->groupHistory($history['histories']);
                $result = [
                    'success' => true,
                    'entries' => $entries,
                    'next_page_token' => $history['continuationToken'] ?? '',
                ];
            }
        } catch (\Exception $e) {
            $message = $e->getMessage();
            if (!$message) {
                $message = __(
                    'A system error has occurred. Please try again. If the issue persists,'
                    . ' please contact our support team at support@writetext.ai.'
                );
            }
            
            $result = [
                'success' => false,
                'message' => $message
            ];
        }

        $response->setData($result);

        return $response;
    }

    /**
     * Group history by timestamp, user, and action
     *
     * @param array $history
     * @return array
     */
    protected function groupHistory($history)
    {
        /**
         * group by timestamp, user, and action
         */
        $entries = [];
        $storeId = $this->getRequest()->getParam('store_id', 0);
        $historyType = $this->getRequest()->getParam('history_type', 'Product');
        
        if ($historyType == 'Product') {
            $mappingSettings = $this->helperData->getMappingSettings();
            $mapping = [
                'page title' => $mappingSettings['page_title'],
                'page description' => $mappingSettings['page_description'],
                'product description' => $mappingSettings['product_description'],
                'excerpt' => $mappingSettings['short_product_description']
            ];
            if (isset($mappingSettings['open_graph'])) {
                $mapping['open graph text'] = $mappingSettings['open_graph'];
            }
        } else {
            $mappingSettings = $this->categoriesFieldsHelper->getMappingSettings();
            $mapping = [
                Fields::CATEGORY_PAGE_TITLE => $mappingSettings['page_title'],
                Fields::CATEGORY_PAGE_DESCRIPTION => $mappingSettings['page_description'],
                Fields::CATEGORY_DESCRIPTION => $mappingSettings['category_description']
            ];
        }

        foreach ($history as $entry) {
            if (!isset($mapping[$entry['textType']])) {
                continue;
            }

            $value = $this->getRecordDataValue($entry, $historyType, $storeId, $mapping);
            if ($value === 'entity not found') {
                continue;
            }
            $date = $this->timezone->date($entry['timestamp']);
            $timestamp = $date->format('Y-m-d H:i');

            if (!isset($entries[
                $timestamp .
                $entry['recordId'] .
                $entry['editor'] .
                $entry['action'] .
                ($entry['isAutomatic'] ? 'auto' : 'manual')
            ])) {
                $userName = $this->getUserNameByEmail($entry['editor']);

                $entry['actionDisplay'] = $this->replaceLabel($entry['actionDisplay'], $entry['action']);
                
                $entries[
                    $timestamp .
                    $entry['recordId'] .
                    $entry['editor'] .
                    $entry['action'] .
                    ($entry['isAutomatic'] ? 'auto' : 'manual')
                ] = [
                    'timestamp' => $date->format('M d, Y') .' | '. $date->format('H:i'),
                    'actionDisplay' => $userName ? str_replace(
                        $entry['editor'],
                        $userName,
                        $entry['actionDisplay']
                    ) : $entry['actionDisplay'],
                    'recordName' => $this->entities[$entry['recordId']]['name'] ?? '',
                    'recordId' => $entry['recordId'],
                    'storeId' => $storeId,
                    'textEntries' => []
                ];
            }

            if (!isset($entries[
                $timestamp .
                $entry['recordId'] .
                $entry['editor'] .
                $entry['action'] .
                ($entry['isAutomatic'] ? 'auto' : 'manual')
            ]['textEntries'][$entry['textType']])) {
                $entries[
                    $timestamp .
                    $entry['recordId'] .
                    $entry['editor'] .
                    $entry['action'] .
                    ($entry['isAutomatic'] ? 'auto' : 'manual')
                ]['textEntries'][$entry['textType']] = [];
            }
            
            $entries[
                $timestamp .
                $entry['recordId'] .
                $entry['editor'] .
                $entry['action'] .
                ($entry['isAutomatic'] ? 'auto' : 'manual')
            ]['textEntries'][$entry['textType']][] = $value;
        }

        return $entries;
    }

    /**
     * Get params for API request
     *
     * @return mixed[]
     */
    private function getParams()
    {
        $params = $this->getRequest()->getParams();
        $data = [
            'type' => $params['history_type'],
            'storeId' => $params['store_id'],
            'recordId' => $params['record_id'],
        ];

        $language = $this->storeHelper->getFormattedLanguage($params['store_id']);
        $data['language'] = $language;

        if (isset($params['start_date']) && $params['start_date']) {
            $startDate = $this->timezone->convertConfigTimeToUtc($params['start_date'] . ' 00:00:00');
            $data['startDate'] = $startDate;
        }

        if (isset($params['end_date']) && $params['end_date']) {
            $endDate = $this->timezone->convertConfigTimeToUtc($params['end_date'] . ' 23:59:59');
            $data['endDate'] = $endDate;
        }

        if (isset($params['user']) && $params['user']) {
            $model = $this->userFactory->create();
            $user = $model->load($params['user']);

            if ($user->getId()) {
                $data['userName'] = $user->getEmail();
            }
        }

        if (isset($params['token']) && $params['token']) {
            $data['continuationToken'] = $params['token'];
        }
        
        return $data;
    }

    /**
     * Get user name by email
     *
     * @param string $email
     * @return string
     */
    protected function getUserNameByEmail($email)
    {
        try {
            $users = $this->getUsers();

            foreach ($users as $user) {
                if ($user->getEmail() == $email) {
                    return $user->getFirstname() . ' ' . $user->getLastname();
                }
            }
        } catch (\Exception $e) {
            return '';
        }
    }

    /**
     * Get users
     *
     * @return \Magento\User\Model\ResourceModel\User\Collection
     */
    protected function getUsers()
    {
        if (!$this->users) {
            $collection = $this->userCollectionFactory->create();
            $this->users = $collection;
        }

        return $this->users;
    }

    /**
     * Collect products
     *
     * @param array $history
     * @return void
     */
    protected function collectProducts($history)
    {
        $storeId = $this->getRequest()->getParam('store_id', 0);
        
        try {
            $productIds = array_unique(array_column($history, 'recordId'));
        } catch (\Exception $e) {
            return;
        }

        $collection = $this->productCollectionFactory->create();
        $collection->setStoreId($storeId);
        $collection->addAttributeToSelect('name');
        $collection->addFieldToFilter('entity_id', ['in' => $productIds]);

        foreach ($collection as $product) {
            if (!isset($this->entities[$product->getId()])) {
                $productName = $this->outputHelper->productAttribute(
                    $product,
                    $product->getName(),
                    'name'
                );
                $this->entities[$product->getId()] = [
                    'name' => $productName,
                    'entity' => $product
                ];
            }
        }
    }

    /**
     * Collect categories
     *
     * @param array $history
     * @return void
     */
    protected function collectCategories($history)
    {

        $storeId = $this->getRequest()->getParam('store_id', 0);
        
        try {
            $categoryIds = array_unique(array_column($history, 'recordId'));
        } catch (\Exception $e) {
            return;
        }

        $collection = $this->categoryCollectionFactory->create();
        $collection->setStoreId($storeId);
        $collection->addAttributeToSelect('name');
        $collection->addFieldToFilter('entity_id', ['in' => $categoryIds]);

        foreach ($collection as $category) {
            if (!isset($this->entities[$category->getId()])) {
                $categoryName = $this->outputHelper->categoryAttribute(
                    $category,
                    $category->getName(),
                    'name'
                );
                $this->entities[$category->getId()] = [
                    'name' => $categoryName,
                    'entity' => $category
                ];
            }
        }
    }

    /**
     * Get record data value
     *
     * @param array $entry
     * @param string $type
     * @param int $storeId
     * @param array $mapping
     * @return string
     */
    protected function getRecordDataValue($entry, $type, $storeId, $mapping)
    {
        $value = '';
        $entity = $this->entities[$entry['recordId']]['entity'] ?? null;
        $methodAttribute = '';
        if (!$entity) {
            try {
                if ($type == 'Product') {
                    $entity = $this->productRepository->getById(
                        $entry['recordId'],
                        false,
                        $storeId
                    );
                } else {
                    $entity = $this->categoryRepository->get(
                        $entry['recordId'],
                        $storeId
                    );
                }
            } catch (NoSuchEntityException $e) {
                return 'entity not found';
            }
        }
        
        $methodAttribute = $type === 'Product' ? 'productAttribute' : 'categoryAttribute';
        $value = $this->outputHelper->$methodAttribute(
            $entity,
            $entry['value'],
            $mapping[$entry['textType']]
        );
        return $value;
    }

    /**
     * Replace label with value
     *
     * @param string $initialValue
     * @param string $action
     * @return string
     */
    public function replaceLabel($initialValue, $action)
    {
        $returnValue = $initialValue;
        switch ($action) {
            case 'ForTransferring':
                $returnValue = str_replace(
                    'Tagged for transferring',
                    'Queued for transfer to Magento',
                    $initialValue
                );
                break;
            default:
                # code...
                break;
        }
        
        return $returnValue;
    }
}
