<?php

namespace Shetabit\Multipay\Drivers\Behpardakht;

use Shetabit\Multipay\Abstracts\Driver;
use Shetabit\Multipay\Exceptions\InvalidPaymentException;
use Shetabit\Multipay\Exceptions\PurchaseFailedException;
use Shetabit\Multipay\Contracts\ReceiptInterface;
use Shetabit\Multipay\Invoice;
use Shetabit\Multipay\Receipt;
use Shetabit\Multipay\Request;
use Carbon\Carbon;
use Shetabit\Multipay\RedirectionForm;
use SoapClient;

class Behpardakht extends Driver
{
    /**
     * Invoice
     *
     * @var Invoice
     */
    protected $invoice;

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

    /**
     * Behpardakht constructor.
     * Construct the class with the relevant settings.
     *
     * @param $settings
     */
    public function __construct(Invoice $invoice, $settings)
    {
        $this->invoice($invoice);
        $this->settings = (object)$settings;
    }

    /**
     * Purchase Invoice.
     *
     * @return string
     *
     * @throws PurchaseFailedException
     * @throws \SoapFault
     */

    public function purchase()
    {
        $soap = $this->client($this->settings->apiPurchaseUrl);

        $purchaseData = $this->preparePurchaseData();

        if ($this->settings->cumulativeDynamicPayStatus) {
            $response = $soap->bpCumulativeDynamicPayRequest($purchaseData);
        } else {
            $response = $soap->bpPayRequest($purchaseData);
        }

        // fault has happened in bank gateway
        if ($response->return == 21) {
            throw new PurchaseFailedException('پذیرنده معتبر نیست.', 21);
        }

        $data = explode(',', $response->return);

        // purchase was not successful
        if ($data[0] !== "0") {
            throw new PurchaseFailedException($this->translateStatus($data[0]), (int)$data[0]);
        }

        $this->invoice->transactionId($data[1]);

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

    /**
     * Pay the Invoice
     */
    public function pay(): RedirectionForm
    {
        $payUrl = $this->settings->apiPaymentUrl;

        $data = [
            'RefId' => $this->invoice->getTransactionId()
        ];

        if (!empty($this->invoice->getDetails()['mobile'])) {
            $data['MobileNo'] = $this->invoice->getDetails()['mobile'];
        }

        return $this->redirectWithForm($payUrl, $data, 'POST');
    }

    /**
     * Verify payment
     *
     * @return mixed|Receipt
     *
     * @throws InvalidPaymentException
     * @throws \SoapFault
     */
    public function verify(): ReceiptInterface
    {
        $resCode = Request::input('ResCode');
        if ($resCode != '0') {
            throw new InvalidPaymentException($this->translateStatus($resCode), $resCode);
        }

        $data = $this->prepareVerificationData();

        $soap = $this->client($this->settings->apiVerificationUrl);

        // step1: verify request
        $verifyResponse = (int)$soap->bpVerifyRequest($data)->return;
        if ($verifyResponse != 0) {
            // rollback money and throw exception
            // avoid rollback if request was already verified
            if ($verifyResponse != 43) {
                $soap->bpReversalRequest($data);
            }

            throw new InvalidPaymentException($this->translateStatus($verifyResponse), $verifyResponse);
        }

        // step2: settle request
        $settleResponse = $soap->bpSettleRequest($data)->return;
        if ($settleResponse != 0) {
            // rollback money and throw exception
            // avoid rollback if request was already settled/reversed
            if ($settleResponse != 45 && $settleResponse != 48) {
                $soap->bpReversalRequest($data);
            }

            throw new InvalidPaymentException($this->translateStatus($settleResponse), $settleResponse);
        }

        $receipt = $this->createReceipt($data['saleReferenceId']);

        $receipt->detail([
            "RefId" => Request::input('RefId'),
            "SaleOrderId" => Request::input('SaleOrderId'),
            "CardHolderPan" => Request::input('CardHolderPan'),
            "CardHolderInfo" => Request::input('CardHolderInfo'),
            "SaleReferenceId" => Request::input('SaleReferenceId'),
        ]);

        return $receipt;
    }

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

    /**
     * Prepare data for payment verification
     */
    protected function prepareVerificationData(): array
    {
        $orderId = Request::input('SaleOrderId');
        $verifySaleOrderId = Request::input('SaleOrderId');
        $verifySaleReferenceId = Request::input('SaleReferenceId');

        return [
            'terminalId' => $this->settings->terminalId,
            'userName' => $this->settings->username,
            'userPassword' => $this->settings->password,
            'orderId' => $orderId,
            'saleOrderId' => $verifySaleOrderId,
            'saleReferenceId' => $verifySaleReferenceId
        ];
    }

    /**
     * Prepare data for purchasing invoice
     */
    protected function preparePurchaseData(): array
    {
        if (!empty($this->invoice->getDetails()['description'])) {
            $description = $this->invoice->getDetails()['description'];
        } else {
            $description = $this->settings->description;
        }

        $payerId = $this->invoice->getDetails()['payerId'] ?? 0;

        return [
            'terminalId' => $this->settings->terminalId,
            'userName' => $this->settings->username,
            'userPassword' => $this->settings->password,
            'callBackUrl' => $this->settings->callbackUrl,
            'amount' => $this->invoice->getAmount() * ($this->settings->currency == 'T' ? 10 : 1), // convert to rial
            'localDate' => Carbon::now()->format('Ymd'),
            'localTime' => Carbon::now()->format('Gis'),
            'orderId' => crc32($this->invoice->getUuid()),
            'additionalData' => $description,
            'payerId' => $payerId
        ];
    }

    private function client(string $url): SoapClient
    {
        if (isset($_SERVER['SERVER_PROTOCOL']) && $_SERVER['SERVER_PROTOCOL'] == "HTTP/2.0") {
            $context = stream_context_create(['ssl' => ['verify_peer' => false, 'verify_peer_name' => false]]);

            return new SoapClient($url, ['stream_context' => $context]);
        }

        return new SoapClient($url);
    }

    /**
     * Convert status to a readable message.
     *
     * @param $status
     *
     * @return mixed|string
     */
    private function translateStatus($status): string
    {
        $translations = [
            '0' => 'تراکنش با موفقیت انجام شد',
            '11' => 'شماره کارت نامعتبر است',
            '12' => 'موجودی کافی نیست',
            '13' => 'رمز نادرست است',
            '14' => 'تعداد دفعات وارد کردن رمز بیش از حد مجاز است',
            '15' => 'کارت نامعتبر است',
            '16' => 'دفعات برداشت وجه بیش از حد مجاز است',
            '17' => 'کاربر از انجام تراکنش منصرف شده است',
            '18' => 'تاریخ انقضای کارت گذشته است',
            '19' => 'مبلغ برداشت وجه بیش از حد مجاز است',
            '111' => 'صادر کننده کارت نامعتبر است',
            '112' => 'خطای سوییچ صادر کننده کارت',
            '113' => 'پاسخی از صادر کننده کارت دریافت نشد',
            '114' => 'دارنده کارت مجاز به انجام این تراکنش نیست',
            '21' => 'پذیرنده نامعتبر است',
            '23' => 'خطای امنیتی رخ داده است',
            '24' => 'اطلاعات کاربری پذیرنده نامعتبر است',
            '25' => 'مبلغ نامعتبر است',
            '31' => 'پاسخ نامعتبر است',
            '32' => 'فرمت اطلاعات وارد شده صحیح نمی‌باشد',
            '33' => 'حساب نامعتبر است',
            '34' => 'خطای سیستمی',
            '35' => 'تاریخ نامعتبر است',
            '41' => 'شماره درخواست تکراری است',
            '42' => 'تراکنش Sale یافت نشد',
            '43' => 'قبلا درخواست Verify داده شده است',
            '44' => 'درخواست Verify یافت نشد',
            '45' => 'تراکنش Settle شده است',
            '46' => 'تراکنش Settle نشده است',
            '47' => 'تراکنش Settle یافت نشد',
            '48' => 'تراکنش Reverse شده است',
            '412' => 'شناسه قبض نادرست است',
            '413' => 'شناسه پرداخت نادرست است',
            '414' => 'سازمان صادر کننده قبض نامعتبر است',
            '415' => 'زمان جلسه کاری به پایان رسیده است',
            '416' => 'خطا در ثبت اطلاعات',
            '417' => 'شناسه پرداخت کننده نامعتبر است',
            '418' => 'اشکال در تعریف اطلاعات مشتری',
            '419' => 'تعداد دفعات ورود اطلاعات از حد مجاز گذشته است',
            '421' => 'IP نامعتبر است',
            '51' => 'تراکنش تکراری است',
            '54' => 'تراکنش مرجع موجود نیست',
            '55' => 'تراکنش نامعتبر است',
            '61' => 'خطا در واریز',
            '62' => 'مسیر بازگشت به سایت در دامنه ثبت شده برای پذیرنده قرار ندارد',
            '98' => 'سقف استفاده از رمز ایستا به پایان رسیده است'
        ];

        $unknownError = 'خطای ناشناخته رخ داده است.';

        return array_key_exists($status, $translations) ? $translations[$status] : $unknownError;
    }
}
