<?php

namespace FiloBlu\Flow\Console\Command;

use Exception;
use FiloBlu\Flow\Helper\Product;
use FiloBlu\Flow\Model\Inboundflow;
use FiloBlu\Flow\Model\InboundflowFactory;
use Magento\Framework\App\Area;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\App\State;
use Magento\Framework\DB\Adapter\AdapterInterface;
use Magento\Store\Model\ScopeInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * Class CopyToConfigurableCommand
 * @package FiloBlu\Flow\Console\Command
 */
class CopyToConfigurableCommand extends Command
{
    /**
     * @var string
     */
    const FLOW_ARGUMENT = 'flow';

    /**
     * @var array
     */
    protected $excludeFromBulk = ['sku'];

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

    /**
     * @var InboundflowFactory
     */
    protected $inboundFlowFactory;

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

    /**
     * @var ResourceConnection
     */
    private $resourceConnection;
    /**
     * @var ScopeConfigInterface
     */
    private $scopeConfig;

    /**
     * CopyToConfigurableCommand constructor.
     * @param State $state
     * @param ResourceConnection $resourceConnection
     * @param InboundflowFactory $inboundFlowFactory
     * @param ScopeConfigInterface $scopeConfig
     * @param string $name
     */
    public function __construct(
        State $state,
        ResourceConnection $resourceConnection,
        InboundflowFactory $inboundFlowFactory,
        ScopeConfigInterface $scopeConfig,
        $name = null
    ) {
        parent::__construct($name);
        $this->resourceConnection = $resourceConnection;
        $this->state = $state;
        $this->inboundFlowFactory = $inboundFlowFactory;
        $this->scopeConfig = $scopeConfig;
    }

    /**
     *
     */
    protected function configure()
    {
        $this->setName('flow:copytoconfigurable')
            ->setDescription('copy attributes from simple to configurable by eavs flow id')
            ->setDefinition([
                new InputArgument(
                    self::FLOW_ARGUMENT,
                    InputArgument::REQUIRED,
                    'Flow'
                ),
            ]);
    }

    /**
     * @param InputInterface $input
     * @param OutputInterface $output
     * @return int|null|void
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        try {
            $this->state->setAreaCode(Area::AREA_ADMINHTML);
        } catch (Exception $e) {
            $output->writeln('Area code was already set, skipping!');
        }

        $flowId = (int)$input->getArgument(self::FLOW_ARGUMENT);

        $inboundFlow = $this->inboundFlowFactory->create()->load($flowId);

        if ($inboundFlow === null) {
            $output->writeln("<error>Flow with id {$flowId} does not exists!</error>");
            return;
        }

        if ($inboundFlow->getFlow() !== 'From\Eavs') {
            $output->writeln("<error>Flow with id {$flowId} is not a From\Eavs type!</error>");
            return;
        }

        $this->bulkCopyAttributesToConfigurable($inboundFlow);

        $output->writeln('<info>Attributes copied from simples to configurables!</info>');

        return 0;
    }

    /**
     * @param Inboundflow $inboundflow
     */
    public function bulkCopyAttributesToConfigurable(Inboundflow $inboundflow)
    {
        $lookupAttributeTable = [
            'datetime' => $this->getConnection()->getTableName('catalog_product_entity_datetime'),
            'int' => $this->getConnection()->getTableName('catalog_product_entity_int'),
            'text' => $this->getConnection()->getTableName('catalog_product_entity_text'),
            'varchar' => $this->getConnection()->getTableName('catalog_product_entity_varchar'),
            'decimal' => $this->getConnection()->getTableName('catalog_product_entity_decimal')
        ];

        $excludedAttributes = $this->excludeFromBulk;

        $attributesToCopy = $this->getAttributesToCopy();

        $allowedAttributesToCopy = array_diff($attributesToCopy, $excludedAttributes);

        if (empty($allowedAttributesToCopy)) {
            return;
        }

        $sqlAttributes = [];

        foreach ($allowedAttributesToCopy as $allowedAttribute) {
            $sqlAttributes[] = $this->getConnection()->quote($allowedAttribute);
        }

        $attributesSet = implode(',', $sqlAttributes);

        $safeAttributes = $this->getAttributesSafeToCopy($inboundflow, $attributesSet);

        /* SELECT PARENT - CHILD */

        $sql = "SELECT
	cpe.row_id as simple_row_id,
	sl.product_id as simple_entity_id,
	sl.parent_id as configurable_row_id
FROM
	catalog_product_super_link as sl
	LEFT JOIN catalog_product_entity as cpe on cpe.entity_id = sl.product_id
WHERE
	sl.product_id in (
        SELECT
			c.entity_id
		FROM
			flow_from_eavs AS f
		LEFT JOIN catalog_product_entity as c ON
			c.sku = f.e
            AND c.type_id = 'simple'
            AND f.a IN  ({$attributesSet})
        WHERE f.meta_file = ? AND f.meta_processed = 1
	)
GROUP BY
	sl.parent_id";
        $results = $this->getConnection()->fetchAll($sql, [$inboundflow->getId()]);

        foreach ($results as $entry) {
            /*
            simple_row_id,
            simple_entity_id,
            configurable_row_id
            */

            $this->copyAttrybutesToConfigurableByRowId($safeAttributes, $lookupAttributeTable, $entry['configurable_row_id'], $entry['simple_row_id']);
        }
    }

    /**
     * @return AdapterInterface
     */
    protected function getConnection()
    {
        if ($this->connection === null) {
            $this->connection = $this->resourceConnection->getConnection();
        }

        return $this->connection;
    }

    /**
     * @return array
     */
    protected function getAttributesToCopy()
    {
        $storeScope = ScopeInterface::SCOPE_WEBSITE;

        $attributesToCopy = $this->scopeConfig->getValue(Product::CONFIG_FLOW_ATTRIBUTE_PATH, $storeScope);

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

        return [];
    }


    /**
     * @param Inboundflow $inboundflow
     * @param string $attributeSet quoted string like "'size','description','shorted_description'"
     * @return mixed
     */
    protected function getAttributesSafeToCopy(Inboundflow $inboundflow, $attributeSet)
    {
        $superAttributesTable = $this->getConnection()->getTableName('catalog_product_super_attribute');
        $eavAttributeTable = $this->getConnection()->getTableName('eav_attribute');
        $superLinkTable = $this->getConnection()->getTableName('catalog_product_super_link');

        return $this->getConnection()->fetchAll("SELECT
            `attribute_id`,
            `attribute_code`,
            `backend_type`
        FROM
            {$eavAttributeTable}
        WHERE
            `entity_type_id` = 4
            AND `attribute_code` IN ({$attributeSet})
        AND `attribute_id` NOT IN (
            SELECT
                    `attribute_id`
                FROM
                    {$superAttributesTable}
                WHERE
                    product_id IN (
            SELECT
                            sl.parent_id
                        FROM
                            {$superLinkTable} as sl
                        LEFT JOIN catalog_product_entity as cpe on
                            cpe.entity_id = sl.product_id
                        WHERE
                            sl.product_id in (
            SELECT
                                    c.entity_id
                                FROM
                                    flow_from_eavs AS f
                                LEFT JOIN catalog_product_entity as c ON
                                    c.sku = f.e
                                    AND c.type_id = 'simple'
                                    AND f.a IN ({$attributeSet})
                                WHERE
                                    f.meta_file = ?
                                    AND f.meta_processed = ?
                            )
                        GROUP BY
                            sl.parent_id
                    )
            )", [$inboundflow->getId(), Inboundflow::STATUS_PROCESSED]);
    }

    /**
     * @param array $attributesToCopy array of array  containing [ 'attribute_id' => ..., 'attribute_code' => ...., 'backend_type' => ....]
     * @param array $lookupAttributeTable
     * @param $configurableRowId
     * @param $simpleRowId
     */
    protected function copyAttrybutesToConfigurableByRowId(array $attributesToCopy, array $lookupAttributeTable, $configurableRowId, $simpleRowId)
    {
        /* TODO: to optimize -> group attributes by backend_type and perform a mass update */
        foreach ($attributesToCopy as $attributeInfo) {
            $backendType = $attributeInfo['backend_type'];

            if ($backendType === 'static') {
                // TODO :
                continue;
            }

            $table = $lookupAttributeTable[$backendType];

            $sql = "INSERT INTO {$table} ( attribute_id, store_id, `value`, row_id) SELECT `attribute_id`, `store_id`, `value`, {$configurableRowId} AS row_id FROM {$table} WHERE row_id = {$simpleRowId} AND attribute_id = {$attributeInfo['attribute_id']} ON DUPLICATE KEY UPDATE `value` = VALUES(`value`)";

            $this->getConnection()->query($sql);
        }
    }
}
