<?php

namespace FiloBlu\Rma\Model\Order;

use FiloBlu\Rma\Helper\RefundParentOrderHelper;
use FiloBlu\Rma\Model\Payment\ChangeSizePay;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\Pricing\PriceCurrencyInterface;
use Magento\Sales\Api\CreditmemoManagementInterface as CreditmemoManager;
use Magento\Sales\Api\OrderRepositoryInterface;
use Magento\Sales\Model\Order\OrderStateResolverInterface;
use Magento\Sales\Model\Order\Payment\Operations\SaleOperation;
use Magento\Sales\Model\Order\Payment\Transaction;
use Magento\Sales\Model\Order\Payment\Transaction\ManagerInterface;

class Payment extends \Magento\Sales\Model\Order\Payment
{
    /**
     * @var OrderStateResolverInterface
     */
    private $orderStateResolver;

    /**
     * @var RefundParentOrderHelper
     */
    private $rmaRefundHelper;

    public function refund($creditmemo)
    {
        $baseAmountToRefund = $this->formatAmount($creditmemo->getBaseGrandTotal());
        $this->setTransactionId(
            $this->transactionManager->generateTransactionId($this, Transaction::TYPE_REFUND)
        );

        $isOnline = false;

        $order = $creditmemo->getOrder();
        $orderPayment = $order->getPayment();
        $isChangeSizePayOrderAndAllowedRefund = false;
        $rmaRefundHelper = $this->getRefundParentOrderHelper();
        if ($orderPayment->getMethod() == ChangeSizePay::PAYMENT_CODE && $rmaRefundHelper->isOnlinePaymentRefundForParentEnabled()
            && $creditmemo->getDoTransaction()) {
            $isOnline = true;
            $parentOrderId = $orderPayment->getAdditionalInformation('parent_order_id');
            $parentOrder = $this->orderRepository->get($parentOrderId);
            $parentOrderPayment = $parentOrder->getPayment();
            if ($rmaRefundHelper->isOrderAllowedToRefund($orderPayment, $parentOrderPayment)) {
                $isChangeSizePayOrderAndAllowedRefund = true;
                $gateway = $parentOrderPayment->getMethodInstance();
            }
        }

        $invoice = null;
        $this->getCreditmemo();

        if ($isChangeSizePayOrderAndAllowedRefund) {
            if ($gateway->canRefund()) {
                $this->setCreditmemo($creditmemo);
                $parentOrderPayment->setCreditmemo($creditmemo);

                // Get parent order invoice
                $invoiceCollection = $parentOrder->getInvoiceCollection();
                $parentOrderInvoice = $invoiceCollection->getFirstItem();

                // Get current order invoice
                $invoice = $creditmemo->getInvoice();
                if(is_null($invoice)) {
                    $invoice = $this->_order->getInvoiceCollection()->getFirstItem();
                }

                if ($invoice) {
                    $invoice->setTransactionId($parentOrderInvoice->getTransactionId());
                    if(!is_null($parentOrderPayment->getCcTransId())) {
                        $this->setCcTransId($parentOrderPayment->getCcTransId());
                    }

                    // Get capture transaction_id from parent order inovice
                    $parentLastTransId = $parentOrderInvoice->getTransactionId();
                    if (is_null($parentLastTransId)) {
                        throw new \Magento\Framework\Exception\LocalizedException(
                            __("Capture transaction Id missing. Cannot continue with online refund.")
                        );
                    }

                    switch ($parentOrderPayment->getMethod())
                    {
                        case 'filoblu_paypal_express_checkout':
                        case 'filoblu_paypal':
                            $ppCaptureId = $parentOrderPayment->getCcTransId() == $parentOrderPayment->getAdditionalInformation('pp_capture_id') ?
                                $parentOrderPayment->getCcTransId() :
                                $parentOrderPayment->getAdditionalInformation('pp_capture_id');
                            $this->setAdditionalInformation('pp_capture_id', $ppCaptureId);
                            break;
                        case 'amazon_payment_v2':
                        case 'amazon_payment_apb':
                        case 'paypal_express':
                        case 'satispay':
                            if (is_null($this->getParentTransactionId()))
                            {
                                $this->setParentTransactionId($parentLastTransId);
                            }
                            break;
                    }

                    if(!is_null($parentLastTransId)) {
                        $this->setLastTransId($parentLastTransId);
                    }

                    if(is_null($creditmemo->getInvoice())) {
                        $creditmemo->setInvoice($invoice);
                    }

                    $captureTxn = $this->transactionRepository->getByTransactionId(
                        $parentOrderInvoice->getTransactionId(),
                        $parentOrderPayment->getEntityId(),
                        $parentOrder->getEntityId()
                    );
                    if ($captureTxn) {
                        $this->setTransactionIdsForRefund($captureTxn);
                    }
                    $this->setShouldCloseParentTransaction(true);
                    // TODO: implement multiple refunds per capture
                    try {
                        $gateway->setStore(
                            $this->getOrder()->getStoreId()
                        );
                        $this->setRefundTransactionId($parentOrderInvoice->getTransactionId());
                        $gateway->refund($this, $baseAmountToRefund);

                        $creditmemo->setTransactionId($parentOrderPayment->getLastTransId());
                    } catch (\Magento\Framework\Exception\LocalizedException $e) {
                        if (!$captureTxn) {
                            throw new \Magento\Framework\Exception\LocalizedException(
                                __('If the invoice was created offline, try creating an offline credit memo.'),
                                $e
                            );
                        }
                        throw $e;
                    }
                }
            }

            // update self totals from creditmemo
            $this->_updateTotals(
                [
                    'amount_refunded' => $creditmemo->getGrandTotal(),
                    'base_amount_refunded' => $baseAmountToRefund,
                    'base_amount_refunded_online' => $isOnline ? $baseAmountToRefund : null,
                    'shipping_refunded' => $creditmemo->getShippingAmount(),
                    'base_shipping_refunded' => $creditmemo->getBaseShippingAmount(),
                ]
            );

            // update transactions and order state
            $transaction = $this->addTransaction(
                Transaction::TYPE_REFUND,
                $creditmemo,
                $isOnline
            );
            if ($invoice) {
                $message = __('We refunded %1 online.', $this->formatPrice($baseAmountToRefund));
            } else {
                $message = $this->hasMessage() ? $this->getMessage() : __(
                    'We refunded %1 offline.',
                    $this->formatPrice($baseAmountToRefund)
                );
            }
            $message = $this->prependMessage($message);
            $message = $this->_appendTransactionToMessage($transaction, $message);
            $orderState = $this->getOrderStateResolver()->getStateForOrder($this->getOrder());
            $statuses = $this->getOrder()->getConfig()->getStateStatuses($orderState, false);
            $status = in_array($this->getOrder()->getStatus(), $statuses, true)
                ? $this->getOrder()->getStatus()
                : $this->getOrder()->getConfig()->getStateDefaultStatus($orderState);
            $this->getOrder()
                ->addStatusHistoryComment(
                    $message,
                    $status
                )->setIsCustomerNotified($creditmemo->getOrder()->getCustomerNoteNotify());
            $this->_eventManager->dispatch(
                'sales_order_payment_refund',
                ['payment' => $this, 'creditmemo' => $creditmemo]
            );

            return $this;
        } else {
            return parent::refund($creditmemo);
        }
    }

    private function getOrderStateResolver()
    {
        if ($this->orderStateResolver === null) {
            $this->orderStateResolver = ObjectManager::getInstance()->get(OrderStateResolverInterface::class);
        }

        return $this->orderStateResolver;
    }

    private function getRefundParentOrderHelper()
    {
        if ($this->rmaRefundHelper === null) {
            $this->rmaRefundHelper = ObjectManager::getInstance()->get(RefundParentOrderHelper::class);
        }

        return $this->rmaRefundHelper;
    }

    /**
     * Function to get payment capture transaction (txn_id) from sales_payment_transaction if last_trans_id on sales_order_payment isn't capture
     *
     * @param string $parentLastTransId
     * @param $parentOrderId
     * @return string
     * @throws \Magento\Framework\Exception\InputException
     */
    private function checkLastTransId(string $parentLastTransId, $parentOrderId)
    {
        $parentLastTransIdData = explode("-", $parentLastTransId);
        if(end($parentLastTransIdData) != 'capture') {
            $parentLastTransId = $this->transactionRepository->getByTransactionType('capture', $parentOrderId);
            $parentLastTransId = $parentLastTransId->getTxnId();
        }
        return $parentLastTransId;
    }
}
