<?php

namespace Shetabit\Multipay\Drivers\Rayanpay;

use GuzzleHttp\Client;
use Shetabit\Multipay\Abstracts\Driver;
use Shetabit\Multipay\Contracts\ReceiptInterface;
use Shetabit\Multipay\Exceptions\InvalidPaymentException;
use Shetabit\Multipay\Exceptions\PurchaseFailedException;
use Shetabit\Multipay\Invoice;
use Shetabit\Multipay\Receipt;
use Shetabit\Multipay\RedirectionForm;

class Rayanpay extends Driver
{
    /**
     * Sadad Client.
     *
     * @var object
     */
    protected \GuzzleHttp\Client $client;

    /**
     * Invoice
     *
     * @var Invoice
     */
    protected $invoice;

    /**
     * Driver settings
     *
     * @var object
     */
    protected $settings;

    /**
     * Open Gate By Render Html
     * @var string $htmlPay
     */
    /**
     * Sadad constructor.
     * Construct the class with the relevant settings.
     *
     * @param $settings
     */
    public function __construct(Invoice $invoice, $settings)
    {
        $this->invoice($invoice);
        $this->settings = (object)$settings;
        $this->client = new Client(
            [
                'base_uri' => $this->settings->apiPurchaseUrl,
                'verify' => false
            ]
        );
    }

    /**
     * @throws InvalidPaymentException
     */
    private function auth()
    {
        $data = [
            'clientId' => $this->settings->client_id,
            'userName' => $this->settings->username,
            'password' => $this->settings->password,
        ];
        return $this->makeHttpChargeRequest(
            $data,
            $this->settings->apiTokenUrl,
            'token',
            false
        );
    }

    /**
     * Purchase Invoice.
     *
     * @return string
     *
     * @throws PurchaseFailedException
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function purchase()
    {
        $this->auth();

        $details = $this->invoice->getDetails();

        if (!empty($details['mobile'])) {
            $mobile = $details['mobile'];
        }
        if (!empty($details['phone'])) {
            $mobile = $details['phone'];
        }

        if (empty($mobile)) {
            throw new PurchaseFailedException('شماره موبایل را وارد کنید.');
        }

        if (preg_match('/^(?:98)?9\d{9}$/', $mobile) == false) {
            $mobile = '';
        }

        $amount = $this->invoice->getAmount() * ($this->settings->currency == 'T' ? 10 : 1); // convert to rial

        if ($amount <= 10000) {
            throw new PurchaseFailedException('مقدار مبلغ ارسالی بزگتر از 10000 ریال باشد.');
        }

        $referenceId = hexdec(uniqid());

        $callback = $this->settings->callbackUrl . "?referenceId=" . $referenceId . "&price=" . $amount . "&mobile=" . $mobile;

        $data = [
            'referenceId' => $referenceId,
            'amount' => $amount,
            'msisdn' => $mobile,
            'gatewayId' => 100,
            'callbackUrl' => $callback,
            'gateSwitchingAllowed' => true,
        ];

        $response = $this->makeHttpChargeRequest(
            $data,
            $this->settings->apiPayStart,
            'payment_start',
            true
        );

        $body = json_decode($response, true);

        $this->invoice->transactionId($referenceId);

        // Get RefIf From Html Form Becuese GetWay Not Provide In Api
        $dom = new \DOMDocument();
        $dom->loadHTML($body['bankRedirectHtml']);
        $xp = new \DOMXPath($dom);
        $nodes = $xp->query('//input[@name="RefId"]');
        $node = $nodes->item(0);
        $_SESSION['RefId'] = $node->getAttribute('value');

        return $this->invoice->getTransactionId();
    }

    /**
     * Pay the Invoice render html redirect to getway
     */
    public function pay(): RedirectionForm
    {
        return $this->redirectWithForm($this->settings->apiPurchaseUrl, [
            'x_GateChanged' => 0,
            'RefId' => empty($_SESSION['RefId']) ? null : $_SESSION['RefId'],
        ], 'POST');
    }

    /**
     * Verify payment
     *
     *
     * @throws InvalidPaymentException
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function verify(): ReceiptInterface
    {
        $data = [
            'referenceId' => (int)$this->getInvoice()->getTransactionId(),
            'header' => '',
            'content' => http_build_query($_POST),
        ];

        $response = $this->makeHttpChargeRequest(
            $data,
            $this->settings->apiPayVerify,
            'payment_parse',
            true
        );

        $body = json_decode($response, true);

        $receipt = $this->createReceipt($body['paymentId']);

        $receipt->detail([
            'paymentId' => $body['paymentId'],
            'hashedBankCardNumber' => $body['hashedBankCardNumber'],
            'endDate' => $body['endDate'],
        ]);

        return $receipt;
    }

    /**
     * Generate the payment's receipt
     *
     * @param $referenceId
     */
    protected function createReceipt($referenceId): \Shetabit\Multipay\Receipt
    {
        return new Receipt('rayanpay', $referenceId);
    }


    /**
     * Trigger an exception
     *
     * @param $status
     * @param $method
     * @throws InvalidPaymentException
     */
    private function notVerified($status, string $method): void
    {
        $message = "";
        if ($method === 'token') {
            switch ($status) {
                case '400':
                    $message = 'نقص در پارامترهای ارسالی';
                    break;

                case '401':
                    $message = 'کد کاربری/رمز عبور /کلاینت/آی پی نامعتبر است';
                    break;

                case '500':
                    $message = 'خطایی سمت سرور رخ داده است';
                    break;
            }
        } elseif ($method === 'payment_start') {
            switch ($status) {
                case '400':
                    $message = 'شناسه ارسالی تکراری می باشد ';
                    break;
                case '401':
                    $message = 'توکن نامعتبر';
                    break;

                case '601':
                    $message = 'اتصال به درگاه خطا دارد (پرداخت ناموفق)';
                    break;

                case '500':
                    $message = 'خطایی سمت سرور رخ داده است (احتمال تکراری بودن شماره ref شما یا اگر شماره موبایل دارید باید فرمت زیر باشد 989121112233 )';
                    break;
            }
        } elseif ($method === 'payment_status') {
            switch ($status) {
                case '401':
                    $message = 'توکن نامعتبر است';
                    break;
                case '601':
                    $message = 'پرداخت ناموفق';
                    break;

                case '600':
                    $message = 'پرداخت در حالت Pending می باشد و باید متد fullfill برای تعیین وضعیت صدا زده شود';
                    break;
            }
        } elseif ($method === 'payment_parse') {
            switch ($status) {
                case '401':
                    $message = 'توکن نامعتبر است';
                    break;

                case '500':
                    $message = 'خطایی سمت سرور رخ داده است';
                    break;

                case '600':
                    $message = 'وضعیت نامشخص';
                    break;

                case '601':
                    $message = 'پرداخت ناموفق';
                    break;

                case '602':
                    $message = 'پرداخت یافت نشد';
                    break;

                case '608':
                    $message = 'قوانین پرداخت یافت نشد (برای پرداخت هایی که قوانین دارند)';
                    break;

                case '609':
                    $message = 'وضعیت پرداخت نامعتبر میباشد';
                    break;
            }
        }
        if ($message !== '' && $message !== '0') {
            throw new InvalidPaymentException($message, (int)$status);
        }
        throw new InvalidPaymentException('خطای ناشناخته ای رخ داده است.', (int)$status);
    }

    private function makeHttpChargeRequest(array $data, $url, string $method, bool $forAuth = true)
    {
        $header[] = 'Content-Type: application/json';
        if ($forAuth) {
            $header[] = 'Authorization: Bearer ' . $this->auth();
        }
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $result = curl_exec($ch);
        $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        if ($http_code != 200) {
            $this->notVerified($http_code, $method);
        }
        return $result;
    }
}
