<?php

namespace App\Services;

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

class MockPaymentGatewayService
{
    public function __construct(private NotificationService $notifications, private OrderRewardService $rewards) {}

    public function initiate(Order $order): PaymentAttempt
    {
        if ($order->payment_method !== 'mock_gateway') {
            throw new RuntimeException('This order does not use the mock 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()
                ->whereIn('status', ['initiated', 'pending'])
                ->latest()
                ->first();

            if ($existing) {
                return $existing;
            }

            return $order->paymentAttempts()->create([
                'gateway' => 'mock_gateway',
                'reference' => 'PAY-'.now()->format('ymd').'-'.Str::upper(Str::random(8)),
                'status' => 'pending',
                'amount_minor' => $order->total_minor,
                'currency_code' => $order->currency_code,
                'request_payload' => [
                    'order_number' => $order->order_number,
                    'customer_email' => $order->customer_email,
                    'amount_minor' => $order->total_minor,
                ],
            ]);
        });
    }

    public function confirm(PaymentAttempt $attempt): Order
    {
        return DB::transaction(function () use ($attempt) {
            $attempt = PaymentAttempt::whereKey($attempt->id)->with('order')->lockForUpdate()->firstOrFail();

            if ($attempt->status === 'confirmed') {
                return $attempt->order;
            }

            if ($attempt->status === 'failed') {
                throw new RuntimeException('Failed payment attempts cannot be confirmed.');
            }

            $attempt->update([
                'status' => 'confirmed',
                'confirmed_at' => now(),
                'response_payload' => [
                    'mock_result' => 'approved',
                    'authorization_code' => 'AUTH-'.Str::upper(Str::random(6)),
                ],
            ]);

            $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->order;
        });
    }

    public function fail(PaymentAttempt $attempt): Order
    {
        return DB::transaction(function () use ($attempt) {
            $attempt = PaymentAttempt::whereKey($attempt->id)->with('order')->lockForUpdate()->firstOrFail();

            if ($attempt->status === 'confirmed') {
                throw new RuntimeException('Confirmed payment attempts cannot be failed.');
            }

            $attempt->update([
                'status' => 'failed',
                'failed_at' => now(),
                'response_payload' => [
                    'mock_result' => 'declined',
                    'decline_reason' => 'Mock gateway declined by QA action.',
                ],
            ]);

            $attempt->order->update(['payment_status' => 'failed']);

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

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

            return $attempt->order;
        });
    }
}
