<?php

namespace FiloBlu\Flow\Console\Command;

use Exception;
use Magento\Catalog\Model\ProductFactory;
use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory;
use Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\ConfigurableFactory;
use Magento\Framework\App\State;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * Class MissingparentsCommand
 * @package FiloBlu\Flow\Console\Command
 */
class MissingparentsCommand extends Command
{
    /**
     * @var CollectionFactory
     */
    protected $productCollectionFactory;

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

    /**
     * @var State
     */
    protected $state;
    /**
     * @var ConfigurableFactory
     */
    protected $configurableFactory;

    /**
     * MissingparentsCommand constructor.
     * @param ProductFactory $productFactory
     * @param CollectionFactory $productCollectionFactory
     * @param ConfigurableFactory $configurableFactory
     * @param State $state
     * @param string $name
     */
    public function __construct(
        ProductFactory $productFactory,
        CollectionFactory $productCollectionFactory,
        ConfigurableFactory $configurableFactory,
        State $state,
        $name = null
    ) {
        $this->productCollectionFactory = $productCollectionFactory;
        $this->productFactory = $productFactory;
        $this->state = $state;
        $this->configurableFactory = $configurableFactory;

        parent::__construct($name);
    }

    /**
     *
     */
    protected function configure()
    {
        $this->setName('flow:missingparents')->setDescription('Find missing connected parents to simples');
    }

    /**
     * @param InputInterface $input
     * @param OutputInterface $output
     * @return int
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        try {
            $this->state->setAreaCode('adminhtml');
        } catch (Exception $ex) {
            $output->writeln('Areacode was already set, skipping!');
        }

        // All simple products that should have a parent
        $product_collection = $this->getProductsCollection();
        $count = 0;

        echo "\n\nChecking now if alle the simple products 'that should have a parent' -> really have a parent?\n";
        sleep(3);

        // Product are simples
        foreach ($product_collection as $product) {
            $parentIds = $this->configurableFactory->create()->getParentIdsByChild($product->getEntityId());

            // Can't find any parent
            if (!isset($parentIds[0])) {
                $count++;
                echo "Can't find any parent connected to simple '{$product->getSku()}', it should be '{$product->getParentSku()}'\n";
            } else {
                $product_collection->removeItemByKey($product->getId());
            }
        }

        //$output->writeln("<info>Done! ({$count})</info>");

        echo "\n\nTry to fix associations based on simple's parent_sku attribute? (y/n):\n";
        $handle = fopen('php://stdin', 'rb');
        $line = trim(fgets($handle));

        if ($line === 'y') {
            echo "\n\nLoading all parents <-> childs mapping...\n";
            $parent_childs_associations_ids = $this->getConfigurablesChildsArray();
            echo "Loaded\n\n";

            // Integrating news childs (that we have found have no parent connected but have parent_sku attribute filled) on $parent_childs_associations_ids array
            foreach ($product_collection as $simple) {
                $parent_childs_associations_ids[$simple->getParentSku()][] = $simple->getEntityId();
            }

            $parents_done = [];
            foreach ($product_collection as $simple) {
                if (in_array($simple->getParentSku(), $parents_done, false)) {
                    continue;
                }

                $loaded_parent = $this->loadProductBySku($simple->getParentSku());

                if (!$loaded_parent) {
                    echo "ERROR: Not found parent SKU {$simple->getParentSku()} from simple SKU {$simple->getSku()}\n";
                    continue;
                }

                if (!isset($parent_childs_associations_ids[$simple->getParentSku()])) {
                    echo "ERROR: Parent SKU {$simple->getParentSku()} have zero childs?\n";
                    continue;
                }

                // Gettin' current parent simples
                $associatedProductIds = $parent_childs_associations_ids[$simple->getParentSku()];
                // Removing duplicates (if any)
                $associatedProductIds = array_values(array_unique($associatedProductIds));
                $childs_string = implode(',', $associatedProductIds);
                // Saving new associations
                $loaded_parent->setAssociatedProductIds($associatedProductIds);

                try {
                    //return $this->productRepositoryInterface->get($sku);
                    $loaded_parent->save();
                    echo "Saved childs IDS on parent SKU {$simple->getParentSku()} -> {$childs_string}\n";
                } catch (Exception $ex) {
                    echo "Failed saving childs IDS on parent SKU {$simple->getParentSku()} -> {$childs_string}\n";
                }

                $parents_done[] = $loaded_parent->getSku();
            }
        }

        $output->writeln("<info>Done! ({$count})</info>");

        return 0;
    }

    /**
     * @return mixed
     */
    public function getProductsCollection()
    {
        echo "Loading all simple products that should have a parent...\n";

        $collection = $this->productCollectionFactory->create();
        $collection->addAttributeToSelect('*');
        $collection->addAttributeToFilter('type_id', ['eq' => 'simple']);

        // Removing simples with no parent needed
        foreach ($collection as $simple) {
            $have_categories = count($simple->getCategoryIds());

            if (!$simple->getParentSku()) {
                echo "Simple {$simple->getSku()} have empty parent_sku, why?\n";
                $collection->removeItemByKey($simple->getId());
                continue;
            }
            if ($simple->getSku() == $simple->getParentSku()) {
                echo "Simple {$simple->getSku()} is standalone simple with {$have_categories} categories. (sku = parent_sku)\n";
                $collection->removeItemByKey($simple->getId());
                continue;
            }

            if ($simple->getSku() != $simple->getParentSku()) {
                //We need to check the conf
                $loaded_parent = $this->loadProductBySku($simple->getParentSku());

                if (!$loaded_parent || !$loaded_parent->getEntityId()) {
                    if ($have_categories) {
                        echo "Simple {$simple->getSku()} parent_sku {$simple->getParentSku()} does not exist but simple have {$have_categories} categories, maybe it's a standalone?\n";
                    } else {
                        echo "Simple {$simple->getSku()} parent_sku {$simple->getParentSku()} does not exist. Why?\n";
                    }
                    $collection->removeItemByKey($simple->getId());
                    continue;
                }

                if ($loaded_parent->getTypeId() != 'configurable') {
                    if ($have_categories) {
                        echo "Simple {$simple->getSku()} parent_sku {$simple->getParentSku()} is not a configurable but simple have {$have_categories} categories, maybe it is a standalone?\n";
                    } else {
                        echo "Simple {$simple->getSku()} parent_sku {$simple->getParentSku()} is not a configurable. Why?\n";
                    }
                    $collection->removeItemByKey($simple->getId());
                    continue;
                }
            }
        }

        return $collection;
    }

    /**
     * @param $sku
     * @return bool
     */
    public function loadProductBySku($sku)
    {
        try {
            return $this->productFactory->create()->loadByAttribute('sku', trim($sku));
        } catch (Exception $ex) {
            return false;
        }
    }

    /**
     * @return array
     */
    public function getConfigurablesChildsArray()
    {
        $result = [];
        $collection = $this->productCollectionFactory->create();
        $collection->addAttributeToSelect('*');
        $collection->addAttributeToFilter('type_id', ['eq' => 'configurable']);

        foreach ($collection as $product) {
            $_children = $product->getTypeInstance()->getUsedProducts($product);
            foreach ($_children as $child) {
                $result[$product->getSku()][] = $child->getEntityId();
            }
        }
        return $result;
    }
}
