<?php

namespace FiloBlu\Esb\Setup;

use FiloBlu\Esb\Api\Data\QueueItemInterface;
use FiloBlu\Esb\Api\QueueItemSqlRepositoryInterface;
use FiloBlu\Esb\Model\QueueItem;
use Magento\Framework\Exception\AlreadyExistsException;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Framework\Setup\UpgradeDataInterface;
use Magento\Sales\Model\Order;
use Magento\Sales\Model\Order\StatusFactory;
use Magento\Sales\Model\ResourceModel\Order\Status\Collection as OrderStatusCollection;

/**
 * Class UpgradeData
 * @package FiloBlu\Esb\Setup
 */
class UpgradeData implements UpgradeDataInterface
{
    const STATE_HOLDED = 'holded';
    const STATE_NEW = 'new';
    const STATE_PAYMENT_REVIEW = 'payment_review';
    const STATE_PENDING_PAYMENT = 'pending_payment';
    const STATE_PROCESSING = 'processing';
    /**
     * @var StatusFactory
     */
    protected $statusFactory;
    /**
     * @var OrderStatusCollection
     */
    protected $orderStatusCollection;


    /**
     * @param \Magento\Sales\Model\Order\StatusFactory $statusFactory
     * @param \Magento\Sales\Model\ResourceModel\Order\Status\Collection $orderStatusCollection
     */
    public function __construct(
        StatusFactory $statusFactory,
        OrderStatusCollection $orderStatusCollection
    ) {
        $this->statusFactory = $statusFactory;
        $this->orderStatusCollection = $orderStatusCollection;
    }

    /**
     * {@inheritdoc}
     */
    public function upgrade(
        ModuleDataSetupInterface $setup,
        ModuleContextInterface $context
    ) {
        if (version_compare($context->getVersion(), '1.0.2') < 0) {
            $confirmedStatus = $this->statusFactory->create();
            $confirmedStatus->setData('status', 'confirmed');
            $confirmedStatus->setData('label', 'Confirmed');
            $confirmedStatus->save();
            $confirmedStatus->assignState(Order::STATE_PROCESSING, false, true);

            $orderExportedStatus = $this->statusFactory->create();
            $orderExportedStatus->setData('status', 'order_exported');
            $orderExportedStatus->setData('label', 'Order Exported');
            $orderExportedStatus->save();
            $orderExportedStatus->assignState(Order::STATE_PROCESSING, false, false);

            $paymentConfirmedStatus = $this->statusFactory->create();
            $paymentConfirmedStatus->setData('status', 'payment_confirmed');
            $paymentConfirmedStatus->setData('label', 'Payment Confirmed');
            $paymentConfirmedStatus->save();
            $paymentConfirmedStatus->assignState(Order::STATE_PROCESSING, false, true);

            $paymentExportedStatus = $this->statusFactory->create();
            $paymentExportedStatus->setData('status', 'payment_exported');
            $paymentExportedStatus->setData('label', 'Payment Exported');
            $paymentExportedStatus->save();
            $paymentExportedStatus->assignState(Order::STATE_PROCESSING, false, false);

            $invoicedStatus = $this->statusFactory->create();
            $invoicedStatus->setData('status', 'invoiced');
            $invoicedStatus->setData('label', 'Invoiced');
            $invoicedStatus->save();
            $invoicedStatus->assignState(Order::STATE_PROCESSING, false, true);

            $shippedStatus = $this->statusFactory->create();
            $shippedStatus->setData('status', 'shipped');
            $shippedStatus->setData('label', 'Shipped');
            $shippedStatus->save();
            $shippedStatus->assignState(Order::STATE_PROCESSING, false, true);
        }

        if (version_compare($context->getVersion(), '1.0.21') < 0) {
            $newStatus = $this->statusFactory->create();
            $newStatus->setData('status', 'sent_to_logistics');
            $newStatus->setData('label', 'Sent to Logistics');
            $newStatus->save();
            $newStatus->assignState(Order::STATE_PROCESSING, false, true);
        }

        if (version_compare($context->getVersion(), '1.1.11') < 0) {
            $newStatus = $this->statusFactory->create();
            $newStatus->setData('status', 'blocked');
            $newStatus->setData('label', 'Blocked');
            try {
                $newStatus->save();
            } catch (AlreadyExistsException $e) {
            }

            try {
                $newStatus->assignState(self::STATE_HOLDED, false, true);
            } catch (AlreadyExistsException $e) {
            }

            try {
                $newStatus->assignState(self::STATE_NEW, false, true);
            } catch (AlreadyExistsException $e) {
            }

            try {
                $newStatus->assignState(self::STATE_PAYMENT_REVIEW, false, true);
            } catch (AlreadyExistsException $e) {
            }

            try {
                $newStatus->assignState(self::STATE_PENDING_PAYMENT, false, true);
            } catch (AlreadyExistsException $e) {
            }

            try {
                $newStatus->assignState(self::STATE_PROCESSING, false, true);
            } catch (AlreadyExistsException $e) {
            }
        }

        if (version_compare($context->getVersion(), '2.0.0') < 0) {
            $this->addNewEsbOrderStatuses();
        }

        if (version_compare($context->getVersion(), '2.0.1') < 0) {
            $this->fixCanceledByExtStatuses();
        }

        if (version_compare($context->getVersion(), '2.0.3') < 0) {
            $this->updateMessageHash($setup);
        }
    }

    /**
     * @return void
     */
    private function addNewEsbOrderStatuses()
    {
        $this->createOrderStatusIfNotExists(
            'waiting_confirmation_by_ext',
            'Waiting Confirmation By Ext',
            Order::STATE_PROCESSING
        );
        $this->createOrderStatusIfNotExists(
            'payment_confirmed_on_ext',
            'Payment Confirmed On Ext',
            Order::STATE_PROCESSING
        );
        $this->createOrderStatusIfNotExists(
            'confirmed_by_ext',
            'Confirmed By Ext',
            Order::STATE_PROCESSING
        );
        $this->createOrderStatusIfNotExists(
            'canceled_by_ext',
            'Canceled By Ext',
            Order::STATE_CANCELED
        );
    }

    /**
     * @param $status
     * @param $label
     * @param $state
     * @return false|void
     */
    public function createOrderStatusIfNotExists($status, $label, $state)
    {
        $orderStatusesArray = $this->orderStatusCollection->getAllIds();

        if (\in_array($status, $orderStatusesArray, true)) {
            return false;
        }

        $newStatus = $this->statusFactory->create();
        $newStatus->setData('status', $status);
        $newStatus->setData('label', $label);
        $newStatus->save();
        $newStatus->assignState($state, false, true);
    }

    /**
     * @return void
     */
    private function fixCanceledByExtStatuses()
    {
        $statusToFix = 'canceled_by_ext';
        $label = 'Canceled By Ext';
        $states_to_add = ['new', 'processing'];

        foreach ($states_to_add as $stateToAdd) {
            $orderStatusesCollection = $this->orderStatusCollection->joinStates();
            $found = false;
            foreach ($orderStatusesCollection as $item) {
                $data = $item->getData();
                $status = $data['status'];
                $state = $data['state'];
                if ($status == $statusToFix && $state == $stateToAdd) {
                    $found = true;
                    break;
                }
            }
            if (!$found) {
                $newStatus = $this->statusFactory->create();
                $newStatus->setData('status', $statusToFix);
                $newStatus->setData('label', $label);
                $newStatus->save();
                $newStatus->assignState($stateToAdd, false, true);
            }
        }
    }

    /**
     * @param \Magento\Framework\Setup\ModuleDataSetupInterface $setup
     * @return void
     */
    private function updateMessageHash(ModuleDataSetupInterface $setup)
    {
        $table = $setup->getTable(QueueItemSqlRepositoryInterface::TABLE);

        $setup->getConnection()->update(
            $table,
            [
                QueueItemInterface::MESSAGE_HASH => new \Zend_Db_Expr(sprintf('SHA1(%s)', QueueItemInterface::MESSAGE))
            ]
        );
    }

}
