<?php
/**
 * Copyright © 2018 Wyomind. All rights reserved.
 * See LICENSE.txt for license details.
 */

namespace Wyomind\ElasticsearchCore\Model\Indexer;

class Category extends AbstractIndexer
{
    /**
     * @var string
     */
    public $type = 'category';

    /**
     * @var string
     */
    public $name = 'Categories';

    /**
     * @var string
     */
    public $comment = 'Categories indexer';

    /**
     * @var \Magento\Catalog\Model\ResourceModel\Category\Attribute\CollectionFactory
     */
    protected $_attributeCollectionFactory = null;

    /**
     * @var \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory
     */
    protected $_productCollectionFactory = null;

    /**
     * Searchable attributes cache
     * @var \Magento\Eav\Model\Entity\Attribute[]
     */
    protected $_searchableAttributes = [];

    /**
     * @var \Wyomind\ElasticsearchCore\Helper\Category
     */
    protected $_categoryHelper = null;

    /**
     * @param \Wyomind\ElasticsearchCore\Model\Indexer\Context $context
     * @param \Magento\Catalog\Model\ResourceModel\Category\Attribute\CollectionFactory $attributeCollectionFactory
     * @param \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory
     * @param \Wyomind\ElasticsearchCore\Helper\Category $categoryHelper
     */
    public function __construct(
        Context $context,
        \Magento\Catalog\Model\ResourceModel\Category\Attribute\CollectionFactory $attributeCollectionFactory,
        \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory,
        \Wyomind\ElasticsearchCore\Helper\Category $categoryHelper
    )
    {
        parent::__construct($context);
        $this->_attributeCollectionFactory = $attributeCollectionFactory;
        $this->_productCollectionFactory = $productCollectionFactory;
        $this->_categoryHelper = $categoryHelper;
    }

    /**
     * {@inheritdoc}
     */
    public function export($storeId, $ids = [])
    {
        $this->handleLog('');
        $this->handleLog('<comment>' . __('Indexing categories for store id: ') . $storeId . '</comment>');

        $this->_eventManager->dispatch('wyomind_elasticsearchcore_category_export_before', ['store_id' => $storeId, 'ids' => $ids]);

        $categories = [];

        $defaultStore = $this->_storeManager->getStore();

        /** @var \Magento\Store\Model\Store $store */
        $store = $this->_storeManager->getStore($storeId);
        $attributesConfig = [];
        $attributes = $this->_configHelper->getEntitySearchableAttributes($this->type, $storeId);
        foreach ($attributes as $attributeCode => $attributeInfo) {
            if ($attributeInfo['c'] == "1") {
                $attributesConfig[] = $attributeCode;
            }
        }
        $rootCategoryId = $store->getRootCategoryId();

        /** @var \Magento\Catalog\Model\ResourceModel\Category\Collection $collection */
        $collection = $this->_categoryHelper->createCategoryCollection()
            ->setStoreId($storeId)
            ->addIsActiveFilter()
            ->addAttributeToSelect($attributesConfig)
            ->addAttributeToFilter('path', ['like' => "1/{$rootCategoryId}/%"]);

        // Specific documents
        if (false === empty($ids)) {
            $collection->addFieldToFilter('entity_id', ['in' => $ids]);
        }

        // Don't use : $collection->addUrlRewriteToResult(); because it use the current store id ( very bad when saving a category in the backend ! )
        $collection->joinTable(
            'url_rewrite',
            'entity_id = entity_id',
            ['request_path'],
            sprintf(
                '{{table}}.is_autogenerated = 1 AND {{table}}.store_id = %d AND {{table}}.entity_type = \'%s\'',
                $storeId, \Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator::ENTITY_TYPE
            ),
            'left'
        );

        // Store URL for the store we are indexing for, including the store code if used
        $storeUrl = str_replace(['http://', 'https://'], '', $store->getBaseUrl());
        $defaultStoreUrl = str_replace(['http://', 'https://'], '', $defaultStore->getBaseUrl());

        // Remove the storecode from the default store URL if store codes are used.
        // We'll use it to replace it with the correct store URL in the category URL.
//        if ($this->_configHelper->getStoreConfig("web/url/use_store", $storeId)) {
//            $defaultStoreUrl = substr($defaultStoreUrl, 0, strlen($defaultStore->getCode() . '/') * -1);
//        }


        $this->handleLog('<info>' . count($collection) . ' categories found</info>');
        foreach ($collection as $category) {
            $category->setStoreId($storeId);

            // getURL() on a category contains the default store URL without store code,
            // so we replace it with the correct store URL
            $url = str_replace($defaultStoreUrl, $storeUrl, $category->getUrl());


            $categories[$category->getId()] = array_merge(
                [
                    'id' => (int)$category->getId(),
                    'url' => $url,
                    'path' => $this->_categoryHelper->getCategoryPathName($category),
                    'path_plain' => str_replace(' > ', ' ', $this->_categoryHelper->getCategoryPathName($category)),
                ],
                $category->toArray($attributesConfig)
            );
        }
        $this->handleLog('<info>' . count($collection) . __(' categories indexed') . '</info>');

        yield $categories;

        $this->_eventManager->dispatch('wyomind_elasticsearchcore_category_export_after', ['store_id' => $storeId, 'ids' => $ids]);
    }

    /**
     * {@inheritdoc}
     */
    public function getProperties($store = null, $withBoost = false)
    {
        $data = [];
        $attributes = $this->getSearchableAttributes($store);

        /** @var \Magento\Eav\Model\Entity\Attribute[] $attributes */
        foreach ($attributes as $attributeCode => $attributeInfo) {
            $key = $attributeCode;
            //$attribute->setData('is_searchable', true);
            $data[$key] = $this->getAttributeProperties($attributeCode, $attributeInfo, $store, $withBoost);

            if ($key == "name") {
                $data[$key]['analyzer'] = 'std';
                $data[$key]['fields']['prefix'] = [
                    'type' => 'text',
                    'analyzer' => 'text_prefix',
                    'search_analyzer' => 'std'
                ];
                $data[$key]['fields']['suffix'] = [
                    'type' => 'text',
                    'analyzer' => 'text_suffix',
                    'search_analyzer' => 'std'
                ];
                $data[$key]['copy_to'] = 'all';
            }
        }

        $compatibility = $this->_configHelper->getCompatibility($store);

        if ($compatibility >= 6) {
            // Add URL field
            $data['url'] = [
                'type' => 'text',
                'store' => 'true',
                'index' => 'false'
            ];

            // Add category path field
            $data['path'] = [
                'type' => 'text',
                'store' => 'true',
                'copy_to' => 'all'
            ];

            $data['path_plain'] = [
                'type' => 'text',
                'store' => 'true',
                'copy_to' => 'all'
            ];

            $data['all'] = ['type' => 'text'];
        } elseif ($compatibility < 6) {
            // Add URL field
            $data['url'] = [
                'type' => 'string',
                'store' => true,
                'index' => 'no'
            ];

            // Add category path field
            $data['path'] = [
                'type' => 'string',
                'store' => true,
                'include_in_all' => true
            ];

            $data['path_plain'] = [
                'type' => 'string',
                'store' => true,
                'include_in_all' => true
            ];
        }

        $data['id'] = ['type' => 'long'];
        $properties = new \Magento\Framework\DataObject($data);

        $this->_eventManager->dispatch('wyomind_elasticsearchcore_category_index_properties', [
            'indexer' => $this,
            'store' => $store,
            'properties' => $properties
        ]);

        return $properties->getData();
    }

    /**
     * {@inheritdoc}
     */
    public function getDynamicConfigGroups()
    {
        $dynamicConfigFields['enable'] = [
            'id' => 'enable',
            'translate' => 'label comment',
            'type' => 'select',
            'sortOrder' => '10',
            'showInDefault' => '1',
            'showInWebsite' => '1',
            'showInStore' => '1',
            'label' => __('Enable Category Index'),
            'source_model' => 'Magento\Config\Model\Config\Source\Yesno',
            'comment' => __('If enabled, categories will be indexed in Elasticsearch.'),
            '_elementType' => 'field',
            'path' => 'wyomind_elasticsearchcore/types/category',
        ];

        // Indexable attributes
        $dynamicConfigFields['attributes'] = [
            'id' => 'attributes',
            'translate' => 'label comment',
            'type' => 'hidden',
            'sortOrder' => '30',
            'showInDefault' => '1',
            'showInWebsite' => '1',
            'showInStore' => '1',
            'label' => 'Attributes to index',
            '_elementType' => 'field',
            'frontend_model' => 'Wyomind\ElasticsearchCore\Block\Adminhtml\System\Config\Form\Field\CategoryAttributes',
            'path' => 'wyomind_elasticsearchcore/types/category',
            'depends' => [
                'fields' => [
                    'enable' => [
                        'id' => 'wyomind_elasticsearchcore/types/category/enable',
                        'value' => '1',
                        '_elementType' => 'field',
                        'dependPath' => [
                            0 => 'wyomind_elasticsearchcore',
                            1 => 'types',
                            2 => 'category',
                            3 => 'enable'
                        ]
                    ]
                ]
            ]
        ];

        $dynamicConfigGroups['category'] = [
            'id' => 'category',
            'translate' => 'label',
            'sortOrder' => '50',
            'showInDefault' => '1',
            'showInWebsite' => '0',
            'showInStore' => '1',
            'label' => __('Category'),
            'children' => $dynamicConfigFields,
            '_elementType' => 'group',
            'path' => 'wyomind_elasticsearchcore/types'
        ];

        return $dynamicConfigGroups;
    }

    /**
     * {@inheritdoc}
     */
    public function getEvents()
    {
        return [
            'catalog_category_save_before' => [['indexer' => $this->type, 'action' => 'productIdToReindexBeforeCategoryUpdate']],
            'catalog_category_save_commit_after' => [['indexer' => $this->type, 'action' => 'executeRow', 'fallback' => 'wyomind_elasticsearchcore_event_reindex_catalog_category_commit_after']],
            'catalog_category_delete_after' => [['indexer' => $this->type, 'action' => 'deleteRow']],
            'catalog_category_delete_commit_after' => [['indexer' => $this->type, 'action' => 'executeRow', 'fallback' => 'wyomind_elasticsearchcore_event_reindex_catalog_category_commit_after']],
            'wyomind_elasticsearchcore_full_reindex_after_category' => [['indexer' => $this->type, 'action' => 'reindex']]
        ];
    }

    /**
     * @param mixed $store
     * @return \Magento\Eav\Model\Entity\Attribute[]
     * @throws \Magento\Framework\Exception\LocalizedException
     */
    protected function getSearchableAttributes($store)
    {
        if (!isset($this->_searchableAttributes[$store])) {
            $this->_searchableAttributes[$store] = [];
            $atts = $this->_configHelper->getEntitySearchableAttributes($this->type, $store);
            foreach ($atts as $attributeCode => $attributeInfo) {
                if ($attributeInfo['c'] === "1") {
                    if ($attributeInfo['b'] == "varchar" || $attributeInfo['b'] == "text") {
                        $attributeInfo['b'] = "string";
                    } elseif ($attributeInfo['b'] == "int") {
                        $attributeInfo['b'] = "integer";
                    }
                    $this->_searchableAttributes[$store][$attributeCode] = $attributeInfo;
                }
            }
        }

        return $this->_searchableAttributes[$store];
    }

    public function getBrowseActions(&$item, $name)
    {
        $item[$name]['edit'] = [
            'href' => $this->_urlBuilder->getUrl(
                'catalog/category/edit',
                ['id' => $item['id']]
            ),
            'label' => __('Edit')
        ];
    }

    /**
     * Store in session the list of category_products before the update
     * @param string $categoryId
     */
    public function productIdToReindexBeforeCategoryUpdate($categoryId)
    {
        $productIds = [];
        $rowId = $this->moduleIsEnabled('Magento_Enterprise') ? 'row_id' : 'entity_id';

        $productCollection = $this->_productCollectionFactory->create();
        $productCollection->addAttributeToSelect('*')->addCategoriesFilter(['eq' => $categoryId]);
        foreach ($productCollection->getData() as $product) {
            $productIds[] = $product[$rowId];
        }

        if (!empty($productIds)) {
            $this->_sessionHelper->setIdsToReindex('productIdToReindexBeforeCategoryUpdate', $productIds);
            $productIndexer = $this->_indexerHelperFactory->create()->getIndexer('product');
            $productIndexer->delete($productIds);
            $productIndexer->forceIds = $productIds;
        }
    }
}