<?php

namespace Mtc\Plugins\Realex\Classes;

use GlobalPayments\Api\Entities\Address;
use GlobalPayments\Api\Entities\Enums\AddressType;
use GlobalPayments\Api\Entities\Enums\HppVersion;
use GlobalPayments\Api\Entities\Exceptions\ApiException;
use GlobalPayments\Api\Entities\HostedPaymentData;
use GlobalPayments\Api\HostedPaymentConfig;
use GlobalPayments\Api\ServiceConfigs\Gateways\GpEcomConfig;
use GlobalPayments\Api\ServicesContainer;
use GlobalPayments\Api\Services\HostedService;
use GlobalPayments\Api\Entities\Transaction;
use Illuminate\Support\Facades\App;
use \Illuminate\Support\Facades\DB;

/**
 * Class Realex
 *
 * Realex Payment Gateway
 *
 * @package Mtc\Plugins\Realex\Classes
 * @author Keith Stewart <keith.stewart@mtcmedia.co.uk>
 */
class Realex
{
    public $merchant_id;
    public $app_secret;
    public $sub_account;
    public $endpoint;
    public $return_url;


    public function __construct()
    {
        $this->sub_account = 'internet';
        $this->return_url = SITE_URL . route('realex-callback', [], false);

        if (DEV_MODE) {
            $this->merchant_id = REALEX_TEST_MERCHANT_ID;
            $this->app_secret = REALEX_TEST_APP_SECRET;
            $this->endpoint = REALEX_TEST_ENDPOINT;
            $this->capture_endpoint = REALEX_TEST_CAPTURE_ENDPOINT;
        } else {
            $this->merchant_id = REALEX_MERCHANT_ID;
            $this->app_secret = REALEX_APP_SECRET;
            $this->endpoint = REALEX_ENDPOINT;
            $this->capture_endpoint = REALEX_CAPTURE_ENDPOINT;
        }
    }


    public function form(\Basket $basket)
    {
        $r = $this->requestPaymentForm($basket);

        return App::make('twig')->render('Realex\form.twig', [
            'show_test_cards' => DEV_MODE,
            'order_id' => $basket->order_id,
            'jsonFromRequestEndpoint' => $r['json'],
            'endpoint' => $this->endpoint,
            'return_url' => "{$this->return_url}?order_id={$basket->order_id}",
        ]);
    }


    public function settleDelayedCaptureTransaction(OrderRealex $payment)
    {
        $output = [
            'success' => false,
            'response_code' => null,
            'response_message' => null,
        ];

        $config = new GpEcomConfig();
        $config->merchantId = $this->merchant_id;
        $config->sharedSecret = $this->app_secret;
        $config->serviceUrl = $this->capture_endpoint;

        ServicesContainer::configureService($config);

        $orderReference = \Mtc\Shop\Order::query()
            ->where('id', $payment->order_id)
            ->value('order_ref');

        if (empty($orderReference)) {
            $orderReference = OrderRealex::getPrefixedOrderRef($payment->order_id);
        }

        $transaction = Transaction::fromId($payment->tnx_id, $orderReference);
        try {
            $response = $transaction
                ->capture($payment->amount_paid)
                ->withCurrency("GBP")
                ->execute()
            ;
            RealexLog::saveResponse(json_encode($response), $payment->order_id, 'capture');

            $output['response_code'] = $response->responseCode;
            $output['response_message'] = $response->responseMessage;

            if ($response->responseCode === '00') {
                $output['success'] = true;
            }

        } catch (\Exception $e) {
            RealexLog::saveResponse($e->getMessage(), $payment->order_id, 'capture');
            $output['response_message'] = $e->getMessage();
        }

        return $output;
    }


    public function parseCallbackResponse($response, $order_id)
    {
        $output = [
            'success' => false,
            'order_id' => null,
            'response_code' => null,
            'transaction_id' => null,
        ];

        if (json_decode($response, true)) {
            $config = new GpEcomConfig();
            $config->merchantId = $this->merchant_id;
            $config->sharedSecret = $this->app_secret;
            $config->serviceUrl = $this->endpoint;

            $service = new HostedService($config);

            try {
                $parsed_response = $service->parseResponse($response, true);
                RealexLog::saveResponse(json_encode($parsed_response), $order_id, 'callback_parsed');

                $order_id = OrderRealex::getStrippedOrderRef($parsed_response->orderId);
                $response_code = $parsed_response->responseCode;

                $output['response_code'] = $response_code;
                $output['order_id'] = $order_id;
                $output['transaction_id'] = $parsed_response->transactionId;

                // 00 = Successful Transaction
                if ($response_code === '00') {
                    $output['success'] = true;
                    if (!empty($order_id)) {
                        \Mtc\Shop\Order::query()
                            ->where('id', (int)$order_id)
                            ->update(['order_ref' => $parsed_response->orderId]);
                    }
                }


            } catch (\Exception $e) {

                RealexLog::saveResponse($e->getMessage(), $order_id, 'callback');
            }
        }

        return $output;
    }


    private function requestPaymentForm($basket)
    {
        if (! $basket) {
            return [
                'status' => 'failed',
            ];
        }

        $amount = number_format($basket->cost_total, 2);

        try {
            $orderToken = bin2hex(random_bytes(4));
        } catch (\Exception $exception) {
            $orderToken = substr(md5(microtime(true)), -8);
        }

        $orderReference = OrderRealex::getPrefixedOrderRef($basket->order_id, $orderToken);

        \Mtc\Shop\Order::query()
            ->where('id', $basket->order_id)
            ->update(['order_ref' => $orderReference]);

        $config = new GpEcomConfig();
        $config->merchantId = $this->merchant_id;
        $config->sharedSecret = $this->app_secret;
        $config->account = $this->sub_account;
        $config->serviceUrl = $this->endpoint;

        $config->hostedPaymentConfig = new HostedPaymentConfig();
        $config->hostedPaymentConfig->version = HppVersion::VERSION_2;
        $service = new HostedService($config);

        $hostedPaymentData = new HostedPaymentData();
        $hostedPaymentData->customerEmail = $basket->info['email'];
        $hostedPaymentData->customerPhoneMobile = $basket->info['contact_no'];
        $hostedPaymentData->addressesMatch = false;

        $billingAddress = new Address();
        $billingAddress->streetAddress1 = $basket->address['billing']['address1'];
        $billingAddress->streetAddress2 = $basket->address['billing']['address2'];
        $billingAddress->city = $basket->address['billing']['city'];
        $billingAddress->postalCode = $basket->address['billing']['postcode'];
        $billingAddress->country = self::getIsoCode($basket->address['billing']['country']);

        $shippingAddress = new Address();
        $shippingAddress->streetAddress1 = $basket->address['shipping']['address1'];
        $shippingAddress->streetAddress2 = $basket->address['shipping']['address2'];
        $shippingAddress->city = $basket->address['shipping']['city'];
        $shippingAddress->postalCode = $basket->address['shipping']['postcode'];
        $shippingAddress->country = self::getIsoCode($basket->address['shipping']['country']);

        $json = '';
        try {
            $json = $service->authorize($amount)
                ->withCurrency("GBP")
                ->withOrderId($orderReference)
                ->withHostedPaymentData($hostedPaymentData)
                ->withAddress($billingAddress, AddressType::BILLING)
                ->withAddress($shippingAddress, AddressType::SHIPPING)
                ->serialize();


            RealexLog::saveResponse($json, $basket->order_id, 'form_create');

        } catch (\Exception $e) {

            RealexLog::saveResponse($e->getMessage(), $basket->order_id, 'form_create');

        }

        return [
            'status' => 'ok',
            'json' => $json,
        ];
    }


    private static function getIsoCode($country)
    {
        $iso_codes = [
            'AD' => '020',
            'AE' => '784',
            'AF' => '004',
            'AG' => '028',
            'AI' => '660',
            'AL' => '008',
            'AM' => '051',
            'AO' => '024',
            'AQ' => '010',
            'AR' => '032',
            'AS' => '016',
            'AT' => '040',
            'AU' => '036',
            'AW' => '533',
            'AX' => '248',
            'AZ' => '031',
            'BA' => '070',
            'BB' => '052',
            'BD' => '050',
            'BE' => '056',
            'BF' => '854',
            'BG' => '100',
            'BH' => '048',
            'BI' => '108',
            'BJ' => '204',
            'BL' => '652',
            'BM' => '060',
            'BN' => '096',
            'BO' => '068',
            'BQ' => '535',
            'BR' => '076',
            'BS' => '044',
            'BT' => '064',
            'BV' => '074',
            'BW' => '072',
            'BY' => '112',
            'BZ' => '084',
            'CA' => '124',
            'CC' => '166',
            'CD' => '180',
            'CF' => '140',
            'CG' => '178',
            'CH' => '756',
            'CI' => '384',
            'CK' => '184',
            'CL' => '152',
            'CM' => '120',
            'CN' => '156',
            'CO' => '170',
            'CR' => '188',
            'CU' => '192',
            'CV' => '132',
            'CW' => '531',
            'CX' => '162',
            'CY' => '196',
            'CZ' => '203',
            'DE' => '276',
            'DJ' => '262',
            'DK' => '208',
            'DM' => '212',
            'DO' => '214',
            'DZ' => '012',
            'EC' => '218',
            'EE' => '233',
            'EG' => '818',
            'EH' => '732',
            'ER' => '232',
            'ES' => '724',
            'ET' => '231',
            'FI' => '246',
            'FJ' => '242',
            'FK' => '238',
            'FM' => '583',
            'FO' => '234',
            'FR' => '250',
            'GA' => '266',
            'GB' => '826',
            'GD' => '308',
            'GE' => '268',
            'GF' => '254',
            'GG' => '831',
            'GH' => '288',
            'GI' => '292',
            'GL' => '304',
            'GM' => '270',
            'GN' => '324',
            'GP' => '312',
            'GQ' => '226',
            'GR' => '300',
            'GS' => '239',
            'GT' => '320',
            'GU' => '316',
            'GW' => '624',
            'GY' => '328',
            'HK' => '344',
            'HM' => '334',
            'HN' => '340',
            'HR' => '191',
            'HT' => '332',
            'HU' => '348',
            'ID' => '360',
            'IE' => '372',
            'IL' => '376',
            'IM' => '833',
            'IN' => '356',
            'IO' => '086',
            'IQ' => '368',
            'IR' => '364',
            'IS' => '352',
            'IT' => '380',
            'JE' => '832',
            'JM' => '388',
            'JO' => '400',
            'JP' => '392',
            'KE' => '404',
            'KG' => '417',
            'KH' => '116',
            'KI' => '296',
            'KM' => '174',
            'KN' => '659',
            'KP' => '408',
            'KR' => '410',
            'KW' => '414',
            'KY' => '136',
            'KZ' => '398',
            'LA' => '418',
            'LB' => '422',
            'LC' => '662',
            'LI' => '438',
            'LK' => '144',
            'LR' => '430',
            'LS' => '426',
            'LT' => '440',
            'LU' => '442',
            'LV' => '428',
            'LY' => '434',
            'MA' => '504',
            'MC' => '492',
            'MD' => '498',
            'ME' => '499',
            'MF' => '663',
            'MG' => '450',
            'MH' => '584',
            'MK' => '807',
            'ML' => '466',
            'MM' => '104',
            'MN' => '496',
            'MO' => '446',
            'MP' => '580',
            'MQ' => '474',
            'MR' => '478',
            'MS' => '500',
            'MT' => '470',
            'MU' => '480',
            'MV' => '462',
            'MW' => '454',
            'MX' => '484',
            'MY' => '458',
            'MZ' => '508',
            'NA' => '516',
            'NC' => '540',
            'NE' => '562',
            'NF' => '574',
            'NG' => '566',
            'NI' => '558',
            'NL' => '528',
            'NO' => '578',
            'NP' => '524',
            'NR' => '520',
            'NU' => '570',
            'NZ' => '554',
            'OM' => '512',
            'PA' => '591',
            'PE' => '604',
            'PF' => '258',
            'PG' => '598',
            'PH' => '608',
            'PK' => '586',
            'PL' => '616',
            'PM' => '666',
            'PN' => '612',
            'PR' => '630',
            'PS' => '275',
            'PT' => '620',
            'PW' => '585',
            'PY' => '600',
            'QA' => '634',
            'RE' => '638',
            'RO' => '642',
            'RS' => '688',
            'RU' => '643',
            'RW' => '646',
            'SA' => '682',
            'SB' => '090',
            'SC' => '690',
            'SD' => '729',
            'SE' => '752',
            'SG' => '702',
            'SH' => '654',
            'SI' => '705',
            'SJ' => '744',
            'SK' => '703',
            'SL' => '694',
            'SM' => '674',
            'SN' => '686',
            'SO' => '706',
            'SR' => '740',
            'SS' => '728',
            'ST' => '678',
            'SV' => '222',
            'SX' => '534',
            'SY' => '760',
            'SZ' => '748',
            'TC' => '796',
            'TD' => '148',
            'TF' => '260',
            'TG' => '768',
            'TH' => '764',
            'TJ' => '762',
            'TK' => '772',
            'TL' => '626',
            'TM' => '795',
            'TN' => '788',
            'TO' => '776',
            'TR' => '792',
            'TT' => '780',
            'TV' => '798',
            'TW' => '158',
            'TZ' => '834',
            'UA' => '804',
            'UG' => '800',
            'UM' => '581',
            'US' => '840',
            'UY' => '858',
            'UZ' => '860',
            'VA' => '336',
            'VC' => '670',
            'VE' => '862',
            'VG' => '092',
            'VI' => '850',
            'VN' => '704',
            'VU' => '548',
            'WF' => '876',
            'WS' => '882',
            'YE' => '887',
            'YT' => '175',
            'ZA' => '710',
            'ZM' => '894',
            'ZW' => '716',
        ];

        return isset($iso_codes[$country]) ? $iso_codes[$country] : '826';
    }
}
