<?php


namespace NioModules\Paypal;

use App\Interfaces\Payable;
use App\Models\PaymentMethod;
use App\Enums\PaymentMethodStatus;
use App\Models\UserTempMeta;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\ValidationException;

use PayPalHttp\HttpException;
use PayPalCheckoutSdk\Core\PayPalHttpClient;
use PayPalCheckoutSdk\Core\SandboxEnvironment;
use PayPalCheckoutSdk\Orders\OrdersCreateRequest;
use PayPalCheckoutSdk\Core\ProductionEnvironment;
use PayPalCheckoutSdk\Orders\OrdersCaptureRequest;

class PaypalModule implements Payable
{
    const VERSION = '1.5.0';
    const LAST_UPDATE = '04102024';
    const MIN_APP_VER = '1.5.0';
    const MOD_TYPES = 'core';
    const SLUG = 'paypal';
    const METHOD = 'paypal';

    private const COMPLETED = 'COMPLETED';
    private $transaction;
    private $clientId;
    private $clientSecret;
    private $client;
    private $paymentMethod;
    private $cancelRoute = 'public.payment.paypal.cancel';
    private $returnRoute = 'public.payment.paypal.return';

    public function __construct()
    {
        $this->paymentMethod = PaymentMethod::where('slug', self::SLUG)->first();
    }

    /**
     * @return string
     * @version 1.0.0
     * @since 1.0
     */
    public function getRequiredMinBaseAppVersion()
    {
        return self::MIN_APP_VER;
    }

    /**
     * @return string
     * @version 1.0.0
     * @since 1.0
     */
    public function getSlug()
    {
        return self::SLUG;
    }

    /**
     * @return string
     * @version 1.0.0
     * @since 1.0
     */
    public function getTitle()
    {
        return data_get($this->paymentMethod->config, 'meta.title', $this->paymentMethod->name);
    }

    /**
     * @version 1.0.0
     * @since 1.0
     */
    private function setClient()
    {
        if ($this->getCredential('sandbox')) {
            $environment = new SandboxEnvironment($this->clientId, $this->clientSecret);
        } else {
            $environment = new ProductionEnvironment($this->clientId, $this->clientSecret);
        }

        $this->client = new PayPalHttpClient($environment);
    }

    public function getNonDecimalCurrencies()
    {
        return data_get(config('modules.'.PaypalModule::SLUG), 'non_decimal_currency');
    }

    public function updateTnxAmount($currency, $amount)
    {
        return in_array($currency, $this->getNonDecimalCurrencies()) ? round($amount) : number_format($amount, 2, '.', '');
    }

    public function updateSessionData()
    {
        $user = auth()->user();
        $payment = user_temp_meta('deposit_details', null, $user);

        if (!empty($payment)) {
            $payment['amount'] = round($payment['amount']);
            $payment['total'] = round(to_sum($payment['amount'], $payment['amount_fees']));
            $payment['base_amount'] = round(($payment['amount'] / $payment['exchange_rate']), 2);
            $payment['base_total'] = to_sum($payment['base_amount'], $payment['base_fees']);

            UserTempMeta::updateOrCreate([
                'user_id' => $user->id,
                'meta_key' => 'deposit_details',
            ], ['meta_value' => $payment]);
        }
        return $payment;
    }

    /**
     * @return mixed
     * @throws ValidationException
     * @version 1.0.0
     * @since 1.0
     */
    private function createPayment()
    {
        $tnxCurrency = data_get($this->transaction, 'tnx_currency');
        $tnxTotal = data_get($this->transaction, 'tnx_total');

        $request = new OrdersCreateRequest();
        $request->prefer('return=representation');
        $request->body = [
            "intent" => "CAPTURE",
            "purchase_units" => [[
                "reference_id" => data_get($this->transaction, 'tnx'),
                "amount" => [
                    "value" => $this->updateTnxAmount($tnxCurrency, $tnxTotal),
                    "currency_code" => $tnxCurrency
                ]
            ]],
            "application_context" => [
                "cancel_url" => route($this->cancelRoute),
                "return_url" => route($this->returnRoute),
            ]
        ];

        try {
            return $this->client->execute($request);
        } catch (HttpException $ex) {
            if ($ex->statusCode >= 500) {
                Log::error('PAYPAL_SERVER_ERROR', [$ex->getMessage()]);
                throw ValidationException::withMessages(['confirm' => __('Sorry right now we are unable to connect Paypal server. Please try again later.')]);
            } else {
                Log::error('PAYPAL_PAYMENT', [$ex->getMessage()]);
                throw ValidationException::withMessages(['confirm' => __('Unable to proceed, please reload the page and try again or contact us if error persist.')]);
            }
        }
    }

    /**
     * @param $transaction
     * @param $paymentMethodDetails
     * @param bool $deletable
     * @return array
     * @throws ValidationException
     * @version 1.0.0
     * @since 1.0
     */
    public function makePayment($transaction, $paymentMethodDetails, $deletable = true)
    {
        $this->transaction = $transaction;
        $this->clientId = $this->getCredential('client_id');
        $this->clientSecret = $this->getCredential('client_secret');
        $this->setClient();
        $orderResponse = $this->createPayment();
        $transactionMeta = $transaction->meta;
        $transactionMeta['paypal_order_response'] = $orderResponse->result;
        $transaction->reference = data_get($orderResponse->result, 'id');
        $transaction->meta = $transactionMeta;
        $transaction->save();

        if (($orderResponse->statusCode != Response::HTTP_CREATED) && $deletable) {
            $transaction->delete();
        }

        $approvalLink = array_values(array_filter(data_get($orderResponse->result, 'links'), function ($item) {
            return data_get($item, 'rel') == 'approve';
        }));

        return [
            'redirect' => true,
            'approve_url' => (array) array_shift($approvalLink),
            'cancel_url' => [
                'href' => route($this->cancelRoute, ['token' => $transaction->reference]),
                'rel' => 'cancel',
                'method' => "GET",
            ],
            'return_url' => [
                'href' => route($this->returnRoute, ['token' => $transaction->reference]),
                'rel' => 'return',
                'method' => "GET",
            ],
            'transaction' => $transaction,
        ];
    }

    /**
     * @param $transaction
     * @param $paymentMethodDetails
     * @return bool
     * @version 1.0.0
     * @since 1.0
     */
    public function verifyPayment($transaction, $paymentMethodDetails)
    {
        $this->transaction = $transaction;
        $this->clientId = $this->getCredential('client_id');
        $this->clientSecret = $this->getCredential('client_secret');
        $this->setClient();

        $request = new OrdersCaptureRequest(data_get($transaction, 'reference'));
        $request->prefer('return=representation');
        try {
            $response = $this->client->execute($request);
            if (data_get($response->result, 'status') == self::COMPLETED) {
                return true;
            }

            return false;
        } catch (HttpException $ex) {
            //Log::error('PAYPAL_VERIFICATION', [$ex->getMessage()]);
            return false;
        }
    }

    /*
     * Check if this active
     */
    public function isActive()
    {
        if (blank($this->paymentMethod)) {
            return false;
        }

        if (!$this->hasConfig()) {
            return false;
        }

        if (empty($this->paymentMethod->currencies)) {
            return false;
        }

        return true;
    }

    /*
     * Check if this enable
     */
    public function isEnable()
    {
        if (!$this->isActive()) {
            return false;
        }

        if ($this->paymentMethod->status == PaymentMethodStatus::ACTIVE) {
            return true;
        }

        return false;
    }

    /*
     * Check it has minimal configuration
     */
    public function hasConfig()
    {
        $clientId = data_get($this->paymentMethod, 'config.api.client_id');
        $clientSecret = data_get($this->paymentMethod, 'config.api.client_secret');
        $account = data_get($this->paymentMethod, 'config.api.account');
        $sandbox = data_get($this->paymentMethod, 'config.api.sandbox');

        if (empty($clientId) || empty($clientSecret) || empty($account) || empty($sandbox)) {
            return false;
        }

        return true;
    }

    /*
     * Provide payment info
     */
    public function getPayInfo($currency = null, $only = false, $apiCompact = true)
    {
        if (!$this->hasConfig()) {
            return false;
        }

        $config = $this->paymentMethod->config;
        $api = $config['api'];
        if (isset($api['sandbox'])) {
            $api['sandbox'] = ($api['sandbox'] == 'active') ? true : false;
        }

        if ($only === true) {
            $acc_ref = $api['account'] ?? 'PP';
            $acc_id = $api['client_id'] ?? '0000';
            return $acc_ref . ' ' . str_compact($acc_id, '-', 4);
        }

        if (!empty($api) && ($apiCompact === true)) {
            $api['client_id'] = str_compact($api['client_id'], '-xx-');
            // $api['client_secret'] = str_compact($api['client_secret'], '-xx-');
            if (isset($api['client_secret']) && $api['client_secret']) {
                unset($api['client_secret']);
            }
        }

        return !empty($api) ? $api : false;
    }

    /*
     * Return api credential
     */
    public function getCredential($name = null)
    {
        if (empty($name)) {
            return false;
        }

        $api = $this->getPayInfo(null, null, false);
        if (in_array($name, ['client_id', 'client_secret', 'sandbox'])) {
            return ($api[$name]) ? $api[$name] : false;
        }

        return false;
    }

    public function getDetailsView($transaction, $paymentMethodDetails)
    {
        return view('Paypal::details', compact('transaction', 'paymentMethodDetails'));
    }
}
