<?php

namespace App\Services;

use App\Models\Order;
use App\Models\PaymentAttempt;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;
use RuntimeException;
use Throwable;

class GenericPaymentGatewayService
{
    public function __construct(
        private IntegrationSignature $signature,
        private NotificationService $notifications,
        private OrderRewardService $rewards,
    ) {}

    public function initiate(Order $order): PaymentAttempt
    {
        if ($order->payment_method !== 'online_gateway') {
            throw new RuntimeException('This order does not use the online gateway.');
        }

        return DB::transaction(function () use ($order) {
            $order = Order::whereKey($order->id)->lockForUpdate()->firstOrFail();

            if ($order->payment_status === 'paid') {
                throw new RuntimeException('Order is already paid.');
            }

            $existing = $order->paymentAttempts()
                ->where('gateway', 'generic_http')
                ->whereIn('status', ['initiated', 'pending'])
                ->latest()
                ->first();

            if ($existing) {
                return $existing;
            }

            $payload = [
                'order_number' => $order->order_number,
                'amount_minor' => $order->total_minor,
                'currency_code' => $order->currency_code,
                'customer' => [
                    'name' => $order->customer_name,
                    'email' => $order->customer_email,
                ],
                'callback_url' => route('payments.generic.webhook'),
                'return_url' => route('orders.show', $order),
            ];

            $reference = 'PAY-'.now()->format('ymd').'-'.Str::upper(Str::random(8));
            $responsePayload = $this->sendInitiationRequest($payload + ['reference' => $reference]);
            $providerReference = data_get($responsePayload, 'reference', data_get($responsePayload, 'payment_reference', $reference));

            return $order->paymentAttempts()->create([
                'gateway' => 'generic_http',
                'reference' => $providerReference,
                'status' => data_get($responsePayload, 'status', 'pending'),
                'amount_minor' => $order->total_minor,
                'currency_code' => $order->currency_code,
                'request_payload' => $payload,
                'response_payload' => $responsePayload,
            ]);
        });
    }

    public function applyWebhook(array $payload): ?PaymentAttempt
    {
        $reference = data_get($payload, 'reference', data_get($payload, 'payment_reference'));

        if (blank($reference)) {
            throw new RuntimeException('Missing payment reference.');
        }

        return DB::transaction(function () use ($payload, $reference) {
            $attempt = PaymentAttempt::where('reference', $reference)->with('order')->lockForUpdate()->firstOrFail();
            $status = (string) data_get($payload, 'status', 'pending');

            if (in_array($attempt->status, ['confirmed', 'failed'], true)) {
                return $attempt;
            }

            if (in_array($status, ['paid', 'confirmed', 'success', 'succeeded'], true)) {
                $attempt->update([
                    'status' => 'confirmed',
                    'confirmed_at' => now(),
                    'response_payload' => array_merge($attempt->response_payload ?? [], ['webhook' => $payload]),
                ]);

                $attempt->order->update([
                    'payment_status' => 'paid',
                    'paid_at' => now(),
                ]);

                $attempt->order->timelines()->create([
                    'event' => 'payment_confirmed',
                    'title' => 'تم تأكيد الدفع عبر بوابة الإنتاج',
                    'note' => $attempt->reference,
                ]);

                $this->rewards->apply($attempt->order);
                $this->notifications->paymentConfirmed($attempt->order);

                return $attempt;
            }

            if (in_array($status, ['failed', 'declined', 'cancelled', 'canceled'], true)) {
                $attempt->update([
                    'status' => 'failed',
                    'failed_at' => now(),
                    'response_payload' => array_merge($attempt->response_payload ?? [], ['webhook' => $payload]),
                ]);

                $attempt->order->update(['payment_status' => 'failed']);
                $attempt->order->timelines()->create([
                    'event' => 'payment_failed',
                    'title' => 'فشل الدفع عبر بوابة الإنتاج',
                    'note' => $attempt->reference,
                ]);

                $this->notifications->paymentFailed($attempt->order);
            }

            return $attempt;
        });
    }

    public function verifyWebhook(array $payload, ?string $signature): bool
    {
        return $this->signature->verify($payload, config('raseednow.payments.generic_http.webhook_secret'), $signature);
    }

    private function sendInitiationRequest(array $payload): array
    {
        $endpoint = config('raseednow.payments.generic_http.endpoint');

        if (blank($endpoint)) {
            throw new RuntimeException('Payment endpoint is not configured.');
        }

        $signature = $this->signature->sign($payload, config('raseednow.payments.generic_http.api_secret'));

        try {
            $response = Http::timeout(config('raseednow.payments.generic_http.timeout'))
                ->withHeaders(array_filter([
                    'Accept' => 'application/json',
                    'X-Api-Key' => config('raseednow.payments.generic_http.api_key'),
                    'X-Signature' => $signature,
                ]))
                ->post($endpoint, $payload);
        } catch (Throwable $exception) {
            throw new RuntimeException('Payment provider request failed: '.$exception->getMessage());
        }

        if (! $response->successful()) {
            throw new RuntimeException('Payment provider rejected the request.');
        }

        return $response->json() ?: ['status' => 'pending'];
    }
}
