<?php

namespace App\Services\Payment\Methods;

use App\Exceptions\PaymentProcessingException;
use App\Models\PaymentMethod;
use App\Services\Payment\Helpers\PayUHelper;
use App\Services\Payment\Payable;
use App\Services\Payment\Traits\PaymentServiceHelper;
use Illuminate\Http\Request;

class PayUService extends AbstractPayment
{
    use PaymentServiceHelper;

    private $payu;

    const SUCCESS = 'success';

    const PENDING = 'pending';

    const FAILURE = 'failure';

    public function pay(Payable $payable, $rest = [])
    {
        $this->setUp($payable->getMethod());

        $response = $this->makePayment($payable);

        $payment = $this->createPayment($payable);

        return [
            'payment' => $payment,
            'response' => [
                ...$response,
            ],
        ];
    }

    public function handleWebhook(Request $request)
    {
        $this->setUp($this->getPaymentMethod('payu'));

        if (! $this->payu->verifyRequest($request->all())) {
            throw new PaymentProcessingException('Invalid request');
        }

        $pid = $request->txnid;

        $payment = $this->getPaymentByPid($pid);

        if ($request->status == $this::PENDING) {
            throw new PaymentProcessingException(__('Payment is not completed'));
        }

        if ($request->status == $this::FAILURE) {
            throw new PaymentProcessingException(__('Payment failed'));
        }

        $payment->gateway_id = $request->mihpayid;
        $payment->save();

        return match ($request->status) {
            $this::SUCCESS => [
                'status' => self::SUCCESS,
                'payment' => $payment,
            ],
            $this::FAILURE => [
                'status' => self::FAILURE,
                'payment' => $payment,
            ],
            default => throw new PaymentProcessingException(__('Payment status not found'), code: 1),
        };
    }

    public function handleSuccess(Request $request)
    {
        $this->setUp($this->getPaymentMethod('payu'));

        $verified = $this->payu->verifyRequest($request->all());
        if (! $verified) {
            return throw new PaymentProcessingException(__('Invalid request'));
        }

        $pid = $request->pid;

        $payment = $this->getPaymentByPid($pid);

        if (! $payment) {
            throw new PaymentProcessingException(__('Payment not found'));
        }

        if ($request->status == $this::PENDING) {
            throw new PaymentProcessingException(__('Payment is not completed'));
        }

        if ($request->status == $this::FAILURE) {
            throw new PaymentProcessingException(__('Payment failed'));
        }

        return $pid;
    }

    public function handleFailed(Request $request)
    {
        $pid = $request->pid;

        $this->setUp($this->getPaymentMethod('payu'));

        if (! $this->payu->verifyRequest($request->all())) {
            throw new PaymentProcessingException('Invalid request');
        }

        return $pid;
    }

    private function makePayment(Payable $payable)
    {
        $user = auth('client')->user();

        $data = [];

        $data['amount'] = $payable->getConvertedAmount();
        $data['productinfo'] = 'Add fund';
        $data['txnid'] = $payable->getPid();
        $data['firstname'] = $user->first_name;
        $data['lastname'] = $user->last_name;
        $data['email'] = $user->email;
        $data['currency'] = $payable->getCurrency();
        $data['surl'] = $this->getReturnUrl($payable->getMethod(), pid: $payable->getPid(), status: 'success');
        $data['furl'] = $this->getReturnUrl($payable->getMethod(), pid: $payable->getPid(), status: 'cancel');

        $paymentData = $this->payu->initiatePayment($data);

        return [
            'gateway' => $payable->getMethod()->slug,
            'params' => [
                'paymentData' => $paymentData,
                'requestUrl' => $this->payu->baseUrl,
            ],
        ];
    }

    private function setUp(PaymentMethod $paymentMethod)
    {
        $config = $paymentMethod->meta;
        $env = $paymentMethod->is_test ? 'sandbox' : 'production';

        $this->payu = new PayUHelper($config['api_key'], $config['salt'], $env);
    }
}
