<?php

namespace App\PaymentGateways\PayPal;

use AllowDynamicProperties;
use App\Contracts\PaymentGatewayContract;
use App\Models\Order;
use App\Services\OrderService;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Omnipay\Common\GatewayInterface;
use Omnipay\Omnipay;

#[AllowDynamicProperties]
class PayPalGateway implements PaymentGatewayContract
{
    private GatewayInterface $gateway;

    public function __construct(protected OrderService $orderService)
    {
        $gateway = Omnipay::create('PayPal_Express');

        $this->gateway = $gateway->initialize(array(
            'username' => setting("paypal.username"),
            'password' => setting("paypal.password"),
            'signature' => setting("paypal.signature"),
            'testMode' => setting('paypal.sandbox_mode') == 'yes',
            'currency' => setting('currency', 'USD'),
        ));
    }

    public function processPayment(Order $order)
    {
        // Send purchase request
        $response = $this->gateway->purchase([
            'amount' => $order->total,
            'returnUrl' => route('ch_order_submitted', [$order->encrypted_id]),
            'cancelUrl' => route('ch_order_cancel', [$order->encrypted_id]),
            'notifyUrl' => route('gateway.ipn', ['gateway' => 'PayPal', 'order_id' => $order->id]),
            'transactionId' => $order->order_reference,
            'no_shipping' => 1,
        ])->send();



        if ($response->isRedirect()) {
            // Redirect to offsite payment gateway
            return response()->json([
                'redirect_url' => $response->getRedirectUrl(),
                'external' => true,
            ]);
        } else {
            $this->orderService->handlePostPaymentFlow($order, getConstant('ORDER_STATUS_FAILED'));
            $order->update([
                'payment_error' => $response->getMessage(),
                'status' => getConstant('ORDER_STATUS_FAILED'),
            ]);
            return response()->json([
                'redirect_url' => route('ch_order_failed', [$order->encrypted_id]),
            ]);
        }
    }

    public function completePurchase(Order $order): void
    {
        $order->update(['status' => getConstant('ORDER_STATUS_PROCESSING')]);

        $this->gateway->completePurchase([
            'amount' => number_format((float)$order->total, 2),
            'currency' => setting('currency', 'USD'),
            'transactionId' => $order->order_reference
        ])->send();
    }

    public function processIPN()
    {
        $order_reference = request('invoice');
        $order = Order::where('order_reference', $order_reference)->firstOrFail();

        $sandbox = setting('paypal.sandbox_mode') === 'yes';
        $paypalUrl = $sandbox
            ? 'https://ipnpb.sandbox.paypal.com/cgi-bin/webscr'
            : 'https://ipnpb.paypal.com/cgi-bin/webscr';

        $payload = ['cmd' => '_notify-validate'] + request()->all();

        try {
            $res = Http::asForm()
                ->withHeaders(['Connection' => 'Close'])
                ->post($paypalUrl, $payload);

            Log::info('PayPal IPN Response', [
                'body' => $res->body(),
                'payload' => $payload,
            ]);

            if ($res->body() === 'VERIFIED') {
                if (strtolower(request('payment_status')) === 'completed') {
                    $order->update(['transaction_id' => request('txn_id')]);
                    $this->orderService->handlePostPaymentFlow($order, getConstant('ORDER_STATUS_COMPLETED'));
                } else {
                    $order->update(['status' => getConstant('ORDER_STATUS_FAILED')]);
                }
            } else {
                Log::warning('PayPal IPN INVALID', ['payload' => $payload]);
            }
        } catch (\Exception $e) {
            Log::error('PayPal IPN Error: ' . $e->getMessage());
        }
    }

    public function getToken()
    {
        // TODO: Implement getToken() method.
    }

    public function isConfigured(): bool
    {
        return setting('paypal.enabled', 'yes') == 'yes' &&
            (
                setting('paypal.username') != '' &&
                setting('paypal.password') != '' &&
                setting('paypal.signature') != ''
            );
    }

    public function renderForm()
    {
        return [
            'id' => $this->getIdentifier(),
        ];
    }

    public function getLabel(): string
    {
        return setting('paypal.title', 'PayPal');
    }

    public function getIdentifier(): string
    {
        return 'paypal';
    }
}
