<?php

namespace App\Jobs;

use App\Models\User;
use App\Services\PaymentMethodService;
use App\Services\ReservedAccountProvisioningService;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;

class ProvisionMonnifyReservedAccountJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public int $tries = 3;

    public int $backoff = 30;

    public function __construct(
        public int $userId,
        public string $source = 'registration'
    ) {
    }

    public function handle(
        ReservedAccountProvisioningService $provisioningService,
        PaymentMethodService $paymentMethodService
    ): void
    {
        $user = User::query()->find($this->userId);

        if (! $user) {
            return;
        }

        $this->provisionMonnifyIfEligible($user, $provisioningService, $paymentMethodService);
        $this->provisionPaymentPointIfEligible($user, $provisioningService, $paymentMethodService);
    }

    public function failed(\Throwable $exception): void
    {
        Log::warning('Monnify reserved account auto-provisioning failed', [
            'user_id' => $this->userId,
            'source' => $this->source,
            'error' => $exception->getMessage(),
        ]);
    }

    protected function provisionMonnifyIfEligible(
        User $user,
        ReservedAccountProvisioningService $provisioningService,
        PaymentMethodService $paymentMethodService
    ): void {
        if (! $paymentMethodService->isEnabled(PaymentMethodService::METHOD_MONNIFY_RESERVED)) {
            Log::info('Skipping Monnify auto-provisioning because method is disabled by admin', [
                'user_id' => $this->userId,
                'source' => $this->source,
            ]);

            return;
        }

        if (! $provisioningService->monnifyConfigured()) {
            Log::info('Skipping Monnify auto-provisioning because Monnify is not configured', [
                'user_id' => $this->userId,
                'source' => $this->source,
            ]);

            return;
        }

        if ($paymentMethodService->requireBvnForMonnify() && ! $paymentMethodService->userHasValidBvn($user)) {
            Log::info('Skipping Monnify auto-provisioning because BVN is required but missing', [
                'user_id' => $this->userId,
                'source' => $this->source,
            ]);

            return;
        }

        $existing = $user->reservedAccounts()
            ->where('provider', 'monnify')
            ->where('status', 'active')
            ->first();

        if ($existing && ! empty($existing->accounts_json)) {
            return;
        }

        try {
            $provisioningService->provisionMonnifyForUser($user);

            Log::info('Monnify reserved account auto-provisioned', [
                'user_id' => $this->userId,
                'source' => $this->source,
            ]);
        } catch (\Throwable $exception) {
            Log::warning('Monnify reserved account auto-provisioning attempt failed', [
                'user_id' => $this->userId,
                'source' => $this->source,
                'error' => $exception->getMessage(),
            ]);
        }
    }

    protected function provisionPaymentPointIfEligible(
        User $user,
        ReservedAccountProvisioningService $provisioningService,
        PaymentMethodService $paymentMethodService
    ): void {
        if (! $paymentMethodService->isEnabled(PaymentMethodService::METHOD_PAYMENTPOINT_RESERVED)) {
            Log::info('Skipping PaymentPoint auto-provisioning because method is disabled by admin', [
                'user_id' => $this->userId,
                'source' => $this->source,
            ]);

            return;
        }

        if (! $provisioningService->paymentpointConfigured()) {
            Log::info('Skipping PaymentPoint auto-provisioning because PaymentPoint is not configured', [
                'user_id' => $this->userId,
                'source' => $this->source,
            ]);

            return;
        }

        if (! $paymentMethodService->userHasValidPhone($user)) {
            Log::info('Skipping PaymentPoint auto-provisioning because user phone is missing/invalid', [
                'user_id' => $this->userId,
                'source' => $this->source,
            ]);

            return;
        }

        $existing = $user->reservedAccounts()
            ->where('provider', 'paymentpoint')
            ->where('status', 'active')
            ->first();

        if ($existing && ! empty($existing->accounts_json)) {
            return;
        }

        try {
            $provisioningService->provisionPaymentPointForUser($user);

            Log::info('PaymentPoint reserved account auto-provisioned', [
                'user_id' => $this->userId,
                'source' => $this->source,
            ]);
        } catch (\Throwable $exception) {
            Log::warning('PaymentPoint reserved account auto-provisioning attempt failed', [
                'user_id' => $this->userId,
                'source' => $this->source,
                'error' => $exception->getMessage(),
            ]);
        }
    }
}
