<?php

namespace App\Services\Payment\Methods;

use App\Exceptions\PaymentProcessingException;
use App\Models\PaymentMethod;
use App\Services\Payment\Payable;
use App\Services\Payment\Traits\PaymentServiceHelper;
use Illuminate\Http\Request;
use Obydul\LaraSkrill\SkrillClient;
use Obydul\LaraSkrill\SkrillRequest;

class SkrillService extends AbstractPayment
{
    use PaymentServiceHelper;

    private $skrilRequest = null;

    const SKRILL_PROCESSED = 2;

    const SKRILL_PENDING = 0;

    const SKRILL_CANCELLED = -1;

    const SKRILL_FAILED = -2;

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

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

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

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

    public function handleSuccess(Request $request)
    {
        $pid = $request->input('pid');

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

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

        // system will not update by this request only use ipn to update the payment
        // that's why we don't need to update the payment status here
        // i'm throwing an exception to show the payment is successful
        // but the payment status is not updated yet       // system will not update by this request only use ipn to update the payment
        // that's why we don't need to update the payment status here
        // i'm throwing an exception to show the payment is successful
        // but the payment status is not updated yet

        return $pid;
    }

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

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

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

        return $pid;
    }

    public function handleWebhook(Request $request)
    {
        $data = $request->all();

        $apiPassword = $this->getConfig('skrill', 'api_password');

        $md5string = $data['merchant_id'].$data['transaction_id'].strtoupper($apiPassword).$data['mb_amount'].$data['mb_currency'].$data['status'];

        $md5sig = strtoupper(md5($md5string));

        if ($md5sig != $data['md5sig']) {
            throw new PaymentProcessingException('Invalid request');
        }

        $payment = $this->getPaymentByPid($data['order_id']);

        return match ($data['status']) {
            self::SKRILL_PROCESSED => [
                'status' => self::SUCCESS,
                'payment' => $payment,
            ],
            self::SKRILL_CANCELLED => [
                'status' => self::CANCELLED,
                'payment' => $payment,
            ],
            self::SKRILL_FAILED => [
                'status' => self::FAILED,
                'payment' => $payment,
            ],
            default => throw new PaymentProcessingException('Payment status not found', code: 1),
        };
    }

    private function makePayment(Payable $payable, array $rest = [])
    {
        if (! $this->skrilRequest) {
            throw new PaymentProcessingException('Skrill credentials not set');
        }

        $user = auth('client')->user();

        $this->skrilRequest->prepare_only = 1;
        $this->skrilRequest->amount = $payable->getConvertedAmount();
        $this->skrilRequest->currency = $payable->getCurrency();
        $this->skrilRequest->language = 'en';

        $this->skrilRequest->merchant_fields = 'name, order_id, customer_id, customer_email';
        $this->skrilRequest->name = $user->full_name;
        $this->skrilRequest->order_id = $payable->getPid();
        $this->skrilRequest->customer_id = $user->id;
        $this->skrilRequest->customer_email = $user->email;

        $client = new SkrillClient($this->skrilRequest);
        $sid = $client->generateSID();
        $jsonSID = json_decode($sid);

        if ($jsonSID != null && $jsonSID->code == 'BAD_REQUEST') {
            throw new PaymentProcessingException($jsonSID->message);
        }

        return [
            'payment_url' => $client->paymentRedirectUrl($sid),
        ];
    }

    private function setUp(PaymentMethod $paymentMethod, $pid)
    {
        $config = $paymentMethod->meta;

        $this->skrilRequest = new SkrillRequest;

        $this->skrilRequest->pay_to_email = $config['email'];
        $this->skrilRequest->return_url = $this->getReturnUrl($paymentMethod, $pid, 'success');
        $this->skrilRequest->cancel_url = $this->getReturnUrl($paymentMethod, $pid, 'cancel');
        $this->skrilRequest->logo_url = 'https://cdn.shouts.dev/images/shoutsdev.png';
        $this->skrilRequest->status_url = $this->getWebhookUrl($paymentMethod);
    }
}
