<?php
/**
 * 2018 Paysera
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Academic Free License (AFL 3.0)
 * that is bundled with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://opensource.org/licenses/afl-3.0.php
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@prestashop.com so we can send you a copy immediately.
 *
 * @author Paysera <plugins@paysera.com>
 * @copyright 2018 Paysera
 * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
 *  International Registered Trademark & Property of Paysera
 */

use Evp\Component\Money\Money;

if (!defined('_PS_VERSION_')) {
    exit;
}

class PayseraCallbackModuleFrontController extends ModuleFrontController
{
    public function postProcess()
    {
        if (!$this->module->active) {
            exit;
        }

        $projectID = Configuration::get('PAYSERA_GENERAL_PROJECT_ID');
        $projectPassword = Configuration::get('PAYSERA_GENERAL_SIGN_PASS');

        try {
            $response = WebToPay::validateAndParseData($_REQUEST, $projectID, $projectPassword);

            if (1 == $response['status']) {
                $order = new Order($response['orderid']);

                if ($this->checkPayment($order, $response)) {
                    $orderOldStateId = $order->getCurrentOrderState()->id;
                    $orderStateId = (int) Configuration::get('PAYSERA_ORDER_STATUS_PAID');

                    if ($orderOldStateId !== $orderStateId) {
                        $order->setCurrentState($orderStateId);
                    }

                    $this->module->triggerPayseraPaymentConfirmedHook($order);

                    exit('OK');
                }
            }
        } catch (Exception $e) {
            exit($e->getMessage());
        }
    }

    /**
     * @param Order $order
     * @param array $response
     * @return bool
     */
    public function checkPayment($order, $response)
    {
        return $this->checkCurrency($order, $response)
            && $this->checkAmount($order, $response);
    }

    /**
     * @param Order $order
     * @param array $response
     * @return bool
     */
    private function checkCurrency($order, $response)
    {
        $currencyObj = (array)(Currency::getCurrency($order->id_currency));
        $orderCurrency = !empty($currencyObj['iso_code'])
            ? $currencyObj['iso_code']
            : null;

        if ($response['currency'] !== $orderCurrency) {
            exit(
                array_key_exists('payamount', $response) && !$response['paycurrency'] !== $orderCurrency
                    ? $this->formatWrongCurrencyError($response['paycurrency'], $orderCurrency)
                    : $this->formatWrongCurrencyError($response['currency'], $orderCurrency)
            );
        }

        return true;
    }

    private function formatWrongCurrencyError($actualCurrency, $expectedCurrency)
    {
        return sprintf(
            'Wrong currency: %s, expected %s',
            $actualCurrency,
            $expectedCurrency
        );
    }

    /**
     * @param Order $order
     * @param array $response
     * @return bool
     */
    private function checkAmount($order, $response)
    {
        $money = new Money($order->getOrdersTotalPaid());
        $responseAmount = Money::createFromNoDelimiterAmount($response['amount'], null);
        $responsePaymentAmount = array_key_exists('payamount', $response)
            ? Money::createFromNoDelimiterAmount($response['payamount'], null)
            : null;

        if (!$money->isEqual($responseAmount)) {
            $expectedAmount = (string) round($order->getOrdersTotalPaid(), 2);

            exit(
                $responsePaymentAmount && !$money->isEqual($responsePaymentAmount)
                    ? $this->formatWrongAmountError(
                        $response['payamount'] / 100,
                        $expectedAmount
                    )
                    : $this->formatWrongAmountError(
                        $response['amount'] / 100,
                        $expectedAmount
                    )
            );
        }

        return true;
    }

    private function formatWrongAmountError($actualAmount, $expectedAmount)
    {
        return sprintf(
            'Wrong amount: %s, expected %s',
            $actualAmount,
            $expectedAmount
        );
    }
}
