<?php

namespace App\PaymentGateways;

use App\Interfaces\PaymentGatewayInterface;
use App\Models\Transaction;
use App\Services\General\CurlService;
use App\Services\General\CustomHttpClientService;

class OnlinePaymentGateway implements PaymentGatewayInterface
{

    /**
     * @param array $params
     * @return array
     * @throws \Exception
     */
    public function processPayment(array $params = []): array
    {
        $url = null;

        $request_data = $params['request_data'] ?? null;
        $session = $params['session'] ?? null;
        $order = $params['order'] ?? null;
        $order_payment = $params['order_payment'] ?? null;
        $token = $params['token'] ?? null;

        $transaction = Transaction::where('token', $token)->first();
        if (!$transaction) {
            return [
                'success' => false,
                'errors' => __('main.pay_problem'),
                'url' => $url,
            ];
        }else{
            $params['transaction'] = $transaction;
            return $this->getPaymentId($params);
        }
    } //end of method

    /**
     * user add his info in our website
     * @param array $params
     * @return array
     */
    private function getMerchantHostedPaymentId(array $params=[]): array
    {
        $success=true;$url='';
        $transaction = $params['transaction'] ?? null;
        $callBackUrl = route('online.callBack', [ 'platform' => $transaction->platform ]);
        $data = [
            "id" => config('services.payments.online.tranportal_id'),
            "password" => config('services.payments.online.tranportal_password'),
            "expYear" => $params['expiry_year'],
            "expMonth" => $params['expiry_month'],
            "member" => $params['card_holder'],
            "cvv2" => $params['cvv'],
            "cardNo" => str_replace('-', '', $params['card_number']),
            "cardType" => "C",
            "action" => "1",
            "currencyCode" => "682",
            "errorURL" => $callBackUrl,
            "responseURL" => $callBackUrl,
            "trackId" => $params['trackId'], //order_id or payment_tokens_id
            "amt" => round($params['amount'], 2),

        ];
        $data = json_encode($data, JSON_UNESCAPED_SLASHES);

        $wrappedData = $this->wrapData($data);

        $encData = [
            "id" => config('services.payments.online.tranportal_id'),
            "trandata" => $this->encrypt($wrappedData, config('services.payments.online.terminal_resource_key')),
            "errorURL" => $callBackUrl,
            "responseURL" => $callBackUrl,
        ];

        $wrappedData = $this->wrapData(json_encode($encData, JSON_UNESCAPED_SLASHES));

        $callCurlParams = array(
            'url' => config('services.payments.online.merchant_hosted_endpoint'),
            'method' => 'POST',
            'data' => $wrappedData,
            'headers' => array(
                'Accept: application/json',
                'Accept-Language: application/json',
                'Content-Type: application/json',
            ),
            'noEncodeData' => true,
        );
        $results = call_curl($callCurlParams);
        $statusCode = $results['responseHttpStatusCode'];
        $data = $results['responseData'];
        $errors = $results['responseErrors'];

        //echo"<pre>";var_dump($wrappedData, $results, $data);echo"</pre>";exit();
        if($statusCode == 200 && $data){
            // parse response and get id
            $response_data = $data[0];
            //echo"<pre>";var_dump($results);echo"</pre>";exit();
            if ($response_data["status"] == "1") {
                $this->CI->system_library->updatePaymentTokensById($params['trackId'], ['expired'=>1]);
                $url = "https:" . explode(":", $response_data["result"])[2];
            } else {
                // handle error either refresh on contact merchant
                $success=false;
                if($response_data['errorText']){
                    $errors = $response_data['errorText'];
                }
            }
        }else{
            $success=false;
        }
        return array(
            'success' => $success,
            'url' => $url,
            'errors' => $errors,
        );
    }

    /**
     * user add his info in Al Rajhi Bank website
     * @param array $params
     * @return array
     * @throws \Exception
     */
    private function getPaymentId(array $params=[]): array
    {
        $success=true;$url='';
        $transaction = $params['transaction'] ?? null;
        $callBackUrl = route('online.callBack', [ 'platform' => $transaction->platform ]);
        $data = [
            "id" => config('services.payments.online.tranportal_id'),
            "password" => config('services.payments.online.tranportal_password'),
            "action" => "1",
            "currencyCode" => "682",
            "errorURL" => $callBackUrl,
            "responseURL" => $callBackUrl,
            "trackId" => strval($transaction->id), //order_id or payment_tokens_id [** MUST BE STRING **]
            "amt" => round($transaction->total, 2),
        ];

        $plainData = json_encode($data, JSON_UNESCAPED_SLASHES);

        $wrappedData = $this->wrapData($plainData);

        $encData = [
            "id" => config('services.payments.online.tranportal_id'),
            "trandata" => $this->encrypt($wrappedData, config('services.payments.online.terminal_resource_key')),
            "errorURL" => $callBackUrl,
            "responseURL" => $callBackUrl,
        ];

        $wrappedData = $this->wrapData(json_encode($encData, JSON_UNESCAPED_SLASHES));

        $headers = [
            'Accept: application/json',
            'Accept-Language: application/json',
            'Content-Type: application/json',
        ];

        $responseResult = [];
        try {
            /*// Create an instance of the custom HTTP client with a base URL
            $http = new CustomHttpClientService();
            // Set custom headers
            $http->withHeaders($headers);
            // Perform POST request
            $responseResult = $http->post(config('services.payments.online.arb_hosted_endpoint'), $wrappedData);*/

            $curlService = new CurlService();
            $responseResult = $curlService->post(config('services.payments.online.arb_hosted_endpoint'), $wrappedData, $headers);

            $errors = $responseResult['errors'] ?? null;
        } catch (\Exception $e) {
            // Handle the exception here
            $success = false;
            $errors = $e->getMessage();
        }
        //dd($responseResult ?? null, $errors);

        $statusCode = $responseResult['status_code'];
        $data = $responseResult['response'];
        if($statusCode == 200 && $data && $success){
            // parse response and get id
            $response_data = $data[0];
            if ($response_data["status"] == "1") {
                $id = explode(":", $response_data["result"])[0];
                $url = config('services.payments.online.payment_endpoint') . $id;
                $transaction->update(['is_expired' => 1, 'payment_id' => $id]);
            } else {
                // handle error either refresh on contact merchant
                $success=false;
                if($response_data['errorText']){
                    $errors = $response_data['errorText'];
                }
            }
        }else{
            $success=false;
        }
        //dd($url, $data);
        return array(
            'success' => $success,
            'url' => $url,
            'errors' => $errors,
        );
    }

    /**
     * @param $trandata
     * @return array
     */
    public function getResult($trandata): array
    {
        $decrypted = $this->decrypt($trandata, config('services.payments.online.terminal_resource_key'));
        $raw = urldecode($decrypted);
        $dataArr = json_decode($raw, true);
        $paymentStatus = $dataArr[0]["result"] ?? null;
        if (isset($paymentStatus) && $paymentStatus === config('services.payments.online.success_status')) {
            $success = true;
        } else {
            $success = false;
        }
        return array(
            'success' => $success,
            'data' => $dataArr,
        );
    }

    /**
     * @param $data
     * @return string
     */
    private function wrapData($data): string
    {
        $data = <<<EOT
        [$data]
EOT;
        return $data;
    }

    function encrypt($str, $key)
    {
        $blocksize = openssl_cipher_iv_length("AES-256-CBC");
        $pad = $blocksize - (strlen($str) % $blocksize);
        $str = $str . str_repeat(chr($pad), $pad);
        $encrypted = openssl_encrypt($str, "AES-256-CBC", $key, OPENSSL_ZERO_PADDING, "PGKEYENCDECIVSPC");
        $encrypted = base64_decode($encrypted);
        $encrypted = unpack('C*', ($encrypted));
        $chars = array_map("chr", $encrypted);
        $bin = join($chars);
        $encrypted = bin2hex($bin);
        $encrypted = urlencode($encrypted);
        return $encrypted;
    }

    /**
     * @param $code
     * @param $key
     * @return bool|string
     */
    private function decrypt($code, $key): bool|string
    {
        $string = hex2bin(trim($code));
        $code = unpack('C*', $string);
        $chars = array_map("chr", $code);
        $code = join($chars);
        $code = base64_encode($code);
        $decrypted = openssl_decrypt($code, "AES-256-CBC", $key, OPENSSL_ZERO_PADDING, "PGKEYENCDECIVSPC");
        $pad = ord($decrypted[strlen($decrypted) - 1]);
        if ($pad > strlen($decrypted)) {
            return false;
        }
        if (strspn($decrypted, chr($pad), strlen($decrypted) - $pad) != $pad) {
            return false;
        }
        return urldecode(substr($decrypted, 0, -1 * $pad));
    }

} //end of class
