<?php

namespace App\Modules\Transactions\Jobs;

use App\Core\Traits\GeneratesReference;
use App\Models\Transaction;
use App\Modules\ProviderIntegration\Interfaces\ProviderInterface;
use App\Modules\Transactions\Interfaces\TransactionServiceInterface;
use App\Modules\Wallet\Interfaces\WalletServiceInterface;
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\DB;
use Illuminate\Support\Facades\Log;

class ProcessBillPurchase implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, GeneratesReference;

    public int $tries = 3;
    public int $timeout = 60;

    public function __construct(
        public string $reference,
        public string $type,
        public array $params
    ) {}

    public function handle(
        ProviderInterface $provider,
        TransactionServiceInterface $transactionService,
        WalletServiceInterface $walletService
    ): void {
        $transaction = DB::transaction(function () {
            return Transaction::where('reference', $this->reference)
                ->lockForUpdate()
                ->first();
        });

        if (!$transaction || $transaction->status !== 'processing') {
            Log::info('Skipping bill purchase - transaction not in processing state', ['reference' => $this->reference]);
            return;
        }

        try {
            $response = match ($this->type) {
                'airtime' => $provider->purchaseAirtime(
                    $this->params['phone'],
                    $this->params['network'],
                    $this->params['amount'],
                    $this->generateVTPassRequestId()
                ),
                'data' => $provider->purchaseData(
                    $this->params['phone'],
                    $this->params['network'],
                    $this->params['variation_code'],
                    $this->generateVTPassRequestId()
                ),
                'cable' => $provider->purchaseCable(
                    $this->params['smartcard_number'],
                    $this->params['service_id'] ?? '.dstv',
                    $this->params['variation_code'],
                    $this->params['subscription_type'] ?? 'renew',
                    $this->params['amount'],
                    $this->params['phone'] ?? '',
                    $this->generateVTPassRequestId()
                ),
                'electricity' => $provider->purchaseElectricity(
                    $this->params['meter_number'],
                    $this->params['service_id'] ?? 'ikeja-electric',
                    $this->params['variation_code'],
                    $this->params['amount'],
                    $this->params['phone'] ?? '',
                    $this->generateVTPassRequestId()
                ),
                'exam_pin' => $provider->purchaseExamPin(
                    $this->params['service_id'] ?? '',
                    $this->params['variation_code'],
                    $this->params['billers_code'] ?? 'N/A',
                    $this->params['phone'] ?? '',
                    $this->params['quantity'],
                    $this->generateVTPassRequestId()
                ),
            };

            if (isset($response['code']) && $response['code'] === '000') {
                $tokenData = null;

                if ($this->type === 'exam_pin') {
                    $tokenData = [
                        'tokens' => $response['tokens'] ?? ($response['purchased_code'] ? [$response['purchased_code']] : []),
                        'cards' => $response['cards'] ?? [],
                        'pin' => $response['Pin'] ?? $response['purchased_code'] ?? null,
                        'response_description' => $response['response_description'] ?? null,
                        'request_id' => $response['requestId'] ?? null,
                        'amount' => $response['amount'] ?? null,
                        'transaction_date' => $response['transaction_date'] ?? null,
                    ];
                } elseif ($this->type === 'electricity') {
                    $tokenData = [
                        'token' => $response['mainToken'] ?? $response['purchased_code'] ?? null,
                        'exchange_reference' => $response['exchangeReference'] ?? null,
                        'response_description' => $response['response_description'] ?? null,
                        'request_id' => $response['requestId'] ?? null,
                        'amount' => $response['amount'] ?? null,
                        'transaction_date' => $response['transaction_date'] ?? null,
                    ];
                }

                $transactionService->markSuccessful($this->reference, $response, $tokenData);
            } else {
                $reason = $response['response_description'] ?? 'Purchase failed';
                $transactionService->markFailed($this->reference, $reason, $response);
                $walletService->refundWallet(
                    $transaction->user_id,
                    (float) $transaction->total_amount,
                    "Refund for failed {$this->type} purchase {$this->reference}",
                    self::generateReference('RFD'),
                    'transaction',
                    $transaction->id
                );
            }
        } catch (\Exception $e) {
            Log::error('Bill purchase failed', ['reference' => $this->reference, 'error' => $e->getMessage()]);

            if ($this->attempts() >= $this->tries) {
                $transactionService->markFailed($this->reference, $e->getMessage(), []);
                $walletService->refundWallet(
                    $transaction->user_id,
                    (float) $transaction->total_amount,
                    "Refund for failed {$this->type} purchase {$this->reference}",
                    self::generateReference('RFD'),
                    'transaction',
                    $transaction->id
                );
            } else {
                $this->release(now()->addMinutes(2));
            }
        }
    }
}
