<?php

declare(strict_types=1);

namespace ColemanProjects\Taxi;

use GuzzleHttp\Client;
use phpseclib3\Crypt\AES;

class Opayo
{
    public const VPS_PROTOCOL = '4.00';
    public const SEND_EMAIL = 1;
    public const APPLY_AVS_CV2 = 0;
    public const APPLY_3D_SECURE = 1;
    public const TRANSACTION_TYPE = 'PAYMENT';
    public const VENDOR = 'blacktaxis';

    private $config;

    public function __construct($config)
    {
        $this->config = $config;

        if ($this->config['test']) {
            $this->config['OPAYO_URL'] = 'https://sandbox.opayo.eu.elavon.com/gateway/service/vspform-register.vsp';
            $this->config['OPAYO_KEY'] = 'EB9gh6P4BReRe8eK';
        } else {
            $this->config['OPAYO_URL'] = 'https://live.opayo.eu.elavon.com/gateway/service/vspform-register.vsp';
            $this->config['OPAYO_KEY'] = 'uNQv62uKsDc2gChn';
        }
    }

    private function generateCrypt($data): string
    {
        /**
         * Opayo require the fields to be built into a query string:
         *
         * Name1=Value1&Name2=Value2
         *
         * We cannot use http_build_query to do this as this function also
         * encodes the values, e.g. spaces are converted to %20.
         */
        $query = '';

        foreach ($data as $key => $value) {
            $query .= "$key=$value&";
        }

        // Remove the final &
        $query = rtrim($query, '&');

        $cipher = new AES('cbc');
        $cipher->setKey($this->config['OPAYO_KEY']);
        $cipher->setKeyLength(128);
        $cipher->setIV($this->config['OPAYO_KEY']);
        $cipher->enablePadding();
        $crypt = $cipher->encrypt($query);

        $crypt = strtoupper(bin2hex($crypt));

        // Opayo require the crypt to start with @
        $crypt = '@' . $crypt;

        return $crypt;
    }

    public function paymentRequest($data): ?string
    {
        $defaults = [
            'VPSProtocol' => self::VPS_PROTOCOL,
            'SendEMail' => self::SEND_EMAIL,
            'ApplyAVSCV2' => self::APPLY_AVS_CV2,
            'Apply3DSecure' => self::APPLY_3D_SECURE,
            'COFUsage' => 'FIRST',
            'InitiatedType' => 'CIT',
            'MITType' => 'UNSCHEDULED'
        ];

        $paymentData = array_merge($defaults, $data);

        // Generate crypt value
        $crypt = $this->generateCrypt($paymentData);

        $payload = [
            'VPSProtocol' => self::VPS_PROTOCOL,
            'TxType' => self::TRANSACTION_TYPE,
            'Vendor' => self::VENDOR,
            'Crypt' => $crypt,
        ];

        $client = new Client([
            'allow_redirects' => false,
            'cookies' => true
        ]);
        $response = $client->post(
            $this->config['OPAYO_URL'],
            [
                'form_params' => $payload
            ]
        );

        if ($response->getStatusCode() === 302 && $response->hasHeader('Location')) {
            return $response->getHeader('Location')[0];
        }

        return null;
    }
}
