<?php
/**
 *                       ######
 *                       ######
 * ############    ####( ######  #####. ######  ############   ############
 * #############  #####( ######  #####. ######  #############  #############
 *        ######  #####( ######  #####. ######  #####  ######  #####  ######
 * ###### ######  #####( ######  #####. ######  #####  #####   #####  ######
 * ###### ######  #####( ######  #####. ######  #####          #####  ######
 * #############  #############  #############  #############  #####  ######
 *  ############   ############  #############   ############  #####  ######
 *                                      ######
 *                               #############
 *                               ############
 *
 * Adyen Payment module (https://www.adyen.com/)
 *
 * Copyright (c) 2015 Adyen BV (https://www.adyen.com/)
 * See LICENSE.txt for license details.
 *
 * Author: Adyen <magento@adyen.com>
 */

namespace Adyen\Payment\Controller\Process;

use Magento\Customer\Api\AccountManagementInterface;
use Magento\Customer\Api\CustomerRepositoryInterface;

class Result extends \Magento\Framework\App\Action\Action
{

    protected $_adyenHelper;

    protected $_orderFactory;

    protected $_order;

    /**
     * @var \Magento\Sales\Model\Order\Status\HistoryFactory
     */
    protected $_orderHistoryFactory;

    protected $_session;

    /**
     * @param \Magento\Framework\App\Action\Context $context
     * @param \Magento\Customer\Model\Session $customerSession
     * @param CustomerRepositoryInterface $customerRepository
     * @param AccountManagementInterface $accountManagement
     */
    public function __construct(
        \Magento\Framework\App\Action\Context $context,
        \Adyen\Payment\Helper\Data $adyenHelper,
        \Magento\Sales\Model\OrderFactory $orderFactory,
        \Magento\Sales\Model\Order\Status\HistoryFactory $orderHistoryFactory,
        \Magento\Checkout\Model\Session $session
    ) {
        $this->_adyenHelper = $adyenHelper;
        $this->_orderFactory = $orderFactory;
        $this->_orderHistoryFactory = $orderHistoryFactory;
        $this->_session = $session;
        parent::__construct($context);
    }

    public function execute()
    {
        $response = $this->getRequest()->getParams();
        $result = $this->validateResponse($response);

        if ($result) {
            $session = $this->_session;
            $session->getQuote()->setIsActive(false)->save();
            $this->_redirect('checkout/onepage/success');
        } else {
            $this->_cancel($response);
            $this->_redirect('checkout/cart');
        }
    }

    protected function _cancel($response)
    {
        $session = $this->_session;

        // restore the quote
        $session->restoreQuote();

        $order = $this->_order;

        $this->_adyenHelper->cancelOrder($order);

        if(isset($response['authResult']) && $response['authResult'] == \Adyen\Payment\Model\Notification::CANCELLED) {
            $this->messageManager->addError(__('You have cancelled the order. Please try again'));
        } else {
            $this->messageManager->addError(__('Your payment failed, Please try again later'));
        }

    }

    protected function validateResponse($response)
    {
        $result = true;

        $this->_debugData['Step1'] = 'Processing ResultUrl';
        $storeId = null;

        if (empty($response)) {
            $this->_debugData['error'] = 'Response is empty, please check your webserver that the result url accepts parameters';
            throw new \Magento\Framework\Exception\LocalizedException(__('Response is empty, please check your webserver that the result url accepts parameters'));
        }

        // Log the results in log file and adyen_debug table
        $this->_debugData['response'] = $response;


        // authenticate result url
        $authStatus = $this->_authenticate($response);
        if (!$authStatus) {
            throw new \Magento\Framework\Exception\LocalizedException(__('ResultUrl authentification failure'));
        }

        $incrementId = $response['merchantReference'];

        if($incrementId) {
            $order = $this->_getOrder($incrementId);
            if ($order->getId()) {

                $this->_eventManager->dispatch('adyen_payment_process_resulturl_before', [
                    'order' => $order,
                    'adyen_response' => $response
                ]);
                if (isset($response['handled'])) {
                    return;
                }

                // set StoreId for retrieving debug log setting
                $storeId = $order->getStoreId();

                // update the order
                $result = $this->_validateUpdateOrder($order, $response);

                $this->_eventManager->dispatch('adyen_payment_process_resulturl_after', [
                    'order' => $order,
                    'adyen_response' => $response
                ]);

            } else {
                throw new \Magento\Framework\Exception\LocalizedException(__('Order does not exists with increment_id: %s1', $incrementId));
            }
        } else {
            throw new \Magento\Framework\Exception\LocalizedException(__('Empty merchantReference'));
        }
        return $result;
    }

    /**
     * @param $order
     * @param $params
     */
    protected function _validateUpdateOrder($order, $response)
    {
        $result = false;

        $this->_debugData['Step2'] = 'Updating the order';

        $authResult = $response['authResult'];
        $paymentMethod = isset($response['paymentMethod']) ? trim($response['paymentMethod']) : '';
        $pspReference = isset($response['pspReference']) ? trim($response['pspReference']) : '';

        $type = 'Adyen Result URL response:';
        $comment = __('%1 <br /> authResult: %2 <br /> pspReference: %3 <br /> paymentMethod: %4', $type, $authResult, $pspReference, $paymentMethod);

        $history = $this->_orderHistoryFactory->create()
            //->setStatus($status)
            ->setComment($comment)
            ->setEntityName('order')
            ->setOrder($order)
        ;

        $history->save();

        // needed  becuase then we need to save $order objects
        $order->setAdyenResulturlEventCode($authResult);


        switch ($authResult) {

            case \Adyen\Payment\Model\Notification::AUTHORISED:
            case \Adyen\Payment\Model\Notification::PENDING:
                // do nothing wait for the notification
                $result = true;
                $this->_debugData['Step4'] = 'Do nothing wait for the notification';
                break;
            case \Adyen\Payment\Model\Notification::CANCELLED:
                $this->_debugData['Step4'] = 'Cancel or Hold the order';
                $result = false;
                break;
            case \Adyen\Payment\Model\Notification::REFUSED:
                // if refused there will be a AUTHORIZATION : FALSE notification send only exception is ideal
                $this->_debugData['Step4'] = 'Cancel or Hold the order';
                $result = false;
                break;
            case \Adyen\Payment\Model\Notification::ERROR:
                //attempt to hold/cancel
                $this->_debugData['Step4'] = 'Cancel or Hold the order';
                $result = false;
                break;
            default:
                $this->_debugData['error'] = 'This event is not supported: ' . $authResult;
                $result = false;
                break;
        }

        return $result;
    }


    /**
     * @desc Authenticate using sha1 Merchant signature
     * @see success Action during checkout
     * @param Varien_Object $response
     */
    protected function _authenticate($response) {

        $hmacKey = $this->_adyenHelper->getHmac();

        // do not include the merchantSig in the merchantSig calculation
        $merchantSigNotification = $response['merchantSig'];
        unset($response['merchantSig']);

        // Sort the array by key using SORT_STRING order
        ksort($response, SORT_STRING);

        // Generate the signing data string
        $signData = implode(":",array_map(array($this, 'escapeString'),array_merge(array_keys($response), array_values($response))));

        $merchantSig = base64_encode(hash_hmac('sha256',$signData,pack("H*" , $hmacKey),true));


        if (strcmp($merchantSig, $merchantSigNotification) === 0) {
            return true;
        }
        return false;
    }

    /*
   * @desc The character escape function is called from the array_map function in _signRequestParams
   * $param $val
   * return string
   */
    protected function escapeString($val)
    {
        return str_replace(':','\\:',str_replace('\\','\\\\',$val));
    }

    protected function _getOrder($incrementId)
    {
        if (!$this->_order) {
            $this->_order = $this->_orderFactory->create()->loadByIncrementId($incrementId);
        }
        return $this->_order;
    }

}