<?php

namespace FiloBlu\Rma\Console;

use FiloBlu\Rma\Handlers\RmaItemStatusUpdateHandler;
use FiloBlu\Rma\Helper\ChangeSizeAttributeHelper;
use FiloBlu\Rma\Model\ChangeSize;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\ProductMetadataInterface;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\DataObject\KeyValueObjectInterface;
use Magento\Framework\Math\Random;
use Magento\Rma\Api\Data\ItemInterface;
use Magento\Rma\Api\Data\RmaInterface;
use Magento\Rma\Api\RmaAttributesManagementInterface;
use Magento\Rma\Model\RmaFactory;
use Magento\Sales\Api\Data\OrderInterface;
use Magento\Sales\Api\Data\OrderItemInterface;
use Magento\Sales\Api\OrderRepositoryInterface;
use Magento\Sales\Model\OrderFactory;
use Magento\SalesSequence\Model\Manager;
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;

use function json_encode;

/**
 *
 */
class RestoreOrderRma extends Command
{
    /**
     * @var ResourceConnection
     */
    protected $resourceConnection;
    /**
     * @var OrderRepositoryInterface
     */
    protected $orderRepository;
    /**
     * @var Manager
     */
    protected $sequenceManager;
    /**
     * @var RmaFactory
     */
    protected $rmaFactory;
    /**
     * @var ChangeSizeAttributeHelper
     */
    protected $changeSizeAttributeHelper;
    /**
     * @var ChangeSize
     */
    protected $changeSize;
    /**
     * @var RmaAttributesManagementInterface
     */
    protected $rmaAttributesManagement;
    /**
     * @var ScopeConfigInterface
     */
    protected $scopeConfig;

    /**
     * @var OrderFactory
     */
    protected $orderFactory;
    /**
     * @var ProductMetadataInterface
     */
    protected $productMetadata;

    /**
     * @param ResourceConnection $resourceConnection
     * @param OrderRepositoryInterface $orderRepository
     * @param Manager $sequenceManager
     * @param RmaFactory $rmaFactory
     * @param ChangeSizeAttributeHelper $changeSizeAttributeHelper
     * @param ChangeSize $changeSize
     * @param RmaAttributesManagementInterface $rmaAttributesManagement
     * @param ScopeConfigInterface $scopeConfig
     * @param OrderFactory $orderFactory
     * @param ProductMetadataInterface $productMetadata
     * @param string $name
     */
    public function __construct(
        ResourceConnection $resourceConnection,
        OrderRepositoryInterface $orderRepository,
        Manager $sequenceManager,
        RmaFactory $rmaFactory,
        ChangeSizeAttributeHelper $changeSizeAttributeHelper,
        ChangeSize $changeSize,
        RmaAttributesManagementInterface $rmaAttributesManagement,
        ScopeConfigInterface $scopeConfig,
        OrderFactory $orderFactory,
        ProductMetadataInterface $productMetadata,
        $name = null
    ) {
        parent::__construct($name);
        $this->resourceConnection = $resourceConnection;
        $this->orderRepository = $orderRepository;
        $this->sequenceManager = $sequenceManager;
        $this->rmaFactory = $rmaFactory;
        $this->changeSizeAttributeHelper = $changeSizeAttributeHelper;
        $this->changeSize = $changeSize;
        $this->rmaAttributesManagement = $rmaAttributesManagement;
        $this->scopeConfig = $scopeConfig;
        $this->orderFactory = $orderFactory;
        $this->productMetadata = $productMetadata;
    }

    /**
     *
     */
    protected function configure()
    {
        $this->setName('filoblu:rma:restore')
            ->setDescription('ChangeSize order increment ID')
            ->addArgument(
                'identifier',
                InputArgument::IS_ARRAY,
                'Order ID or increment ID'
            );

        $this->setDescription(
            'Run this code to restore missing rmas and sync them with corresponding order starting by a changesize order'
        );
        parent::configure();
    }

    /**
     * @param InputInterface $input
     * @param OutputInterface $output
     * @return int
     * @throws \Exception
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        if (version_compare($this->productMetadata->getVersion(), '2.3', '<')) {
            $output->writeln('This command can be used with magento >= 2.3 only');
            return 1;
        }

        $identifiers = $input->getArgument('identifier');

        foreach ($identifiers as $identifier) {
            $orderId = $this->orderFactory->create()->loadByIncrementId($identifier)->getEntityId();
            $order = $this->orderRepository->get($orderId);

            if (!$order->getId()) {
                $output->writeln('Order not found.');
                continue;
            }

            $output->write(
                sprintf(
                    'Processing order with increment_id #%s and entity_id %s ... ',
                    $order->getIncrementId(),
                    $order->getId()
                )
            );

            $restored = $this->restoreFromOrder($order);

            if (!$restored) {
                $output->writeln('is not a changesize order!');
                continue;
            }

            $output->writeln('done!');
        }

        return 0;
    }

    /**
     * @param \Magento\Sales\Api\Data\OrderInterface $order
     * @return bool
     * @throws \Exception
     */
    public function restoreFromOrder(OrderInterface $order)
    {
        if (!$order->getPayment() || $order->getPayment()->getMethod() != 'changesizepay') {
            return false;
        }

        $extensionAttributes = $order->getExtensionAttributes();
        $paymentAdditionalInfo = $extensionAttributes->getPaymentAdditionalInfo();

        $rmaId = (int)$this->getValue($paymentAdditionalInfo, 'rma_id');
        $rmaEntityId = (int)$this->getValue($paymentAdditionalInfo, 'rma_entity_id');
        $parentOrderId = (int)$this->getValue($paymentAdditionalInfo, 'parent_order_id');
        $parentOrderItemId = (int)$this->getValue($paymentAdditionalInfo, 'parent_order_item_id');

        $parentOrder = $this->orderRepository->get($parentOrderId);

        $orderItem = $this->getOrderItemById($parentOrder, $parentOrderItemId);

        $this->rmaExists($rmaId, $parentOrder);
        $this->rmaItemExists($rmaId, $rmaEntityId, $orderItem, $order->getStoreId());

        $rma = $this->rmaFactory->create()->load($rmaId);

        $resolutionId = $this->changeSizeAttributeHelper->getChangeSizeAttributeOptionId();

        $reasonId = $this->getReasonId();
        $conditionId = $this->getConditionId();

        $status = $this->getRmaStatus($order->getStoreId());

        /** @var ItemInterface $rmaItem */
        foreach ($rma->getItems() as $rmaItem) {
            if ((int)$rmaItem->getEntityId() !== $rmaEntityId) {
                continue;
            }

            $rmaItem->setStatus($status);
            $rmaItem->setResolution($resolutionId);
            $rmaItem->setReason($reasonId);
            $rmaItem->setCondition($conditionId);
            $rmaItem->save();
        }

        $rma->setStatus($status)->save();

        $this->addItemToRmaGrid($rma, $order->getCustomerName());

        return true;
    }

    /**
     * @param KeyValueObjectInterface[] $data
     * @param string $key
     * @return string|null
     */
    public function getValue(array $data, string $key)
    {
        foreach ($data as $value) {
            if ($value->getKey() == $key) {
                return $value->getValue();
            }
        }

        return null;
    }

    /**
     * @param OrderInterface $order
     * @param int $itemId
     * @return OrderItemInterface|null
     */
    public function getOrderItemById(OrderInterface $order, int $itemId)
    {
        foreach ($order->getItems() as $item) {
            if ((int)$item->getItemId() === $itemId) {
                return $item;
            }
        }

        return null;
    }

    public function rmaExists(int $rmaId, OrderInterface $order)
    {
        $table = $this->resourceConnection->getTableName('magento_rma');
        $connection = $this->resourceConnection->getConnection();
        $rma = $connection->fetchOne($connection->select()->from($table)->where('entity_id = ?', $rmaId));

        if (!$rma) {
            $connection->insert($table, [
                'entity_id'          => $rmaId,
                'status'             => 'pending',
                'increment_id'       => $this->getRmaIncrementId($order->getStoreId()),
                'order_id'           => $order->getEntityId(),
                'order_increment_id' => $order->getIncrementId(),
                'store_id'           => $order->getStoreId(),
                'customer_id'        => $order->getCustomerId(),
                'protect_code'       => $this->getProcectCode()
            ]);
        }
    }

    /**
     * @param $storeId
     * @return string
     * @throws \Magento\Framework\Exception\LocalizedException
     */
    public function getRmaIncrementId($storeId)
    {
        return $this->sequenceManager->getSequence('rma_item', $storeId)->getNextValue();
    }

    /**
     * @return false|string
     * @throws \Magento\Framework\Exception\LocalizedException
     */
    public function getProcectCode()
    {
        return substr(sha1(uniqid((string)Random::getRandomNumber(), true) . ':' . microtime(true)), 5, 6);
    }

    /**
     * @param int $rmaId
     * @param int $rmaEntityId
     * @param \Magento\Sales\Api\Data\OrderItemInterface $orderItem
     * @param $storeId
     * @return void
     */
    public function rmaItemExists(int $rmaId, int $rmaEntityId, OrderItemInterface $orderItem, $storeId)
    {
        $table = $this->resourceConnection->getTableName('magento_rma_item_entity');
        $connection = $this->resourceConnection->getConnection();
        $rma = $connection->fetchOne($connection->select('*')->from($table)->where('entity_id = ?', $rmaEntityId));

        if (!$rma) {
            $connection->insert($table, [
                'entity_id'          => $rmaEntityId,
                'rma_entity_id'      => $rmaId,
                'status'             => 'pending',
                'order_item_id'      => $orderItem->getItemId(),
                'product_name'       => $orderItem->getName(),
                'qty_authorized'     => 0,
                'qty_requested'      => 1,
                'qty_returned'       => 0,
                'product_sku'        => $orderItem->getSku(),
                'product_admin_name' => $orderItem->getName(),
                'product_admin_sku'  => $orderItem->getSku(),
                'product_options'    => json_encode($orderItem->getProductOptions())
            ]);
        }
    }

    /**
     * @return mixed|null
     */
    public function getReasonId()
    {
        $reasons = $this->changeSize->getAllowedReasons();

        foreach ($reasons as $reason) {
            if ($reason['label'] == 'Taglia errata-Articolo è troppo grande') {
                return $reason['value'];
            }
        }

        return null;
    }

    /**
     * @return string|null
     */
    public function getConditionId()
    {
        $conditions = $this->rmaAttributesManagement->getAttributeMetadata('condition')->getOptions();

        foreach ($conditions as $condition) {
            if ($condition->getLabel() == 'Aperto') {
                return $condition->getValue();
            }
        }

        return null;
    }

    /**
     * @param $storeId
     * @return string
     */
    public function getRmaStatus($storeId)
    {
        if ($this->scopeConfig->getValue(
            RmaItemStatusUpdateHandler::XML_PATH_RMA_AUTO_AUTHORIZED,
            ScopeInterface::SCOPE_STORE,
            $storeId
        )
        ) {
            return 'authorized';
        }

        return 'pending';
    }

    /**
     * @param \Magento\Rma\Api\Data\RmaInterface $rma
     * @param string $customerName
     * @return void
     */
    public function addItemToRmaGrid(RmaInterface $rma, string $customerName)
    {
        $table = $this->resourceConnection->getTableName('magento_rma_grid');
        $connection = $this->resourceConnection->getConnection();

        $rmaGridItem = $connection->fetchOne(
            $connection->select('*')
                ->from($table)
                ->where('increment_id = ?', $rma->getIncrementId())
        );

        if (!$rmaGridItem) {
            $connection->insert($table, [
                'status'             => $rma->getStatus(),
                'increment_id'       => $rma->getIncrementId(),
                'order_id'           => $rma->getOrderId(),
                'order_increment_id' => $rma->getOrderIncrementId(),
                'store_id'           => $rma->getStoreId(),
                'customer_id'        => $rma->getCustomerId(),
                'customer_name'      => $customerName
            ]);
        }
    }
}
