<?php

namespace App\Services\Payments;

use App\Models\Payment;
use App\Services\ApplicationUrlService;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;

class PaypalPaymentService
{
    public function __construct(
        private ApplicationUrlService $urls,
    ) {
    }

    public function getRedirectUrl(): string
    {
        return (string) (config('services.paypal.payment_url')
            ?: env('PAYPAL_PAYMENT_URL')
            ?: 'https://www.paypal.com/ncp/payment/87FJL3EV8C7NE');
    }

    public function createPayment(int $paymentId): array
    {
        $payment = Payment::query()->find($paymentId);
        if (!$payment) {
            throw new \RuntimeException('Payment not found.');
        }

        if (!$this->sdkAvailable()) {
            return [
                'redirect_url' => $this->getRedirectUrl(),
                'mode' => 'hosted',
                'message' => 'PayPal SDK not installed; using hosted redirect URL.',
            ];
        }

        try {
            $apiContext = $this->buildApiContext();

            $payer = new \PayPal\Api\Payer();
            $payer->setPaymentMethod('paypal');

            $amount = new \PayPal\Api\Amount();
            $amount->setCurrency('USD')->setTotal($payment->balance ?? $payment->total_amount ?? 0);

            $transaction = new \PayPal\Api\Transaction();
            $transaction->setAmount($amount)
                ->setDescription('Payment for school fees')
                ->setInvoiceNumber(uniqid('inv_', true));

            $paypalPayment = new \PayPal\Api\Payment();
            $paypalPayment->setIntent('sale')
                ->setPayer($payer)
                ->setTransactions([$transaction]);

            $redirectUrls = new \PayPal\Api\RedirectUrls();
            $redirectUrls->setReturnUrl($this->urls->paypalExecuteReturnUrl())
                ->setCancelUrl($this->urls->paypalCancelUrl());

            $paypalPayment->setRedirectUrls($redirectUrls);
            $paypalPayment->create($apiContext);

            Cache::put($this->cacheKey((string) $paypalPayment->getId()), $paymentId, now()->addHours(2));

            return [
                'redirect_url' => $paypalPayment->getApprovalLink(),
                'mode' => 'sdk',
                'paypal_payment_id' => $paypalPayment->getId(),
                'payment_id' => $paymentId,
            ];
        } catch (\Throwable $e) {
            Log::error('PayPal create payment failed: ' . $e->getMessage());
            throw new \RuntimeException('Unable to create PayPal payment.');
        }
    }

    public function executePayment(string $payerId, ?string $paypalPaymentId = null, ?int $paymentId = null): void
    {
        if (!$this->sdkAvailable()) {
            throw new \RuntimeException('PayPal SDK not installed.');
        }

        $paypalId = $paypalPaymentId ?: '';
        if ($paypalId === '') {
            throw new \RuntimeException('Missing PayPal payment ID.');
        }

        $apiContext = $this->buildApiContext();
        $payment = \PayPal\Api\Payment::get($paypalId, $apiContext);
        $execution = new \PayPal\Api\PaymentExecution();
        $execution->setPayerId($payerId);

        $payment->execute($execution, $apiContext);

        $internalPaymentId = $paymentId;
        if (!$internalPaymentId && $paypalId !== '') {
            $internalPaymentId = (int) Cache::get($this->cacheKey($paypalId));
        }
        if ($internalPaymentId) {
            Payment::query()->whereKey($internalPaymentId)->update(['status' => 'Completed']);
            if ($paypalId !== '') {
                Cache::forget($this->cacheKey($paypalId));
            }
        }
    }

    public function cancelPayment(): void
    {
        // no-op (client handles redirect)
    }

    private function sdkAvailable(): bool
    {
        return class_exists(\PayPal\Rest\ApiContext::class)
            && class_exists(\PayPal\Auth\OAuthTokenCredential::class);
    }

    private function buildApiContext(): \PayPal\Rest\ApiContext
    {
        $clientId = (string) (config('services.paypal.client_id') ?: env('PAYPAL_CLIENT_ID'));
        $secret = (string) (config('services.paypal.secret') ?: env('PAYPAL_SECRET'));
        $mode = (string) (config('services.paypal.mode') ?: env('PAYPAL_MODE') ?: 'sandbox');

        $apiContext = new \PayPal\Rest\ApiContext(
            new \PayPal\Auth\OAuthTokenCredential($clientId, $secret)
        );

        $apiContext->setConfig([
            'mode' => $mode,
            'http.headers' => ['Connection' => 'Close'],
        ]);

        return $apiContext;
    }

    private function cacheKey(string $paypalPaymentId): string
    {
        return 'paypal_payment:' . $paypalPaymentId;
    }
}
