<?php

namespace Sibs\Sibs\Controller\Payment;

use DateTime;
use Exception;
use Magento\Framework\App\Action\Action;
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;

abstract class SibsPayment extends Action
{
    /**
     * @var \Magento\Framework\App\Action\Context
     */
    protected $_context;

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

    /**
     * @var \Magento\Checkout\Model\Session
     */
    protected $_checkoutSession;

    /**
     * @var \Sibs\Sibs\Model\SibsOrder
     */
    protected $_sibsOrder;

    /**
     * @var \Magento\Framework\Controller\Result\JsonFactory
     */
    protected $_resultJsonFactory;

    /**
     * @var string
     */
    protected $_paymentMethod;

    /**
     * @var string
     */
    protected $_checkoutMethod;

    /**
     * @var array
     */
    protected $_transactionData;

    /**
     * @param \Magento\Framework\App\Action\Context $context
     * @param \Magento\Checkout\Model\Session $checkoutSession
     * @param \Sibs\Sibs\Helper\DataHelper $sibsHelper
     * @param \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory
     * @param string $paymentMethod
     * @param string $checkoutMethod
     */
    public function __construct(
        \Magento\Framework\App\Action\Context $context,
        \Magento\Checkout\Model\Session $checkoutSession,
        \Sibs\Sibs\Helper\DataHelper $sibsHelper,
        \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory,
        string $paymentMethod,
        string $checkoutMethod
    ) {
        parent::__construct($context);
        $this->_checkoutSession    = $checkoutSession;
        $this->_sibsHelper         = $sibsHelper;
        $this->_resultJsonFactory  = $resultJsonFactory;

        $orderId                   = $this->_checkoutSession->getLastRealOrder()->getEntityId();
        $this->_sibsOrder          = new SibsOrder($orderId);

        $this->_paymentMethod      = $paymentMethod;
        $this->_checkoutMethod     = $checkoutMethod;
        $this->_transactionData    = $this->transactionData();
    }

    /**
     * Execute SIBS Payment
     *
     * @return Magento\Framework\Controller\Result\Json
     */
    public function execute()
    {
        $result = $this->_resultJsonFactory->create();

        try {
            define('DATE_FORMAT', 'Y-m-d\TH:i:s.vP');

            list($transactionID, $formContext, $transactionSignature) = $this->getFormData();
            $this->_sibsOrder->checkout($this->_checkoutMethod, $transactionID, $formContext, $transactionSignature);

            $method = $this->_sibsOrder->getPaymentMethodCode();
            SibsLog::info('CHECKOUT - Method Code: ' . $method);

            $timezone = $this->_sibsHelper->getTimezone();
            date_default_timezone_set($timezone);

            //SPG form configuration properties
            $widgetURL  = $this->getWidgetURL($transactionID);
            $formConfig = $this->getFormConfig($method, $transactionID);
            $formStyle  = $this->getFormStyle();

            $this->remakeCart(false, false);

            return $result->setData([
                'fields' => [
                    'widgetURL'     => $widgetURL,
                    'formContext'   => $formContext,
                    'formConfig'    => $formConfig,
                    'formStyle'     => $formStyle,
                    'formSignature' => $transactionSignature,
                ],
            ]);
        } catch (Exception $e) {
            SibsLog::err('CHECKOUT - Error: ' . $e->getMessage());

            $this->remakeCart(false, true);

            return $result->setData([
                'code'    => 400,
                'message' => __('Error in store payment configuration') . ': ' . $e->getMessage(),
            ]);
        }
    }

    /**
     * Get Checkout Data
     *
     * @param array $paymentMethod
     * @param string $paymentType
     * @return array
     */
    protected function getCheckoutData(array $paymentMethod, string $paymentType)
    {
        $now = new DateTime();

        $terminalID = intval($this->_sibsHelper->getTerminalID());
        $moto       = boolval($this->_sibsHelper->getMoto());

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

        $shippingdata = $this->_sibsOrder->getShippingAddress()->getData();
        $billingdata  = $this->_sibsOrder->getBillingAddress()->getData();

        $customerName = ! empty($this->_sibsOrder->getCustomerName()) ?
            $this->_sibsOrder->getCustomerName() :
            $this->_sibsOrder->getBillingAddress()->getFirstname() . ' ' . $this->_sibsOrder->getBillingAddress()->getLastname();

        $url = $this->_sibsHelper->getBaseUrl();

        return [
            'merchant' => [
                'terminalId'             => $terminalID,
                'channel'                => 'web',
                'merchantTransactionId'  => $this->_sibsOrder->getIncrementId(),
                'transactionDescription' => $transactionDescription,
                'websiteAddress'         => $url,
                'shopURL'                => $url, // Deprecated
            ],
            'transaction' => [
                'transactionTimestamp' => $now->format(SibsConstants::DATE_FORMAT_API),
                'description'          => $transactionDescription,
                'moto'                 => $moto,
                'paymentType'          => $paymentType,
                'amount'               => [
                    'value'    => $this->getGrandTotal(),
                    'currency' => $this->_sibsOrder->getOrderCurrencyCode(),
                ],
                'paymentMethod' => $paymentMethod,
            ],
            'customer' => [
                'customerInfo' => [
                    'customerName'     => $customerName,
                    'customerLanguage' => $this->_sibsHelper->getStoreLanguage(),
                    'customerEmail'    => $this->_sibsOrder->getCustomerEmail(),
                    'shippingAddress'  => [
                        'street1'  => $shippingdata['street'],
                        'city'     => $shippingdata['city'],
                        'postcode' => $shippingdata['postcode'],
                        'country'  => $shippingdata['country_id'],
                    ],
                    'billingAddress' => [
                        'street1'  => $billingdata['street'],
                        'city'     => $billingdata['city'],
                        'postcode' => $billingdata['postcode'],
                        'country'  => $billingdata['country_id'],
                    ],
                ],
            ],
            'tokenisation' => [
                'tokenisationRequest' => [
                    'tokeniseCard' => false
                ],
                'paymentTokens' => []
            ],
            'info' => [
                'deviceInfo' => [
                    'applicationName'    => 'magento:' . $this->_sibsHelper->getMagentoVersion(),
                    'applicationVersion' => $this->_sibsHelper->getVersion(),
                ],
            ],
        ];
    }

    /**
     * Get Transaction Data
     *
     * @return array
     */
    abstract protected function transactionData();

    /**
     * Call SIBS form API
     *
     * @param array $data
     * @return mixed
     */
    protected function callFormAPI($data)
    {
        $url = $this->_sibsHelper->getUrlCheckout();

        $header = [
            'Content-Type: application/json',
            'Authorization: Bearer ' . $this->_sibsHelper->getToken(),
            'X-IBM-Client-ID: ' . $this->_sibsHelper->getClientID(),
        ];

        SibsLog::info('CHECKOUT - URL: ' . $url);
        SibsLog::info('CHECKOUT - Header: ' . SibsLog::prettify($header));

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

    protected function getGrandTotal()
    {
        return floatval(
            number_format(
                str_replace(
                    ', ',
                    '.',
                    $this->_sibsOrder->getGrandTotal()
                ),
                2,
                '.',
                ''
            )
        );
    }

    /**
     * Remake Cart
     *
     * @param bool $changePayment
     * @param bool $deleteOrder
     */
    protected function remakeCart(bool $changePayment = false, bool $deleteOrder = false)
    {
        SibsLog::info('CHECKOUT - Remake Cart');

        $registry = SibsDB::get('Magento\Framework\Registry');
        $registry->register('isSecureArea', 'true');

        $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
        $quote         = $objectManager->create('Magento\Quote\Model\QuoteFactory')->create()->load($this->_sibsOrder->get()->getQuoteId());

        if ($changePayment) {
            $quote->setReservedOrderId(null);
            $quote->removePayment();
        }

        $quote->setIsActive(true);
        $quote->save();

        // replace the quote to the checkout session (I guess this is the better way)
        $this->_checkoutSession->replaceQuote($quote);

        if ($deleteOrder) {
            $this->_sibsOrder->delete();
        }

        $registry->unregister('isSecureArea');

        SibsLog::info('CHECKOUT - Cart Remade');
    }

    /**
     * Validate SIBS Checkout Response
     *
     * @param array $responseArray
     * @return array
     */
    protected function validateResponse($responseArray)
    {
        if (empty($responseArray['transactionID'])) {
            throw new SibsException('Error-in-checkout');
        }

        if (empty($responseArray['formContext'])) {
            throw new SibsException('Error-in-formContext');
        }

        if (empty($responseArray['transactionSignature'])) {
            throw new SibsException('Error-in-transactionSignature');
        }

        return [
            $responseArray['transactionID'],
            $responseArray['formContext'],
            $responseArray['transactionSignature'],
        ];
    }

    /**
     * Get Widget URL
     *
     * @param string $transactionID
     * @return string
     */
    protected function getWidgetURL($transactionID)
    {
        return $this->_sibsHelper->getUrlCallback() . '/assets/js/widget.js?id=' . $transactionID;
    }

    /**
     * Get Form Config
     *
     * @param string $method
     * @param string $transactionID
     * @return string|false
     */
    protected function getFormConfig($method, $transactionID)
    {
        $formPaymentMethods = $this->_sibsHelper->sibs_get_payment_method_for_spg($method);

        $redirectUrl = ($this->_transactionData['merchant']['websiteAddress'] . 'sibs/payment/sibscallback') . (
            ($formPaymentMethods == SibsConstants::SIBS_API_METHOD_PAY_BY_LINK_KEVIN) ?
            ('?id=' . $transactionID) :
            ''
        );

        return json_encode([
            'paymentMethodList' => [
                $formPaymentMethods,
            ],
            'amount' => [
                'value'    => $this->getGrandTotal(),
                'currency' => $this->_sibsOrder->getOrderCurrencyCode(),
            ],
            'language'     => $this->_sibsHelper->getStoreLanguage(),
            'customerData' => '',
            'redirectUrl'  => $redirectUrl,
        ]);
    }

    /**
     * Get Form Style
     *
     * @return string|false
     */
    protected function getFormStyle()
    {
        return json_encode([]);
    }

    /**
     * Get Form Data
     *
     * @return array
     */
    protected function getFormData()
    {
        if ($this->hasToRemakeForm()) {
            SibsLog::info('CHECKOUT - Call form API');
            SibsLog::info('CHECKOUT - Information: ' . SibsLog::prettify($this->_transactionData));

            $responseArray = $this->callFormAPI(json_encode($this->_transactionData));
            SibsLog::info('CHECKOUT - Result: ' . SibsLog::prettify($responseArray));

            return $this->validateResponse($responseArray);
        }

        SibsLog::info('CHECKOUT - Form was already made');
        SibsLog::info('CHECKOUT - Information: ' . SibsLog::prettify($this->_transactionData));

        return [
            $this->_sibsOrder->getTransactionId(),
            $this->_sibsOrder->getFormContext(),
            $this->_sibsOrder->getTransactionSignature()
        ];
    }

    /**
     * Has to remake Form
     *
     * @return bool
     */
    protected function hasToRemakeForm()
    {
        if (empty($this->_sibsOrder->getTransactionId())) {
            return true;
        }

        if (empty($this->_sibsOrder->getFormContext())) {
            return true;
        }

        if (empty($this->_sibsOrder->getTransactionSignature())) {
            return true;
        }

        if (in_array($this->_sibsOrder->getStatus(), [SibsConstants::SIBS_STATUS_CANCELED])) {
            return true;
        }

        return false;
    }
}
