<?php

/**
 * Created by PhpStorm.
 * User: jderosa
 * Date: 21/04/20
 * Time: 11.43
 */

namespace FiloBlu\Rma\Handlers;

use Exception;
use FiloBlu\Rma\Api\Model\Config\Reader;
use FiloBlu\Rma\Api\RmaExtensionAttributesRepositoryInterface;
use FiloBlu\Rma\Helper\ChangeSizeAttributeHelper;
use FiloBlu\Rma\Helper\RmaHelper;
use FiloBlu\Rma\Helper\SerializerHelper;
use FiloBlu\Rma\Logger\Logger;
use FiloBlu\Rma\Model\Payment\ChangeSizePay;
use FiloBlu\Rma\Model\Rma\Status\HistoryFactory;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\DB\Transaction;
use Magento\Framework\DB\TransactionFactory;
use Magento\Framework\Event\ManagerInterface as EventManager;
use Magento\Framework\Exception\LocalizedException;
use Magento\Rma\Api\Data\ItemInterface;
use Magento\Rma\Model\Rma;
use Magento\Rma\Model\Rma\Source\Status;
use Magento\Sales\Api\Data\OrderInterface;
use Magento\Sales\Api\OrderItemRepositoryInterface;
use Magento\Sales\Api\OrderRepositoryInterface;
use Magento\Sales\Model\Order;
use Magento\Sales\Model\Order\Invoice;
use Magento\Sales\Model\Service\InvoiceService;
use Magento\Store\Model\ScopeInterface;
use Throwable;

use function in_array;

/**
 *
 */
class RmaItemStatusUpdateHandler
{
    /** @var string */
    const XML_PATH_RECEIVED_EMAIL_ENABLE = 'sales_email/magento_rma_receive/enabled';

    /** @var string */
    const XML_PATH_RMA_REASON_MAPPING = 'sales/rma_reason/reason';

    /** @var string */
    const XML_PATH_RMA_AUTO_AUTHORIZED = 'filoblu_disablerma_section/rma_group/rma_auto_authorized';
    /**
     * @var OrderItemInterface
     */
    protected $orderItemInterface;
    /**
     * @var OrderItemRepositoryInterface
     */
    protected $orderItemRepository;

    /**
     * @var RmaExtensionAttributesRepositoryInterface
     */
    private $rmaExtensionAttributesRepository;

    /**
     * @var OrderRepositoryInterface
     */
    private $orderRepository;

    /**
     * @var ChangeSizeAttributeHelper
     */
    private $changeSizeAttributeHelper;

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

    /**
     * @var Reader
     */
    private $reader;

    /**
     * @var SerializerHelper
     */
    private $serializer;

    /**
     * @var HistoryFactory
     */
    private $historyFactory;

    /**
     * @var EventManager
     */
    private $eventManager;

    /**
     * @var Logger
     */
    private $logger;

    /**
     * @var InvoiceService
     */
    private $invoiceService;

    /**
     * @var TransactionFactory
     */
    private $transactionFactory;
    /**
     * @var RmaHelper
     */
    private $rmaHelper;

    /**
     * RmaItemStatusUpdateHandler constructor.
     * @param RmaExtensionAttributesRepositoryInterface $rmaExtensionAttributesRepository
     * @param OrderRepositoryInterface $orderRepository
     * @param ChangeSizeAttributeHelper $changeSizeAttributeHelper
     * @param ScopeConfigInterface $scopeConfig
     * @param Reader $reader
     * @param SerializerHelper $serializer
     * @param HistoryFactory $historyFactory
     * @param EventManager $eventManager
     * @param Logger $logger
     * @param InvoiceService $invoiceService
     * @param TransactionFactory $transactionFactory
     * @param OrderItemRepositoryInterface $orderItemRepository
     * @param RmaHelper $rmaHelper
     */
    public function __construct(
        RmaExtensionAttributesRepositoryInterface $rmaExtensionAttributesRepository,
        OrderRepositoryInterface $orderRepository,
        ChangeSizeAttributeHelper $changeSizeAttributeHelper,
        ScopeConfigInterface $scopeConfig,
        Reader $reader,
        SerializerHelper $serializer,
        HistoryFactory $historyFactory,
        EventManager $eventManager,
        Logger $logger,
        InvoiceService $invoiceService,
        TransactionFactory $transactionFactory,
        OrderItemRepositoryInterface $orderItemRepository,
        RmaHelper $rmaHelper
    ) {
        $this->rmaExtensionAttributesRepository = $rmaExtensionAttributesRepository;
        $this->orderRepository = $orderRepository;
        $this->changeSizeAttributeHelper = $changeSizeAttributeHelper;
        $this->scopeConfig = $scopeConfig;
        $this->reader = $reader;
        $this->serializer = $serializer;
        $this->historyFactory = $historyFactory;
        $this->eventManager = $eventManager;
        $this->logger = $logger;
        $this->invoiceService = $invoiceService;
        $this->transactionFactory = $transactionFactory;
        $this->orderItemRepository = $orderItemRepository;
        $this->rmaHelper = $rmaHelper;
    }

    /**
     * @param Rma $subject
     * @param $result
     * @return Rma
     */
    public function shouldUpdateStatus(Rma $subject, $result)
    {
        $originalSubject = clone $subject;

        //Set new status based on setting
        //$this->setNewRmaStatus($result);

        try {
            if (
                in_array(
                    $result->getStatus(),
                    [
                    Status::STATE_APPROVED_ON_ITEM,
                    Status::STATE_PROCESSED_CLOSED,
                    Status::STATE_DENIED,
                    Status::STATE_CLOSED,
                    Status::STATE_PARTIAL_AUTHORIZED
                    ]
                )
            ) {
                $resolutionChangeSize = $this->changeSizeAttributeHelper->getChangeSizeAttributeOptionId();

                foreach ($result->getItems() as $item) {
                    $rmaItemId = $item->getEntityId();
                    $changeSizeOrderId = $this->rmaExtensionAttributesRepository->getChangeSizeOrderId($rmaItemId);

                    if (!$changeSizeOrderId) {
                        continue;
                    }

                    $rmaId = $result->getEntityId();
                    $changeSizeOrder = $this->orderRepository->get($changeSizeOrderId);

                    if (!$changeSizeOrder->getPayment()) {
                        continue;
                    }

                    $payment = $changeSizeOrder->getPayment()->getMethod();
                    if ($payment !== ChangeSizePay::PAYMENT_CODE) {
                        continue;
                    }

                    if ($item->getResolution() == $resolutionChangeSize && $item->getStatus() == Status::STATE_APPROVED && !$changeSizeOrder->hasInvoices()) {
                        $this->createInvoice($changeSizeOrder);
                        $changeSizeOrder->setStatus('payment_confirmed')->setState('processing');
                        $changeSizeOrder->addStatusHistoryComment(
                            "Status and State updated programmatically by rma with id $rmaId update"
                        );
                        $changeSizeOrder->save();
                    }

                    if (in_array($item->getStatus(), [Status::STATE_REJECTED, Status::STATE_DENIED])) {
                        $changeSizeOrder = $this->orderRepository->get($changeSizeOrderId);
                        $changeSizeOrder->setForceUpdateStatus(true);
                        $changeSizeOrder->setStatus('canceled')->setState('canceled');
                        $changeSizeOrder->addStatusHistoryComment(
                            "Order canceled programmatically by rma with id $rmaId update"
                        );
                        $changeSizeOrder->save();
                    }
                }
            }

            if ($result->getStatus() == Status::STATE_PENDING) {
                if (
                    $this->scopeConfig->getValue(
                        self::XML_PATH_RMA_AUTO_AUTHORIZED,
                        ScopeInterface::SCOPE_STORE,
                        $subject->getStoreId()
                    )
                ) {
                    /** @var Rma $result */
                    $items = $result->getItems();
                    $standardReasonsConfig = $this->reader->read();
                    $automaticApproval = [];
                    foreach ($standardReasonsConfig as $reason) {
                        if ($reason['automatic_approval'] == 1) {
                            $automaticApproval[] = $reason['id'];
                        }
                    }
                    /** @var ItemInterface $item */
                    $auth = 0;

                    $newStatuses = $this->rmaHelper->getDisallowedRmaStatusNewStatus();

                    $itemsComments = '';
                    foreach ($items as $item) {
                        $itemBrandReason = $item->getReason();
                        $mapping = $this->serializer->unserialize(
                            $this->scopeConfig->getValue(
                                self::XML_PATH_RMA_REASON_MAPPING,
                                ScopeInterface::SCOPE_STORE,
                                $subject->getStoreId()
                            )
                        );

                        if (array_key_exists($itemBrandReason, $newStatuses)) {

                            $newRmaStatus = $newStatuses[$itemBrandReason];

                            if (!empty($newRmaStatus)) {
                                $result->setStatus($newRmaStatus);

                            }
                            continue;
                        }

                        foreach ($mapping as $reason) {
                            if (
                                $itemBrandReason == $reason['brand_reason'] && in_array(
                                    $reason['standard_reason'],
                                    $automaticApproval
                                )
                            ) {
                                // avanzamento dello stato dell'item
                                $item->setStatus(Status::STATE_AUTHORIZED);
                                $itemsComments .= ' -- ' . $item->getProductAdminName(
                                ) . ' automatically authorized -- ';
                                $item->setQtyAuthorized($item->getQtyRequested());
                                $item->save();
                                $auth++;
                                break;
                            }
                        }
                    }

                    if ($auth > 0 && $auth == count($items)) {
                        $result->setStatus(Status::STATE_AUTHORIZED);
                        // $this->eventManager->dispatch('filoblu_ext_rma_shouldupdatestatus_after', ['rma' => $result, 'result' => $result]);
                        $result->save();
                    } elseif ($auth > 0) {
                        $result->setStatus(Status::STATE_PARTIAL_AUTHORIZED)->save();
                    }

                    if ($auth > 0) {
                        $statusHistory = $this->historyFactory->create();
                        $statusHistory->setRmaEntityId($result->getEntityId());
                        $statusHistory->saveComment($itemsComments, false, true);
                    }
                }
            }

            $this->logger->warning(
                "RMA with id {$result->getEntityId()}  final status: {$result->getStatus()}. Original status : {$originalSubject->getStatus()}. Result original status: {$result->getOrigData('status')}"
            );
            $this->eventManager->dispatch(
                'filoblu_ext_rma_shouldupdatestatus_after',
                ['rma' => $result, 'result' => $result]
            );
            return $result;
        } catch (Exception $e) {
            $this->logger->critical($e->getMessage(), ['exception' => $e]);
            $this->eventManager->dispatch(
                'filoblu_ext_rma_shouldupdatestatus_after',
                ['rma' => $result, 'result' => $result]
            );
            return $result;
        } catch (Throwable $t) {
            $this->logger->critical($t->getMessage(), ['exception' => $e]);
            $this->eventManager->dispatch(
                'filoblu_ext_rma_shouldupdatestatus_after',
                ['rma' => $result, 'result' => $result]
            );
            return $result;
        }
    }

    /**
     * @param OrderInterface|Order $order
     * @return Invoice
     */
    protected function createInvoice(OrderInterface $order)
    {
        try {
            if (!$order->getId()) {
                throw new LocalizedException(__('The order no longer exists.'));
            }
            if (!$order->canInvoice()) {
                throw new LocalizedException(
                    __('The order does not allow an invoice to be created.')
                );
            }

            $invoice = $this->invoiceService->prepareInvoice($order);
            if (!$invoice) {
                throw new LocalizedException(__('We can\'t save the invoice right now.'));
            }
            if (!$invoice->getTotalQty()) {
                throw new LocalizedException(
                    __('You can\'t create an invoice without products.')
                );
            }
            $invoice->setRequestedCaptureCase(Invoice::CAPTURE_OFFLINE);
            $invoice->register();
            $invoice->getOrder()->setCustomerNoteNotify(false);
            $invoice->getOrder()->setIsInProcess(true);
            $order->addStatusHistoryComment('Invoice automatically create by Rma Update Status', false);

            /** @var Transaction $transaction */
            $transaction = $this->transactionFactory->create();
            $transaction->addObject($invoice);
            $transaction->addObject($invoice->getOrder());
            $transaction->save();
            return $invoice;
        } catch (Exception $e) {
            $this->logger->critical($e->getMessage(), ['exception' => $e]);
        } catch (Throwable $t) {
            $this->logger->critical($t->getMessage(), ['exception' => $t]);
        }

        return null;
    }

    protected function setNewRmaStatus($rma)
    {
        $newStatuses = $this->rmaHelper->getDisallowedRmaStatusNewStatus();

        foreach ($rma->getItems() as $item) {
            $reason = $item->getReason();

            if (array_key_exists($reason, $newStatuses)) {
                $rma->setStatus('pending');
                break;
            }
        }
    }
}
