<?php

namespace App\Services\Payment\Methods;

use App\Enums\PaymentStatusEnum;
use App\Models\Payment;
use App\Models\PaymentMethod;
use App\Services\Payment\Payable;
use Illuminate\Http\Request;

abstract class AbstractPayment
{
    const SUCCESS = 'success';

    const FAILED = 'failed';

    const PENDING = 'pending';

    const CANCELLED = 'cancelled';

    abstract public function pay(Payable $pay, ?array $rest);

    abstract public function handleSuccess(Request $request);

    abstract public function handleFailed(Request $request);

    /**
     * Create a payment record for the authenticated user.
     *
     * @param  PaymentMethod  $paymentMethod  The payment method used.
     * @param  string  $pid  Payment uniquely identifier.
     * @param  string  $gatewayId  The gateway id.
     * @param  float  $amount  The amount of the payment.
     * @param  string  $currencyCode  The currency code of the payment.
     * @param  string  $type  The type of payment.
     * @return Payment
     */
    protected function createPayment(Payable $payable, ?string $gatewayId = null, ?string $pid = null, ?array $meta = null)
    {
        /**
         * @var \App\Models\User $user
         */
        $user = auth('client')->user();

        return $user->payments()->updateOrCreate([
            'payment_identifier' => $pid ?: $payable->getPid(),
            'status' => PaymentStatusEnum::PENDING,
        ], [
            'payment_method_id' => $payable->getMethod()->id,
            'gateway_id' => $payable->getGatewayId() ?: $gatewayId,
            'amount' => $payable->getAmount(),
            'charge' => $payable->getCharge(),
            'payable' => $payable->getPayableAmount(),
            'conversion_rate' => $payable->getCurrencyRate(),
            'type' => $payable->getType(),
            'meta' => $meta,
            'currency' => $payable->getCurrency(),
        ]);
    }

    /**
     * Update the status of a payment to 'cancelled' based on the payment identifier.
     *
     * @param  string  $payment_identifier  The unique identifier of the payment to be updated.
     * @return void
     */
    protected function cancelPayment($payment_identifier)
    {
        Payment::where('payment_identifier', $payment_identifier)->update([
            'status' => PaymentStatusEnum::CANCELLED,
        ]);
    }

    /**
     * Update the status of a payment to 'paid' based on the payment identifier.
     *
     * @param  string  $payment_identifier  The unique identifier of the payment to be updated.
     * @return void
     */
    protected function paidPayment($payment_identifier)
    {
        Payment::where('payment_identifier', $payment_identifier)->update([
            'status' => PaymentStatusEnum::PAID,
        ]);
    }

    /**
     * Return the URL to redirect to after a payment.
     *
     * On local, use ngrok to tunnel to the local environment.
     * On production, use the route 'payment.return'.
     *
     * @param  string  $pid
     * @param  string  $status
     * @param  string  $url
     * @param  string  $method
     * @return string
     */
    protected function getReturnUrl(?PaymentMethod $paymentMethod = null, $pid = null, $status = null, $url = null)
    {

        $returnValue = '';

        $ngrok_url = env('NGROK_URL');
        $domain = $ngrok_url ?? config('app.url');

        if ($url) {
            $returnValue = $domain.$url;
        } else {
            $params = [
                'gateway' => $paymentMethod->slug,
            ];

            if ($status) {
                $params['status'] = $status;
            }

            if ($pid) {
                $params['pid'] = $pid;
            }

            $returnValue = "$domain/payment/return/{$paymentMethod->slug}?".http_build_query($params);
        }

        return $returnValue;
    }

    /**
     * get webhook url.
     *
     * @return string
     */
    protected function getWebhookUrl(PaymentMethod $paymentMethod)
    {
        if (app()->environment(['local'])) {
            $ngrok_url = env('NGROK_URL');

            // if ngrok url is not set, use the app url
            $domain = $ngrok_url ?? config('app.url');

            return "$domain/payment/webhooks/{$paymentMethod->slug}";
        }

        return route('payment.webhooks', ['gateway' => $paymentMethod->slug]);
    }

    protected function getPayment($payment_identifier)
    {
        return Payment::where('payment_identifier', $payment_identifier)->first();
    }
}
