<?php

namespace Sibs\Sibs\Controller\Adminhtml\Payments;

use Magento\Backend\App\Action;
use Magento\Framework\Controller\ResultFactory;
use Magento\Store\Model\ScopeInterface;
use Sibs\Sibs\DB\SibsDB;
use Sibs\Sibs\Model\Api\SibsApi;
use Sibs\Sibs\Model\SibsConstants;
use Sibs\Sibs\Model\SibsOrder;
use Sibs\Sibs\Utils\SibsException;
use Sibs\Sibs\Utils\SibsLog;
use Sibs\Sibs\Utils\SibsUtils;

class Refund extends Action
{
    protected $_sibsOrder;

    protected $_messageManager;

    /**
     * @var \Sibs\Sibs\Helper\DataHelper
     */
    protected $_paymentHelper;

    /**
     * @var \Magento\Sales\Model\OrderRepository
     */
    protected $_orderRepository;

    /**
     * @var \Magento\Store\Model\StoreManagerInterface
     */
    protected $_storeManager;

    /**
     * @var \Magento\Framework\Stdlib\DateTime\TimezoneInterface
     */
    protected $_timezone;

    public function __construct(
        \Magento\Backend\App\Action\Context $context,
        \Sibs\Sibs\Helper\DataHelper $paymentHelper,
        \Magento\Sales\Model\OrderRepository $orderRepository,
        \Magento\Store\Model\StoreManagerInterface $storeManager,
        \Magento\Framework\Stdlib\DateTime\TimezoneInterface $timezone
    ) {
        parent::__construct($context);
        $this->_messageManager   = $context->getMessageManager();

        $this->_paymentHelper    = $paymentHelper;
        $this->_orderRepository  = $orderRepository;
        $this->_storeManager     = $storeManager;
        $this->_timezone         = $timezone;
    }

    public function execute()
    {
        SibsLog::info('Refund');

        $orderId          = $this->_request->getParam('order_id');
        $this->_sibsOrder = new SibsOrder($orderId);

        try {
            $paymentMethod = $this->_sibsOrder->getPaymentMethodCode();
            $paymentType   = SibsConstants::SIBS_PAYMENT_TYPE_RFND;

            if (! in_array($paymentMethod, SibsConstants::SIBS_PAYMENTS_REFUND)) {
                throw new SibsException('It is not a SIBS payment method with refund recognition.');
            }

            $responseArray                                                  = $this->callRefundAPI();
            list($transactionID, $paymentStatus, $statusCode, $amountValue) = $this->getAndValidateResponse($responseArray);

            SibsLog::info('Refund Response' . SibsLog::prettify($responseArray));
            $transactionResult = SibsUtils::getTransactionResult($statusCode);

            list($orderState, $orderStatus) = $this->_paymentHelper->sibs_get_order_state_and_status($paymentStatus, $paymentType);
            $refundStatus                   = $this->_paymentHelper->sibs_get_capture_refund_status($paymentStatus);

            if ($transactionResult == SibsConstants::SIBS_NOK) {
                throw new SibsException('Unfortunately there was an error in the refund request.');
            }

            switch($refundStatus) {
                case SibsConstants::MAGENTO_STATE_COMPLETE:
                    $this->_sibsOrder->refund($orderState, $orderStatus, $refundStatus, $amountValue, $transactionID);

                    return $this->responseOrderFullRefunded($orderId);
                case SibsConstants::MAGENTO_STATE_HOLDED:
                    $this->_sibsOrder->updateRefundStatus($orderState, $orderStatus, $refundStatus, $transactionID);

                    $number_of_retries = ($paymentMethod == SibsConstants::SIBS_PAYMENT_METHOD_BLIK) ? $this->_paymentHelper->getBLIKNumOfRetries() : 5;
                    $seconds_delay     = ($paymentMethod == SibsConstants::SIBS_PAYMENT_METHOD_BLIK) ? $this->_paymentHelper->getBLIKRetryDelay() : 1;

                    SibsLog::info("number of retries: $number_of_retries");
                    SibsLog::info("seconds delay: $seconds_delay");

                    $refundedId = $this->_sibsOrder->getRefundedId();

                    if (! empty($refundedId)) {
                        for ($i = 0; $i < $number_of_retries; $i++) {
                            sleep($seconds_delay);

                            $table_name   = SibsDB::getTableName('sales_order');
                            $refundStatus = SibsDB::fetchOne("SELECT `sibs_refunded_status` FROM `$table_name` WHERE `sibs_refunded_id` = '$refundedId'");

                            SibsLog::info("sibs refundedId $refundedId");
                            SibsLog::info("sibs refundStatus $refundStatus");

                            if ($refundStatus == SibsConstants::MAGENTO_STATE_COMPLETE) {
                                $this->_sibsOrder->refund($orderState, $orderStatus, $refundStatus, $amountValue, $transactionID);

                                return $this->responseOrderFullRefunded($orderId);
                            }
                        }

                        SibsLog::info("Attempts: $i");
                    }

                    // no webhook has been received in the meanwhile
                    return $this->responseOrderCheckBackofficeRefund($orderId);
                case SibsConstants::MAGENTO_STATE_CANCELED:
                default:
                    $this->_sibsOrder->updateRefundStatus($orderState, $orderStatus, $refundStatus, $transactionID);

                    return $this->responseOrderErrorRefund($orderId);
            }
        } catch(SibsException $e) {
            $this->_sibsOrder->updateRefundStatus(
                SibsConstants::MAGENTO_STATE_PROCESSING,
                SibsConstants::SIBS_STATUS_PROCESSING,
                null,
                null
            );

            return $this->responseOrderErrorRefund($orderId/*, $e->getErrorMessage()*/);
        }
    }

    private function callRefundAPI()
    {
        $storeId  = $this->_storeManager->getStore();
        $timezone = $this->_timezone->getConfigTimezone(ScopeInterface::SCOPE_STORES, $storeId);
        date_default_timezone_set($timezone);

        $ref_id                    = ($this->_sibsOrder->getPaymentType() == SibsConstants::SIBS_PAYMENT_TYPE_AUTH) ?
            $this->_sibsOrder->getCaptureId() : $this->_sibsOrder->getReferenceId();
        $original_transaction_date = $this->_sibsOrder->getCreatedAt();

        SibsLog::info("ref_id: $ref_id");
        SibsLog::info("original_transaction_date: $original_transaction_date");

        $url = $this->_paymentHelper->getUrlRefund($ref_id);

        $header = [
            'Authorization: Bearer ' . $this->_paymentHelper->getToken(),
            'X-IBM-Client-ID: ' . $this->_paymentHelper->getClientID(),
        ];

        $terminalID = intval($this->_paymentHelper->getTerminalID());
        $grandTotal = floatval(number_format(str_replace(', ', '.', $this->_sibsOrder->getGrandTotal()), 2, '.', ''));

        $transactionDescription = 'Transaction for entity id ' .
            $this->_sibsOrder->getEntityId() . ' with order number ' .
            $this->_sibsOrder->getIncrementId() . ' and Terminal ID ' .
            $terminalID;

        $data = json_encode([
            'merchant' => [
                'terminalId'            => $terminalID,
                'channel'               => 'web',
                'merchantTransactionId' => $this->_sibsOrder->getIncrementId(),
            ],
            'transaction' => [
                'transactionTimestamp' => date('Y-m-d\TH:i:s.BP'),
                'description'          => $transactionDescription,
                'amount'               => [
                    'value'    => $grandTotal,
                    'currency' => $this->_sibsOrder->getOrderCurrencyCode(),
                ],
                'originalTransaction' => [
                    'id'       => $ref_id,
                    'datetime' => $original_transaction_date,
                ],
            ],
        ]);

        SibsLog::info('Refund Data: ' . SibsLog::prettify($data));

        return SibsApi::post($url, $data, $header);
    }

    private function getAndValidateResponse($response)
    {
        SibsLog::info('Refund Response: ' . SibsLog::prettify($response));

        if (empty($response['transactionID'])) {
            throw new SibsException('Missing [transactionID]');
        }

        if (empty($response['paymentStatus'])) {
            throw new SibsException('Missing [paymentStatus]');
        }

        if (empty($response['returnStatus']['statusCode'])) {
            throw new SibsException('Missing [returnStatus][statusCode]');
        }

        if (empty($response['amount']['value'])) {
            throw new SibsException('Missing [amount][value]');
        }

        return [
            $response['transactionID'], $response['paymentStatus'],
            $response['returnStatus']['statusCode'], $response['amount']['value'],
        ];
    }

    private function responseOrderFullRefunded($orderId)
    {
        $this->_messageManager->addSuccessMessage(__('Order Fully Refunded'));

        return $this->redirect($orderId);
    }

    private function responseOrderCheckBackofficeRefund($orderId)
    {
        $checkBackofficeMessage = __('Please check in Sibs Backoffice for refund process');

        $this->_sibsOrder->addComment($checkBackofficeMessage, false, 0);
        $this->_messageManager->addSuccessMessage($checkBackofficeMessage);

        return $this->redirect($orderId);
    }

    private function responseOrderErrorRefund($orderId, $errorMessage = null)
    {
        $errorMessage = ! empty($errorMessage) ? $errorMessage : __('Unfortunately, your attempt to refund the payment failed');

        $this->_sibsOrder->addComment($errorMessage, false, 0);
        $this->_messageManager->addErrorMessage($errorMessage);

        return $this->redirect($orderId);
    }

    private function redirect($orderId)
    {
        $resultErrorRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
        $resultErrorRedirect->setPath('sales/order/view', ['order_id' => $orderId]);

        return $resultErrorRedirect;
    }
}
