<?php

namespace FiloBlu\Flow\Helper;

use Exception;
use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Catalog\Model\AbstractModel;
use Magento\Catalog\Model\Product\Attribute\Source\Status;
use Magento\Catalog\Model\ProductRepository;
use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory;
use Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\Helper\AbstractHelper;
use Magento\Framework\App\Helper\Context;
use Magento\Framework\App\ProductMetadataInterface;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\DataObject;
use Magento\Framework\DB\Adapter\AdapterInterface;
use Magento\Framework\Module\Manager;
use Magento\Store\Model\ScopeInterface;
use Monolog\Logger;
use Magento\Catalog\Model\ProductFactory;

/**
 * Class Product
 * @package FiloBlu\Flow\Helper
 */
class Product extends AbstractHelper
{
    /**
     * @var string
     */
    public const CONFIG_FLOW_ATTRIBUTE_PATH = 'filoblu_flow/flow_process/flow_attribute_select';
    /**
     * @var string
     */
    public const CONFIG_FLOW_ASSIGN_FULL_TREE_PATH = 'filoblu_flow/flow_process/flow_assign_full_tree_category';
    /**
     * @var string
     */
    public const CONFIG_FLOW_ADD_TO_ALL_WEBSITES = 'filoblu_flow/flow_ite/add_to_websites';
    /**
     * @var string
     */
    public const CONFIG_FLOW_ITE_COPY_NAME_TO_DESCRIPTION = 'filoblu_flow/flow_ite/copy_name_in_description';
    /**
     * @var string
     */
    public const CONFIG_FLOW_ITE_COPY_NAME_TO_SHORT_DESCRIPTION = 'filoblu_flow/flow_ite/copy_name_in_short_description';
    /**
     * @var string
     */
    public const CONFIG_FLOW_ITE_DISABLE_SHOP_STATUS = 'filoblu_flow/flow_ite/disable_shop_status';
    /**
     * @var string
     */
    public const CONFIG_FLOW_ITE_HIDE_PRICE_STATUS = 'filoblu_flow/flow_ite/hide_price_status';

    /**
     * @var string
     */
    public const CONFIG_FLOW_GROUPED_ATTRIBUTE_PATH = 'filoblu_flow/flow_process/flow_attribute_togrouped';

    /**
     * @var string
     */
    public const CONFIG_FLOW_REPLACEWEBSITE_ATTRIBUTE_PATH = 'filoblu_flow/flow_process/flow_attribute_tochangewebsite';

    /**
     * @var ProductMetadataInterface
     */
    protected $_productMetadata;

    /**
     * @var ResourceConnection
     */
    protected $_resource;

    /**
     * @var
     */
    protected $connection;

    /**
     * @var Logger
     */
    protected $_logger;

    /**
     * @var ProductRepository
     */
    protected $_productRepository;

    /**
     * @var Manager
     */
    protected $_moduleManager;

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

    /**
     * @var Configurable
     */
    private $configurableProduct;

    /**
     * @var ProductFactory
     */
    protected $productFactory;

    /**
     * Product constructor.
     * @param Context $context
     * @param ProductMetadataInterface $productMetadata
     * @param ResourceConnection $resource
     * @param LoggerProvider $loggerProvider
     * @param CollectionFactory $productCollectionFactory
     * @param ProductRepository $productRepository
     * @param Configurable $configurableProduct
     * @param ProductFactory $productFactory
     */
    public function __construct(
        Context                  $context,
        ProductMetadataInterface $productMetadata,
        ResourceConnection       $resource,
        LoggerProvider           $loggerProvider,
        CollectionFactory        $productCollectionFactory,
        ProductRepository        $productRepository,
        Configurable             $configurableProduct,
        ProductFactory           $productFactory
    ) {
        $this->scopeConfig = $context->getScopeConfig();
        $this->_resource = $resource;
        $this->_productMetadata = $productMetadata;
        $this->_logger = $loggerProvider->getLogger();
        $this->_productRepository = $productRepository;
        $this->_moduleManager = $context->getModuleManager();
        $this->productCollectionFactory = $productCollectionFactory;
        $this->configurableProduct = $configurableProduct;
        $this->productFactory = $productFactory;
        parent::__construct($context);
    }

    /**
     * Do I need to assign the product to the full category tree?
     * @return bool
     */
    public function assignFullTree()
    {
        return (bool)$this->scopeConfig->getValue(
            self::CONFIG_FLOW_ASSIGN_FULL_TREE_PATH,
            ScopeInterface::SCOPE_WEBSITE
        );
    }

    /**
     * @return mixed
     */
    public function getCopyNameInDescription()
    {
        return $this->scopeConfig->getValue(
            self::CONFIG_FLOW_ITE_COPY_NAME_TO_DESCRIPTION,
            ScopeInterface::SCOPE_WEBSITE
        );
    }

    /**
     * @return mixed
     */
    public function getCopyNameInShortDescription()
    {
        return $this->scopeConfig->getValue(
            self::CONFIG_FLOW_ITE_COPY_NAME_TO_SHORT_DESCRIPTION,
            ScopeInterface::SCOPE_WEBSITE
        );
    }

    /**
     * @return bool
     */
    public function isDisableShopModuleEnabled()
    {
        return $this->_moduleManager->isEnabled('FiloBlu_DisableShop');
    }

    /**
     * @return mixed
     */
    public function getDisableShopStatus()
    {
        return $this->scopeConfig->getValue(self::CONFIG_FLOW_ITE_DISABLE_SHOP_STATUS,
            ScopeInterface::SCOPE_WEBSITE);
    }

    /**
     * @return mixed
     */
    public function getHidePriceStatus()
    {
        return $this->scopeConfig->getValue(self::CONFIG_FLOW_ITE_HIDE_PRICE_STATUS,
            ScopeInterface::SCOPE_WEBSITE);
    }

    /**
     * @return bool
     */
    public function createNewProducts()
    {
        return true;
    }

    /**
     * @return int
     */
    public function getInitialProductStatus()
    {
        return Status::STATUS_DISABLED;
    }

    /**
     * @return mixed
     */
    public function getAddToWebsites()
    {
        return $this->scopeConfig->getValue(self::CONFIG_FLOW_ADD_TO_ALL_WEBSITES);
    }

    /**
     * @param $childProducts
     * @return mixed
     * @throws Exception
     */
    public function getParentProductsIds($childProducts)
    {
        $relationTableName = $this->getConnection()->getTableName('catalog_product_super_link');

        $products = $this->productCollectionFactory->create();

        $products->getSelect()->join(['super_link' => $relationTableName], 'e.entity_id = super_link.parent_id');
        $products->getSelect()->where('super_link.product_id IN (?)', $childProducts);
        //$products->addFieldToFilter('type_id', $productType);
        $products->getSelect()->group('e.entity_id');

        $result = $products->getAllIds();

        // Need to convert row_id into entity_id
        if ($this->isEnterpriseEdition() && count($result)) {
            foreach ($result as $key => $value) {
                if (!$value) {
                    continue;
                }

                try {
                    $entity_id = $this->getConnection()->fetchRow(
                        'SELECT entity_id FROM catalog_product_entity WHERE row_id = ?',
                        [$value]
                    );
                } catch (Exception $e) {
                    $message = "Can't find entity_id from row_id '{$value}')";
                    $this->_logger->info($message);
                    throw new Exception($e->getMessage());
                }

                if (isset($entity_id['entity_id']) && $entity_id['entity_id'] > 0) {
                    $result[$key] = $entity_id['entity_id'];
                }
            }
        }

        return $result;
    }

    /**
     * @return AdapterInterface
     */
    public function getConnection()
    {
        if (!$this->connection) {
            $this->connection = $this->_resource->getConnection();
        }
        return $this->connection;
    }

    /**
     *
     * @return bool
     */
    protected function isEnterpriseEdition()
    {
        return ($this->_productMetadata->getEdition() === 'Enterprise') ? true : false;
    }

    /**
     * @return array
     */
    public function getSimpleToConfigurableSyncAttributes()
    {
        $attributesToCopy = $this->scopeConfig->getValue(self::CONFIG_FLOW_ATTRIBUTE_PATH,
            ScopeInterface::SCOPE_WEBSITE);

        if (!empty($attributesToCopy)) {
            return explode(',', $attributesToCopy);
        }

        return [];
    }

    /**
     * @return bool
     */
    public function getUpdateConfigurablePrice()
    {
        return false;
    }

    /**
     * Get Configurable Childs by Sku
     *
     * @param $sku
     *
     * @return array
     */
    public function getConfigurableChildsBySku($sku)
    {
        $simples = [];

        try {
            // Get Product
            $product = $this->_productRepository->get($sku);

            //If product is configurable
            if ($product->getTypeId() === 'configurable') {
                $attributes = $product->getTypeInstance()->getConfigurableOptions($product);
                //Get childs for super attribute
                foreach ($attributes as $attribute) {
                    foreach ($attribute as $child) {
                        $simples[] = $child['sku'];
                    }
                }
            } //if is simple
            else {
                $simples[] = $sku;
            }
        } catch (Exception $e) {
            $this->_logger->info($e->getMessage() . ' for sku ' . $sku);
            return [];
        }

        return $simples;
    }

    /**
     * @param $initialArray
     * @param $arrayToSubtract
     * @return array
     */
    public function arrayDiff($initialArray, $arrayToSubtract): array
    {
        return array_diff($initialArray, $arrayToSubtract);
    }

    /**
     * get configurable attributes used in the project
     *
     * @return array
     */
    public function getSuperAttributesArray(): array
    {
        $superAttributesTable = $this->getConnection()->getTableName('catalog_product_super_attribute');
        $eavAttributeTable = $this->getConnection()->getTableName('eav_attribute');

        $result = $this->getConnection()->fetchAll(
            "SELECT DISTINCT
                attribute_code
            FROM
                {$superAttributesTable} AS sa
            LEFT JOIN
                {$eavAttributeTable} AS ea
            ON
                sa.attribute_id = ea.attribute_id"
        );

        $list = [];
        foreach ($result as $attribute) {
            $list[] = $attribute['attribute_code'];
        }

        return $list;
    }

    /**
     * @param ProductInterface $child
     * @return array|DataObject[]
     */
    public function getParentsByChild(ProductInterface $child)
    {
        $parents = [];
        $parentIds = $this->configurableProduct->getParentIdsByChild($child->getId());

        if (!empty($parentIds)) {
            return $this->productCollectionFactory->create()
                ->setStore($child->getStore())
                ->addAttributeToSelect('*')
                ->addIdFilter($parentIds)
                ->getitems();
        }

        return $parents;
    }

    /**
     * @return array
     */
    public function getSimpleAttributesSyncToGrouped()
    {
        $attributesToCopy = $this->scopeConfig->getValue(
            self::CONFIG_FLOW_GROUPED_ATTRIBUTE_PATH,
            ScopeInterface::SCOPE_WEBSITE
        );

        if (!empty($attributesToCopy)) {
            return explode(',', $attributesToCopy);
        }

        return [];
    }

    /**
     * @param $childId
     * @return bool|AbstractModel
     */
    public function getGroupedIdByChildId($childId) {

        $product = $this->productFactory->create()->setStoreId(0)->load($childId);
        if($product->getId()) {
            $grouped = $this->productFactory->create()->setStoreId(0)->loadByAttribute('sku', $product->getData('article_code'));
            if($grouped) {
                return $grouped;
            }
        }
        return false;
    }

    /**
     * @return array
     */
    public function getAttributesExcludedToChangeWebsite()
    {
        $attributesToExclude = $this->scopeConfig->getValue(
            self::CONFIG_FLOW_REPLACEWEBSITE_ATTRIBUTE_PATH,
            ScopeInterface::SCOPE_WEBSITE
        );

        if (!empty($attributesToExclude)) {
            return explode(',', $attributesToExclude);
        }

        return [];
    }

    /**
     * @return ScopeConfigInterface
     */
    public function getScopeConfig(): ScopeConfigInterface
    {
        return $this->scopeConfig;
    }
}
