<?php

namespace PortalPeople\redsys\models;

use Yii;

/**
 * @property string $DS_MERCHANT_ORDER
 * @property string $DS_MERCHANT_MERCHANTCODE
 * @property string $DS_MERCHANT_TERMINAL
 * @property string $DS_MERCHANT_CURRENCY
 * @property string $DS_MERCHANT_TRANSACTIONTYPE
 * @property string $DS_MERCHANT_AMOUNT
 * @property string $DS_MERCHANT_IDENTIFIER
 * @property string $DS_MERCHANT_COF_INI
 * @property string $DS_MERCHANT_DIRECTPAYMENT
 * @property string $DS_MERCHANT_EXCEP_SCA
 * @property string $DS_MERCHANT_MERCHANTURL
 * @property string $DS_MERCHANT_URLOK
 * @property string $DS_MERCHANT_URLKO
 */
class Redsys extends \common\models\DynamicModel
{
    public $merchantSecretKey;

    const restSandboxUrl = 'https://sis-t.redsys.es:25443/sis/rest/trataPeticionREST';
    const restProductionUrl = 'https://sis.redsys.es/sis/rest/trataPeticionREST';

    const hppSandboxUrl = 'https://sis-t.redsys.es:25443/sis/realizarPago';
    const hppProductionUrl = 'https://sis.redsys.es/sis/realizarPago';

    const SANDBOX_URL = self::restSandboxUrl;
    const PRODUCTION_URL = self::restProductionUrl;
    const sandboxUrl = self::restSandboxUrl;
    const productionUrl = self::restProductionUrl;

    const Ds_SignatureVersion = 'HMAC_SHA512_V2';
    const HMAC_KEY = 'sq7HjrUOBfKmC576ILgskD5srU870gJ7';

    public static function getSecretKey(): string
    {
        $settings = Yii::$app->params['redsys'] ?? (Yii::$app->params['paygate'] ?? []);
        $secretKey = $settings['secret_key'] ?? '';

        if (
            is_string($secretKey)
            && $secretKey !== ''
            && class_exists('\\backend\\components\\Security')
            && method_exists('\\backend\\components\\Security', 'decryptSensitiveData')
        ) {
            $secretKey = \backend\components\Security::decryptSensitiveData($secretKey);
        }

        return (string) $secretKey;
    }

    public static function localAttributes()
    {
        return [
            'DS_MERCHANT_ORDER' => [
                'rules' => ['string'],
                'label' => '',
                'input' => 'default',
            ],
            'DS_MERCHANT_MERCHANTCODE' => [
                'rules' => ['string'],
                'label' => '',
                'input' => 'default',
            ],
            'DS_MERCHANT_TERMINAL' => [
                'rules' => ['string'],
                'label' => '',
                'input' => 'default',
            ],
            'DS_MERCHANT_CURRENCY' => [
                'rules' => ['string'],
                'label' => '',
                'input' => 'default',
            ],
            'DS_MERCHANT_TRANSACTIONTYPE' => [
                'rules' => ['string'],
                'label' => '',
                'input' => 'default',
            ],
            'DS_MERCHANT_AMOUNT' => [
                'rules' => ['string'],
                'label' => '',
                'input' => 'default',
            ],
            'DS_MERCHANT_IDENTIFIER' => [
                'rules' => ['string'],
                'label' => '',
                'input' => 'default',
            ],
            'DS_MERCHANT_COF_INI' => [
                'rules' => ['string'],
                'label' => '',
                'input' => 'default',
            ],
            'DS_MERCHANT_DIRECTPAYMENT' => [
                'rules' => ['string'],
                'label' => '',
                'input' => 'default',
            ],
            'DS_MERCHANT_EXCEP_SCA' => [
                'rules' => ['string'],
                'label' => '',
                'input' => 'default',
            ],
            'DS_MERCHANT_MERCHANTURL' => [
                'rules' => ['string'],
                'label' => '',
                'input' => 'default',
            ],
            'DS_MERCHANT_URLOK' => [
                'rules' => ['string'],
                'label' => '',
                'input' => 'default',
            ],
            'DS_MERCHANT_URLKO' => [
                'rules' => ['string'],
                'label' => '',
                'input' => 'default',
            ],
        ];
    }

    public function getDsSignatureVersion()
    {
        return self::Ds_SignatureVersion;
    }

    public function getDs_MerchantParameters()
    {
        $params = [
            'DS_MERCHANT_ORDER' => $this->DS_MERCHANT_ORDER,
            'DS_MERCHANT_MERCHANTCODE' => $this->DS_MERCHANT_MERCHANTCODE,
            'DS_MERCHANT_TERMINAL' => $this->DS_MERCHANT_TERMINAL,
            'DS_MERCHANT_CURRENCY' => $this->DS_MERCHANT_CURRENCY,
            'DS_MERCHANT_TRANSACTIONTYPE' => $this->DS_MERCHANT_TRANSACTIONTYPE,
            'DS_MERCHANT_AMOUNT' => $this->DS_MERCHANT_AMOUNT,
        ];

        if (!empty($this->DS_MERCHANT_IDENTIFIER)) {
            $params['DS_MERCHANT_IDENTIFIER'] = $this->DS_MERCHANT_IDENTIFIER;
        }
        if (!empty($this->DS_MERCHANT_COF_INI)) {
            $params['DS_MERCHANT_COF_INI'] = $this->DS_MERCHANT_COF_INI;
        }
        if (!empty($this->DS_MERCHANT_DIRECTPAYMENT)) {
            $params['DS_MERCHANT_DIRECTPAYMENT'] = $this->DS_MERCHANT_DIRECTPAYMENT;
        }
        if (!empty($this->DS_MERCHANT_EXCEP_SCA)) {
            $params['DS_MERCHANT_EXCEP_SCA'] = $this->DS_MERCHANT_EXCEP_SCA;
        }
        if (!empty($this->DS_MERCHANT_MERCHANTURL)) {
            $params['DS_MERCHANT_MERCHANTURL'] = $this->DS_MERCHANT_MERCHANTURL;
        }
        if (!empty($this->DS_MERCHANT_URLOK)) {
            $params['DS_MERCHANT_URLOK'] = $this->DS_MERCHANT_URLOK;
        }
        if (!empty($this->DS_MERCHANT_URLKO)) {
            $params['DS_MERCHANT_URLKO'] = $this->DS_MERCHANT_URLKO;
        }

        return $params;
    }

    public function getDs_Signature()
    {
        $order = $this->DS_MERCHANT_ORDER;
        $params = self::base64_url_encode_safe(json_encode($this->getDs_MerchantParameters()));

        return self::createMerchantSignature($this->getSigningKey(), $params, $order);
    }

    public function getPayload()
    {
        $order = $this->DS_MERCHANT_ORDER;
        $params = self::base64_url_encode_safe(json_encode($this->getDs_MerchantParameters()));
        $signature = self::createMerchantSignature($this->getSigningKey(), $params, $order);

        return json_encode([
            'Ds_SignatureVersion' => $this->getDsSignatureVersion(),
            'Ds_MerchantParameters' => $params,
            'Ds_Signature' => $signature,
        ]);
    }

    public function getHppPayload($production = false)
    {
        return [
            'Ds_SignatureVersion' => $this->getDsSignatureVersion(),
            'Ds_MerchantParameters' => self::base64_url_encode_safe(json_encode($this->getDs_MerchantParameters())),
            'Ds_Signature' => $this->getDs_Signature(),
            'endpoint' => $production ? self::hppProductionUrl : self::hppSandboxUrl,
        ];
    }

    public function getHppForm($production = false)
    {
        $payload = $this->getHppPayload($production);
        $endpoint = $payload['endpoint'];

        $html = '<form name="redsysForm" action="' . htmlspecialchars($endpoint, ENT_QUOTES, 'UTF-8') . '" method="POST">' . PHP_EOL;
        $html .= '  <input type="hidden" name="Ds_SignatureVersion" value="' . htmlspecialchars($payload['Ds_SignatureVersion'], ENT_QUOTES, 'UTF-8') . '" />' . PHP_EOL;
        $html .= '  <input type="hidden" name="Ds_MerchantParameters" value="' . htmlspecialchars($payload['Ds_MerchantParameters'], ENT_QUOTES, 'UTF-8') . '" />' . PHP_EOL;
        $html .= '  <input type="hidden" name="Ds_Signature" value="' . htmlspecialchars($payload['Ds_Signature'], ENT_QUOTES, 'UTF-8') . '" />' . PHP_EOL;
        $html .= '  <button type="submit">Proceed to Payment</button>' . PHP_EOL;
        $html .= '</form>' . PHP_EOL;

        return $html;
    }

    public function getRestUrl($production = false)
    {
        return $production ? self::restProductionUrl : self::restSandboxUrl;
    }

    public function getHppUrl($production = false)
    {
        return $production ? self::hppProductionUrl : self::hppSandboxUrl;
    }

    protected function getSigningKey()
    {
        if (!empty($this->merchantSecretKey)) {
            return $this->merchantSecretKey;
        }

        $secretKey = self::getSecretKey();

        return $secretKey !== '' ? $secretKey : self::HMAC_KEY;
    }

    static function encrypt_AES($data, $key)
    {
        $fixedKey = str_pad(substr($key, 0, 16), 16, '0');

        return base64_encode(openssl_encrypt($data, 'aes-128-cbc', $fixedKey, OPENSSL_RAW_DATA, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"));
    }

    static function mac512($data, $key)
    {
        return hash_hmac('sha512', $data, $key, true);
    }

    static function createMerchantSignature($key, $data, $diversifyingFactor)
    {
        $diversifiedKey = self::encrypt_AES($diversifyingFactor, $key);
        $signature = self::mac512($data, $diversifiedKey);

        return self::base64_url_encode_safe($signature);
    }

    static function checkSignatures($sig1, $sig2)
    {
        if (strcasecmp($sig1, $sig2) !== 0) {
            throw new \Exception('Integrity failure. The received signature does not match the calculated one.');
        }
    }

    static function base64_url_encode($input)
    {
        return strtr(base64_encode($input), '+/', '-_');
    }

    static function base64_url_decode($input)
    {
        return base64_decode(strtr($input, '-_', '+/'));
    }

    static function base64_url_encode_safe($input)
    {
        return str_replace('=', '', strtr(base64_encode($input), '+/', '-_'));
    }

    static function base64_url_decode_safe($input)
    {
        $padded = str_pad($input, strlen($input) + (4 - strlen($input) % 4) % 4, '=', STR_PAD_RIGHT);

        return base64_decode(strtr($padded, '-_', '+/'));
    }
}