<?php
/**
 * @category Mageants SimpleConfigurable
 * @package Mageants_SimpleConfigurable
 * @copyright Copyright (c) 2017 Mageants
 * @author Mageants Team <info@mageants.com>
 */

declare(strict_types=1);

namespace Mageants\SimpleConfigurable\Plugin\Helper\Catalog;

use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\Registry;
use Magento\Store\Model\StoreManagerInterface;
use Mageants\SimpleConfigurable\Helper\Data;
use Magento\Catalog\Model\Product as ProductObj;

class Product
{
    /**
     * @var string[]
     */
    protected $allowedAttributes = [
        'name',
        'sku',
        'short_description',
        'description',
        'meta_title',
        'meta_keyword',
        'meta_description',
    ];

    /**
     * Configurable product resource model.
     *
     * @var Configurable
     */
    protected $configurableResource;

    /**
     * Core registry.
     *
     * @var Registry
     */
    protected $coreRegistry;

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

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

    /**
     * @var Data
     */
    protected $simpleConfigurableHelper;

    /**
     * @var ProductObj
     */
    protected $productObj;

    /**
     * @param Configurable $configurableResource
     * @param Registry $coreRegistry
     * @param ProductRepositoryInterface $productRepository
     * @param StoreManagerInterface $storeManager
     * @param array|null $allowedAttributes
     * @param Data $simpleConfigurableHelper
     * @param ProductObj $productObj
     */
    public function __construct(
        Configurable $configurableResource,
        Registry $coreRegistry,
        ProductRepositoryInterface $productRepository,
        StoreManagerInterface $storeManager,
        Data $simpleConfigurableHelper,
        ProductObj $productObj,
        ?array $allowedAttributes = null
    ) {
        $this->configurableResource = $configurableResource;
        $this->coreRegistry = $coreRegistry;
        $this->productRepository = $productRepository;
        if ($allowedAttributes
            && is_array($allowedAttributes)
            && !empty($allowedAttributes)
        ) {
            $this->allowedAttributes = $allowedAttributes;
        }
        $this->storeManager = $storeManager;
        $this->simpleConfigurableHelper = $simpleConfigurableHelper;
        $this->productObj =  $productObj;
        $this->getAllowedAttributes();
    }

    /**
     * Check is enable
     *
     * @return boolean
     */
    public function isScpEnable()
    {
        return $this->simpleConfigurableHelper->isEnable();
    }

    /**
     * Update page url
     *
     * @return boolean
     */
    public function updateScpPageUrl()
    {
        return $this->simpleConfigurableHelper->replaceUrl();
    }

    /**
     * @param \Magento\Catalog\Helper\Product $subject
     * @param $productId
     * @param $controller
     * @param null $params
     * @return array
     */
    public function beforeInitProduct(
        \Magento\Catalog\Helper\Product $subject,
        $productId,
        $controller,
        $params = null
    ) {
        $enable = $this->isScpEnable();
        $updatePageUrl = $this->updateScpPageUrl();
        if ($updatePageUrl && $enable && $productId) {
            $parentIds = $this->configurableResource->getParentIdsByChild($productId);
            if (!empty($parentIds)) {
                $childProductId = $productId;
                try {
                    $parentProduct = $this->productRepository->getById(
                        $parentIds[0],
                        false,
                        $this->storeManager->getStore()->getId()
                    );
                    $childProduct = $this->productRepository->getById(
                        $childProductId,
                        false,
                        $this->storeManager->getStore()->getId()
                    );
                } catch (NoSuchEntityException $e) {
                    return [$productId, $controller, $params];
                }

                if ($parentProduct->isSaleable() && $subject->canShow($parentProduct)) {
                    $productId = $parentIds[0];
                    return [$productId, $controller, $params, $childProductId];
                }
            }
        }
        return [$productId, $controller, $params];
    }

    /**
     * Set configurable meta data based on current simple product.
     *
     * @param \Magento\Catalog\Helper\Product $subject
     * @param $result
     * @param $productId
     * @param $controller
     * @param null $params
     * @param null $childProductId
     * @return bool|\Magento\Catalog\Model\Product
     * @throws NoSuchEntityException
     */
    public function afterInitProduct(
        \Magento\Catalog\Helper\Product $subject,
        $result,
        $productId,
        $controller,
        $params = null,
        $childProductId = null
    ) {
        $enable = $this->isScpEnable();
        $updatePageUrl = $this->updateScpPageUrl();
        if ($updatePageUrl && $enable && $childProductId && $result) {
            $storeId = $this->storeManager->getStore()->getId();
            $childProduct = $this->productRepository->getById($childProductId, false, $storeId);

            foreach ($this->allowedAttributes as $attribute) {
                if ($childProduct->getData($attribute)) {
                    $result->setData($attribute, $childProduct->getData($attribute));
                }
            }
            $result->setFinalPrice($childProduct->getFinalPrice());
            $result->setPrice($childProduct->getPrice());
            $result->setSpecialPrice($childProduct->getSpecialPrice());
            /**
             * Set product images.
             * If simple product does not have images than parent will be used.
             */
            if ($childProduct->getData('image')) {
                $result->setData('image', $childProduct->getData('image'));
            }
            if ($childProduct->getData('small_image')) {
                $result->setData('small_image', $childProduct->getData('small_image'));
            }
            if ($childProduct->getData('thumbnail')) {
                $result->setData('thumbnail', $childProduct->getData('thumbnail'));
            }
            if ($childProduct->getMediaGalleryImages()) {
                $result->setMediaGalleryImages($childProduct->getMediaGalleryImages());
            }
            /**
             * Add updated product to registry.
             */
            $this->coreRegistry->unregister('product');
            if ($this->simpleConfigurableHelper->updateMetaDta()) {
                $result->setScpChildUrl($this->simpleConfigurableHelper->getProductFullUrlByProduct($childProduct));
            }
            $this->coreRegistry->register('product', $result);
            $this->coreRegistry->register('mageants_scp_child_product', $childProduct);
        } elseif ($enable) {
            $currentProduct = $this->productRepository->getById(
                $productId,
                false,
                $this->storeManager->getStore()->getId()
            );
            $productType = $currentProduct->getTypeId();
            if ($productType == 'configurable') {

                $preSelectProductData = $this->getConfigurablePreselectOption($productId);
                if (!empty($preSelectProductData)) {
                    $preSelectId = $this->getMatchingChildProduct($productId,$preSelectProductData);

                    $storeId = $this->storeManager->getStore()->getId();
                    $childProduct = $this->productRepository->getById($preSelectId, false, $storeId);

                    foreach ($this->allowedAttributes as $attribute) {
                        if ($childProduct->getData($attribute)) {
                            $result->setData($attribute, $childProduct->getData($attribute));
                        }
                    }
                    $result->setFinalPrice($childProduct->getPrice());
                    $result->setPrice($childProduct->getPrice());
                    $result->setSpecialPrice($childProduct->getSpecialPrice());
                    /**
                     * Set product images.
                     * If simple product does not have images than parent will be used.
                     */
                    if ($childProduct->getData('image')) {
                        $result->setData('image', $childProduct->getData('image'));
                    }
                    if ($childProduct->getData('small_image')) {
                        $result->setData('small_image', $childProduct->getData('small_image'));
                    }
                    if ($childProduct->getData('thumbnail')) {
                        $result->setData('thumbnail', $childProduct->getData('thumbnail'));
                    }
                    if ($childProduct->getMediaGalleryImages()) {
                        $result->setMediaGalleryImages($childProduct->getMediaGalleryImages());
                    }
                    /**
                     * Add updated product to registry.
                     */
                    $this->coreRegistry->unregister('product');
                    $this->coreRegistry->register('product', $result);
                }
            }
        }
        return $result;
    }

    /**
     * Get Allowed Attributes
     *
     * @return void
     */
    public function getAllowedAttributes()
    {
        $reloadMeta = $this->simpleConfigurableHelper->updateMetaDta();
        if (!$reloadMeta) {
            $this->allowedAttributes = array_diff(
                $this->allowedAttributes,
                ['meta_title', 'meta_keyword', 'meta_description']
            );
        }
        $reloadAttributes = $this->simpleConfigurableHelper->getReloadContent();
        $reloadAttributes = explode(',', $reloadAttributes);

        $notReloadAttributes = array_diff(
            ['name', 'sku', 'short_description', 'description'],
            $reloadAttributes
        );

        $this->allowedAttributes = array_diff(
            $this->allowedAttributes,
            $notReloadAttributes
        );
    }

    /**
     * Get preselect source
     *
     * @return string
     */
    public function getPreselectSource()
    {
        $preselectVal = $this->simpleConfigurableHelper->getPreselectSource();
        if (!$preselectVal) {
            $preselectVal = 0;
        }
        return $preselectVal;
    }

    /**
     * Get configurable preselect option
     *
     * @param int $productId
     * @return array
     */
    public function getConfigurablePreselectOption($productId)
    {
        $children = $this->getProductById($productId)->getTypeInstance()->getUsedProducts($this->getProductById($productId));
        $optionDataSet = $this->getProductById($productId)->getTypeInstance()->getConfigurableOptions($this->getProductById($productId));

        if ($this->getPreselectSource() == 0) {
            if (!empty($optionDataSet)):
                foreach ($optionDataSet as $opt => $optionData):
                    foreach ($optionData as $optionVal):
                        $optionCode[$opt] = $optionVal['attribute_code'];
                        $preoptionCode[$optionVal['attribute_code']] = $optionVal['value_index'];
                        break;
                    endforeach;
                endforeach;
            endif;
            return $preoptionCode;
        } elseif ($this->getPreselectSource() == 1) {
            if (!$this->getProductById($productId)->getIsDefaultSelected()) {
                return [];
            } else {
                foreach ($optionDataSet as $opt => $optionData):
                    foreach ($optionData as $optionVal):
                        $optionCode[$opt] = $optionVal['attribute_code'];
                        break;
                    endforeach;
                endforeach;

                $selectedOpt = [];
                $product = $this->productObj->load($this->getProductById($productId)->getIsDefaultSelected());
                foreach ($optionCode as $k => $optCode) {
                    $selectedOpt[$optCode] = $product->getData($optCode);
                }

                return $selectedOpt;
            }
        } elseif ($this->getPreselectSource() == 2) {
            if (!empty($optionDataSet)):
                foreach ($optionDataSet as $opt => $optionData):
                    foreach ($optionData as $optionVal):
                        $optionCode[$opt] = $optionVal['attribute_code'];
                        $preoptionCode[$optionVal['attribute_code']] = $optionVal['value_index'];
                        break;
                    endforeach;
                endforeach;
            endif;

            $maxPrice = 0;
            $maxPriceProductId = 0;
            $productPriceArray = [];

            foreach ($children as $child) {
                $assoProduct = $child;
                // Skip disabled products
                if ($assoProduct->getStatus() != 1) {
                    continue;
                }
                if ($maxPrice < $assoProduct->getPrice()) {
                    $maxPrice = $assoProduct->getPrice();
                    $maxPriceProductId = $assoProduct->getId();
                }

                $productPriceArray[] = $assoProduct->getPrice();
            }

            if (!empty($productPriceArray) && count(array_unique($productPriceArray)) === 1) {
                return $preoptionCode;
            } else {
                $selectedOpt = [];
                if ($maxPriceProductId) {
                    $product = $this->productObj->load($maxPriceProductId);
                    foreach ($optionCode as $k => $optCode) {
                        $selectedOpt[$optCode] = $product->getData($optCode);
                    }
                }

                return $selectedOpt;
            }
        } elseif ($this->getPreselectSource() == 3) {
            if (!empty($optionDataSet)):
                foreach ($optionDataSet as $opt => $optionData):
                    foreach ($optionData as $optionVal):
                        $optionCode[$opt] = $optionVal['attribute_code'];
                        $preoptionCode[$optionVal['attribute_code']] = $optionVal['value_index'];
                        break;
                    endforeach;
                endforeach;
            endif;

            $lowPrice = null; // Changed from "" to null for clarity
            $lowPriceProductId = 0;
            $productPriceArray = [];

            foreach ($children as $child) {
                $assoProduct = $child;
                // Skip disabled products
                if ($assoProduct->getStatus() != 1) {
                    continue;
                }
                $currentPrice = $assoProduct->getPrice();
                if ($lowPrice === null || $lowPrice > $currentPrice) {
                    $lowPrice = $currentPrice;
                    $lowPriceProductId = $assoProduct->getId();
                }

                $productPriceArray[] = $currentPrice;
            }

            if (!empty($productPriceArray) && count(array_unique($productPriceArray)) === 1) {
                return $preoptionCode;
            } else {
                $selectedOpt = [];
                if ($lowPriceProductId) {
                    $product = $this->productObj->load($lowPriceProductId);
                    foreach ($optionCode as $k => $optCode) {
                        $selectedOpt[$optCode] = $product->getData($optCode);
                    }
                }

                return $selectedOpt;
            }
        } else {
            $selectedOpt = [];
            return $selectedOpt;
        }
    }

    /**
     * Find matching child product of a configurable product based on attribute values.
     *
     * @param int $parentId
     * @param array $attributeValues (e.g. ['br_fa' => 6142, 'br_groesse' => 155, 'br_glas' => 6126])
     * @return \Magento\Catalog\Model\Product|null
     */
    public function getMatchingChildProduct($parentId, array $attributeValues)
    {
        try {
            $parentProduct = $this->productRepository->getById(
                $parentId,
                false,
                $this->storeManager->getStore()->getId()
            );
        } catch (NoSuchEntityException $e) {
            return null;
        }

        $children = $parentProduct->getTypeInstance()->getUsedProducts($parentProduct);

        foreach ($children as $child) {
            $isMatch = true;
            foreach ($attributeValues as $code => $value) {
                if ((int)$child->getData($code) !== (int)$value) {
                    $isMatch = false;
                    break;
                }
            }
            if ($isMatch) {
                return $child->getId();
            }
        }

        return null;
    }

    /**
     * Get Product By Id
     *
     * @param int $productId
     * @return void
     */
    public function getProductById($productId)
    {
        return $this->productRepository->getById(
            $productId,
            false,
            $this->storeManager->getStore()->getId()
        );
    }
}
