<?php

namespace FiloBlu\Refilo\Helper\Catalog;

use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Catalog\Model\Product\Attribute\Source\Status;
use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory;
use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
use Magento\Eav\Api\AttributeRepositoryInterface;
use Magento\Framework\Api\SearchCriteriaBuilder;
use Magento\Framework\Api\SortOrderBuilder;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Store\Api\Data\StoreInterface;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Swatches\Helper\Data;
use Magento\Swatches\Helper\Media;
use Magento\Framework\App\Config\ScopeConfigInterface;
use function in_array;


/**
 *
 */
class ProductAttributeHelperExt extends \FiloBlu\Refilo\Helper\Catalog\ProductAttributeHelper
{
    /** @var string  */
    const XML_OVERRIDE_PRODUCT_ATTRIBUTE_HELPER = 'refilo_catalog/general/override_product_helper';
    /** @var StoreInterface */
    protected $currentStore = null;
    /** @var array|null */
    protected $currentAttributes = null;
    /** @var array */
    protected $imagePlaceholderAttributes = [];
    /** @var array */
    protected $attributes;
    /** @var array */
    protected $filterableAttributes = [];
    /** @var array */
    protected $sortableAttributes = [];
    /** @var array */
    protected $lookupOptions = [];
    /** @var CollectionFactory */
    private $productCollection;
    /**
     * @var ScopeConfigInterface
     */
    private $scopeConfig;

    /**
     * ProductAttributeHelper constructor.
     * @param ScopeConfigInterface $scopeConfig
     * @param SearchCriteriaBuilder $searchCriteriaBuilder
     * @param SortOrderBuilder $sortOrderBuilder
     * @param AttributeRepositoryInterface $attributeRepository
     * @param StoreManagerInterface $storeManager
     * @param Data $swatchHelper
     * @param Media $swatchMediaHelper
     * @param CollectionFactory $productCollection
     * @param array $mandatoryAttributes
     * @param array $excludedAttributes
     */
    public function __construct(
        ScopeConfigInterface         $scopeConfig,
        SearchCriteriaBuilder        $searchCriteriaBuilder,
        SortOrderBuilder             $sortOrderBuilder,
        AttributeRepositoryInterface $attributeRepository,
        StoreManagerInterface        $storeManager,
        Data                         $swatchHelper,
        Media                        $swatchMediaHelper,
        CollectionFactory            $productCollection,
        array                        $mandatoryAttributes,
        array                        $excludedAttributes
    )
    {
        $this->searchCriteriaBuilder = $searchCriteriaBuilder;
        $this->sortOrderBuilder = $sortOrderBuilder;
        $this->attributeRepository = $attributeRepository;
        $this->storeManager = $storeManager;
        $this->swatchHelper = $swatchHelper;
        $this->swatchMediaHelper = $swatchMediaHelper;
        $this->productCollection = $productCollection;
        $this->excludedAttributes = $excludedAttributes;
        $this->mandatoryAttributes = $mandatoryAttributes;
        parent::__construct(
            $searchCriteriaBuilder,
            $sortOrderBuilder,
            $attributeRepository,
            $storeManager,
            $swatchHelper,
            $swatchMediaHelper,
            $productCollection,
            $mandatoryAttributes,
            $excludedAttributes
        );

        $this->scopeConfig = $scopeConfig;
    }

    /**
     * @param ProductInterface $product
     * @param array $excludeList
     * @return array
     * @throws NoSuchEntityException
     * @throws LocalizedException
     * @override \FiloBlu\Refilo\Helper\Catalog\ProductAttributeHelper::getFilterableAttributesWithChildren
     */
    public function getFilterableAttributesWithChildren(ProductInterface $product, array $excludeList = []): array
    {
        if(!$this->scopeConfig->getValue(self::XML_OVERRIDE_PRODUCT_ATTRIBUTE_HELPER)){
            return parent::getFilterableAttributesWithChildren($product, $excludeList);
        }

        $attributeList = $this->getFilterableAttributesList();
        $children = $product->getTypeInstance()->getChildrenIds($product->getId());
        $storeId = $product->getStore()->getId();
        $products = [$product->getId() => $product];

        if (!empty($children)) {
            //TODO get simple product from grouped
            $attributeCodes = array_keys($attributeList[$storeId]);
            $productCollection = $this->productCollection->create();
            $productCollection
                ->setStore($product->getStore())
                ->addIdFilter(current($children))
                ->addAttributeToSelect($attributeCodes)
                ->addAttributeToSelect(ProductInterface::STATUS)
                ->joinTable(
                    'cataloginventory_stock_item',
                    'product_id = entity_id',
                    ['is_in_stock'],
                    null,
                    'left'
                );
            $products = $productCollection->getItems() + [$product->getId() => $product];
        }

        $superAttributes = $excludeList;

        if ($product->getTypeId() === Configurable::TYPE_CODE) {
            foreach ($product->getTypeInstance()->getUsedProductAttributes($product) as $attribute) {
                $superAttributes[] = $attribute->getAttributeCode();
            }
        }

        $reduced = array_reduce($products, function ($carry, $item) use ($storeId, $attributeList, $excludeList, $superAttributes) {
            if ((int)$item->getStatus() !== Status::STATUS_ENABLED || !(bool)$item->getIsInStock()) {
                return $carry;
            }

            if ($item->getTypeId() === Configurable::TYPE_CODE) {
                $excludeList = $superAttributes;
            }

            foreach ($attributeList[$storeId] as $code => $attribute) {
                if (in_array($code, $excludeList, true)) {
                    continue;
                }

                $type = $attribute['type'];
                $carry[$code]['title'] = $attribute['title'];
                $carry[$code]['type'] = $type;
                $productValue = $item->getData($code);

                if (empty($productValue)) {
                    continue;
                }

                switch ($type) {
                    case 'select':
                        if (isset($this->lookupOptions[$storeId][$code]['value'][$productValue])) {
                            $carry[$code]['values'][$productValue] = $this->lookupOptions[$storeId][$code]['value'][$productValue] ?? null;
                        }

                        break;
                    case 'multiselect':
                        $attributeData = explode(',', $productValue ?? '');

                        if (is_scalar($attributeData)) {
                            $attributeData = [$attributeData];
                        }

                        $keys = array_map('\intval', array_filter($attributeData, '\strlen'));

                        foreach ($keys as $k) {
                            if (isset($this->lookupOptions[$storeId][$code]['value'][$k])) {
                                $carry[$code]['values'][$k] = $this->lookupOptions[$storeId][$code]['value'][$k];
                            }
                        }

                        break;
                    default:
                        $carry[$code]['values'][$productValue] = $productValue;
                        break;
                }
            }

            return $carry;
        }, []);

        $attributes = [];
        foreach ($reduced as $code => $data) {
            if (!isset($data['values'])) {
                continue;
            }
            $attributes[$code] = [
                'title' => $data['title'],
                'key' => array_keys($data['values']),
                'value' => array_values($data['values'])
            ];
        }
        return $attributes;
    }
}
