<?php

namespace App\Http\Controllers\Api\ExamPin;

use App\Core\Exceptions\InsufficientBalanceException;
use App\Core\Exceptions\ServiceException;
use App\Core\Traits\CalculatesFees;
use App\Core\Traits\GeneratesReference;
use App\Http\Controllers\Api\BaseController;
use App\Models\BillService;
use App\Models\BillVariation;
use App\Modules\ProviderIntegration\Interfaces\ProviderInterface;
use App\Modules\Transactions\Interfaces\TransactionServiceInterface;
use App\Modules\Wallet\Interfaces\WalletServiceInterface;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

class ExamPinController extends BaseController
{
    use GeneratesReference, CalculatesFees;

    public function __construct(
        protected ProviderInterface $provider,
        protected TransactionServiceInterface $transactionService,
        protected WalletServiceInterface $walletService
    ) {}

    public function types(): JsonResponse
    {
        $types = BillService::active()->category('exam_pin')->get();
        return $this->successResponse($types);
    }

    public function variations(Request $request, string $type): JsonResponse
    {
        $service = BillService::where('code', $type)->active()->firstOrFail();

        $variations = BillVariation::where('bill_service_id', $service->id)
            ->active()
            ->orderBy('sort_order')
            ->get();

        return $this->successResponse([
            'type' => $service,
            'variations' => $variations,
        ]);
    }

    public function validateProfile(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'profile_id' => ['required', 'string'],
            'service_code' => ['required', 'string'],
            'variation_code' => ['required', 'string'],
        ]);

        $service = BillService::where('code', $validated['service_code'])->active()->firstOrFail();

        try {
            $response = $this->provider->validateCustomer(
                $service->code,
                $validated['profile_id'],
                $validated['variation_code']
            );

            $content = $response['content'] ?? $response;
            $name = $content['Customer_Name'] ?? $content['customer_name'] ?? $content['name'] ?? 'Verified';

            return $this->successResponse(['customer_name' => $name]);
        } catch (\Exception $e) {
            return $this->errorResponse($e->getMessage(), 422);
        }
    }

    public function purchase(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'variation_code' => ['required', 'string'],
            'quantity' => ['required', 'integer', 'in:1'],
            'phone' => ['nullable', 'string', 'max:20'],
            'profile_id' => ['nullable', 'string'],
            'idempotency_key' => ['nullable', 'string', 'unique:transactions,idempotency_key'],
        ]);

        $user = $request->user();
        $variation = BillVariation::where('code', $validated['variation_code'])->firstOrFail();
        $service = $variation->billService;
        $amount = $validated['quantity'] * (float) $variation->amount;
        $fee = $this->calculateFee($amount, (float) $service->service_charge);
        $totalAmount = $amount + $fee;

        if (!$this->checkDailyTransactionLimit($user->id)) {
            throw new ServiceException('Daily transaction limit reached. Please try again tomorrow.', 'DAILY_LIMIT', 422);
        }

        $isJamb = $service->code === 'jamb';
        $billersCode = $isJamb ? ($validated['profile_id'] ?? '') : 'N/A';
        $customerNumber = $isJamb ? ($validated['profile_id'] ?? 'N/A') : 'N/A';

        try {
            DB::beginTransaction();

            if (!$this->walletService->canDebit($user->id, $totalAmount)) {
                throw new InsufficientBalanceException();
            }

            $reference = self::generateReference('EXM');
            $idempotencyKey = $validated['idempotency_key'] ?? (string) Str::uuid();

            $transaction = $this->transactionService->createTransaction([
                'user_id' => $user->id,
                'reference' => $reference,
                'bill_service_id' => $service->id,
                'bill_variation_id' => $variation->id,
                'type' => 'exam_pin',
                'service_name' => $service->name . ' - ' . $variation->name,
                'customer_number' => $customerNumber,
                'amount' => $amount,
                'fee' => $fee,
                'total_amount' => $totalAmount,
                'ip_address' => $request->ip(),
                'device' => $request->userAgent(),
                'idempotency_key' => $idempotencyKey,
            ]);

            $this->walletService->debitWallet(
                $user->id,
                $totalAmount,
                "Exam PIN purchase - {$service->name} ({$variation->name}) x{$validated['quantity']}",
                $reference,
                'transaction',
                $transaction->id
            );

            $this->transactionService->markProcessing($reference);

            DB::commit();

            $requestId = self::generateVTPassRequestId();

            try {
                $response = $this->provider->purchaseExamPin(
                    $service->code,
                    $validated['variation_code'],
                    $billersCode,
                    $validated['phone'] ?? $user->phone ?? '',
                    $validated['quantity'],
                    $requestId
                );

                if (isset($response['code']) && $response['code'] === '000') {
                    $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,
                    ];
                    $this->transactionService->markSuccessful($reference, $response, $tokenData);
                } else {
                    $this->transactionService->markFailed(
                        $reference,
                        $response['response_description'] ?? 'Purchase failed',
                        $response
                    );
                    $this->refundWalletOnFailure($user->id, $totalAmount, $reference);
                }
            } catch (\Exception $e) {
                $this->transactionService->markFailed($reference, $e->getMessage(), []);
                $this->refundWalletOnFailure($user->id, $totalAmount, $reference);
            }

            $transaction = $this->transactionService->getTransaction($user->id, $reference);

            return $this->successResponse($transaction, 'Exam PIN purchase submitted');
        } catch (\Exception $e) {
            DB::rollBack();
            throw $e;
        }
    }

    protected function refundWalletOnFailure(string $userId, float $amount, string $reference): void
    {
        $this->walletService->refundWallet(
            $userId,
            $amount,
            "Refund for failed exam PIN purchase {$reference}",
            'RFD-' . strtoupper(Str::random(8)),
            'transaction',
            null
        );
    }
}
