<?php

namespace App\Services;

use App\Models\GatewayPayment;
use App\Models\Tenant;
use App\Models\TenantPayment;
use Illuminate\Support\Facades\Http;

class PaymentGatewayService
{
    public function __construct(private readonly PlatformSettings $settings)
    {
    }

    public function provider(?Tenant $tenant = null): string
    {
        if ($tenant) {
            return $this->normalizeProvider($tenant->settings['payment_gateway_provider'] ?? 'none');
        }

        return $this->normalizeProvider($this->settings->paymentGatewayProvider());
    }

    public function supportsHostedCheckout(?Tenant $tenant = null): bool
    {
        return in_array($this->provider($tenant), ['paystack', 'monnify', 'flutterwave'], true);
    }

    /**
     * Start a hosted checkout session for tenant subscriptions, savings deposits,
     * or loan repayments.
     *
     * @return array{successful: bool, authorization_url: string|null, access_code: string|null, message: string}
     */
    public function initializeCheckout(GatewayPayment $payment, string $email, string $callbackUrl): array
    {
        $gateway = $this->gatewaySettingsForPayment($payment);

        return match ($gateway['provider']) {
            'paystack' => $this->initializePaystackTransaction($payment, $email, $callbackUrl, $gateway),
            'monnify' => $this->initializeMonnifyTransaction($payment, $email, $callbackUrl, $gateway),
            'flutterwave' => $this->initializeFlutterwaveTransaction($payment, $email, $callbackUrl, $gateway),
            'providus_bank', 'wema_bank' => [
                'successful' => false,
                'authorization_url' => null,
                'access_code' => null,
                'message' => 'This bank provider is for direct bank or virtual account collection and does not support hosted checkout here. Choose Paystack, Monnify, or Flutterwave for online checkout.',
            ],
            default => [
                'successful' => false,
                'authorization_url' => null,
                'access_code' => null,
                'message' => 'Payment gateway is not configured.',
            ],
        };
    }

    /**
     * Verify a hosted checkout payment before delivering value.
     *
     * @return array{successful: bool, reference: string|null, authorization_code: string|null, amount: float|null, raw: array<string, mixed>, message: string}
     */
    public function verifyCheckout(string $reference): array
    {
        $payment = GatewayPayment::withoutGlobalScopes()->with('tenant')->where('reference', $reference)->first();
        $gateway = $this->gatewaySettingsForPayment($payment);

        return match ($gateway['provider']) {
            'paystack' => $this->verifyPaystackTransaction($reference, $gateway),
            'monnify' => $payment ? $this->verifyMonnifyTransaction($payment, $gateway) : $this->failedVerification('Payment record was not found.'),
            'flutterwave' => $this->verifyFlutterwaveTransaction($reference, $gateway),
            default => [
                'successful' => false,
                'reference' => null,
                'authorization_code' => null,
                'amount' => null,
                'raw' => [],
                'message' => 'Payment gateway is not configured.',
            ],
        };
    }

    /**
     * Charge a stored authorization reference for an automatic subscription renewal.
     *
     * The app stores gateway authorization codes or bank mandate references only.
     * Raw card details must stay with the PCI-compliant payment provider.
     *
     * @return array{successful: bool, reference: string|null, message: string}
     */
    public function chargeAutoRenewal(Tenant $tenant, TenantPayment $payment): array
    {
        [$provider, $reference] = $this->autoBillingProviderAndReference($tenant);

        if (! $reference) {
            return [
                'successful' => false,
                'reference' => null,
                'message' => 'Automatic deduction authorization reference is missing.',
            ];
        }

        $payment->forceFill(['gateway_reference' => $reference])->save();
        $gateway = $this->platformGatewaySettings($provider);

        return match ($provider) {
            'paystack' => $this->chargePaystackAuthorization($tenant, $payment, $reference, $gateway),
            'monnify' => $this->chargeMonnifyCardToken($tenant, $payment, $reference, $gateway),
            'flutterwave' => $this->chargeFlutterwaveToken($tenant, $payment, $reference, $gateway),
            default => [
                'successful' => false,
                'reference' => null,
                'message' => 'Automatic deduction is available for Paystack, Monnify card tokens, and Flutterwave tokenized cards only.',
            ],
        };
    }

    /**
     * @return array{successful: bool, authorization_url: string|null, access_code: string|null, message: string}
     */
    private function initializePaystackTransaction(GatewayPayment $payment, string $email, string $callbackUrl, array $gateway): array
    {
        $secretKey = $gateway['paystack_secret_key'];

        if (! $secretKey) {
            return [
                'successful' => false,
                'authorization_url' => null,
                'access_code' => null,
                'message' => 'Paystack secret key is missing.',
            ];
        }

        $baseUrl = $gateway['paystack_base_url'];
        $response = Http::withToken($secretKey)
            ->acceptJson()
            ->timeout(30)
            ->post($baseUrl.'/transaction/initialize', [
                'email' => $email,
                'amount' => (int) round(((float) $payment->amount) * 100),
                'currency' => $payment->currency,
                'reference' => $payment->reference,
                'callback_url' => $callbackUrl,
                'metadata' => [
                    'tenant_id' => $payment->tenant_id,
                    'customer_id' => $payment->customer_id,
                    'purpose' => $payment->purpose,
                    'payment_id' => $payment->id,
                ],
            ]);

        if (! $response->successful() || $response->json('status') !== true) {
            return [
                'successful' => false,
                'authorization_url' => null,
                'access_code' => null,
                'message' => $response->json('message') ?: 'Unable to initialize payment.',
            ];
        }

        return [
            'successful' => true,
            'authorization_url' => $response->json('data.authorization_url'),
            'access_code' => $response->json('data.access_code'),
            'message' => 'Payment initialized.',
        ];
    }

    /**
     * @return array{successful: bool, authorization_url: string|null, access_code: string|null, message: string}
     */
    private function initializeFlutterwaveTransaction(GatewayPayment $payment, string $email, string $callbackUrl, array $gateway): array
    {
        $secretKey = $gateway['flutterwave_secret_key'];

        if (! $secretKey) {
            return [
                'successful' => false,
                'authorization_url' => null,
                'access_code' => null,
                'message' => 'Flutterwave secret key is missing.',
            ];
        }

        $customer = $payment->customer;
        $response = Http::withToken($secretKey)
            ->acceptJson()
            ->timeout(30)
            ->post($gateway['flutterwave_base_url'].'/payments', [
                'tx_ref' => $payment->reference,
                'amount' => (float) $payment->amount,
                'currency' => $payment->currency,
                'redirect_url' => $this->callbackUrlWithReference($callbackUrl, $payment->reference),
                'payment_options' => $payment->purpose === GatewayPayment::PURPOSE_TENANT_SUBSCRIPTION
                    ? 'card'
                    : 'card,banktransfer,ussd',
                'customer' => [
                    'email' => $email,
                    'name' => $customer?->fullName() ?: $payment->tenant?->name ?: $email,
                    'phonenumber' => $customer?->phone,
                ],
                'customizations' => [
                    'title' => $payment->tenant?->name ?: config('app.name'),
                    'description' => ucfirst(str_replace('_', ' ', $payment->purpose)),
                ],
                'meta' => [
                    'tenant_id' => $payment->tenant_id,
                    'customer_id' => $payment->customer_id,
                    'purpose' => $payment->purpose,
                    'payment_id' => $payment->id,
                ],
            ]);

        if (! $response->successful() || $response->json('status') !== 'success') {
            return [
                'successful' => false,
                'authorization_url' => null,
                'access_code' => null,
                'message' => $response->json('message') ?: 'Unable to initialize Flutterwave payment.',
            ];
        }

        return [
            'successful' => true,
            'authorization_url' => $response->json('data.link'),
            'access_code' => $payment->reference,
            'message' => 'Payment initialized.',
        ];
    }

    /**
     * @return array{successful: bool, authorization_url: string|null, access_code: string|null, message: string}
     */
    private function initializeMonnifyTransaction(GatewayPayment $payment, string $email, string $callbackUrl, array $gateway): array
    {
        if (! $gateway['monnify_contract_code']) {
            return [
                'successful' => false,
                'authorization_url' => null,
                'access_code' => null,
                'message' => 'Monnify contract code is missing.',
            ];
        }

        $token = $this->monnifyAccessToken($gateway);

        if (! $token) {
            return [
                'successful' => false,
                'authorization_url' => null,
                'access_code' => null,
                'message' => 'Unable to authenticate with Monnify. Check API key and secret key.',
            ];
        }

        $customer = $payment->customer;
        $response = Http::withToken($token)
            ->acceptJson()
            ->timeout(30)
            ->post($gateway['monnify_base_url'].'/api/v1/merchant/transactions/init-transaction', [
                'amount' => (float) $payment->amount,
                'customerName' => $customer?->fullName() ?: $payment->tenant?->name ?: $email,
                'customerEmail' => $email,
                'paymentReference' => $payment->reference,
                'paymentDescription' => ucfirst(str_replace('_', ' ', $payment->purpose)),
                'currencyCode' => $payment->currency,
                'contractCode' => $gateway['monnify_contract_code'],
                'redirectUrl' => $this->callbackUrlWithReference($callbackUrl, $payment->reference),
                'paymentMethods' => $payment->purpose === GatewayPayment::PURPOSE_TENANT_SUBSCRIPTION
                    ? ['CARD']
                    : ['CARD', 'ACCOUNT_TRANSFER'],
                'tokeniseCard' => $payment->purpose === GatewayPayment::PURPOSE_TENANT_SUBSCRIPTION,
                'metadata' => [
                    'tenant_id' => $payment->tenant_id,
                    'customer_id' => $payment->customer_id,
                    'purpose' => $payment->purpose,
                    'payment_id' => $payment->id,
                ],
            ]);

        $body = $response->json('responseBody', []);

        if (! $response->successful() || $response->json('requestSuccessful') !== true || blank($body['checkoutUrl'] ?? null)) {
            return [
                'successful' => false,
                'authorization_url' => null,
                'access_code' => null,
                'message' => $response->json('responseMessage') ?: 'Unable to initialize Monnify payment.',
            ];
        }

        $payment->forceFill([
            'provider_reference' => $body['transactionReference'] ?? null,
            'gateway_response' => $response->json() ?: [],
        ])->save();

        return [
            'successful' => true,
            'authorization_url' => $body['checkoutUrl'],
            'access_code' => $body['transactionReference'] ?? $payment->reference,
            'message' => 'Payment initialized.',
        ];
    }

    /**
     * @return array{successful: bool, reference: string|null, authorization_code: string|null, amount: float|null, raw: array<string, mixed>, message: string}
     */
    private function verifyPaystackTransaction(string $reference, array $gateway): array
    {
        $secretKey = $gateway['paystack_secret_key'];

        if (! $secretKey) {
            return [
                'successful' => false,
                'reference' => null,
                'authorization_code' => null,
                'amount' => null,
                'raw' => [],
                'message' => 'Paystack secret key is missing.',
            ];
        }

        $baseUrl = $gateway['paystack_base_url'];
        $response = Http::withToken($secretKey)
            ->acceptJson()
            ->timeout(30)
            ->get($baseUrl.'/transaction/verify/'.urlencode($reference));

        if (! $response->successful()) {
            return [
                'successful' => false,
                'reference' => null,
                'authorization_code' => null,
                'amount' => null,
                'raw' => $response->json() ?: [],
                'message' => $response->json('message') ?: 'Unable to verify payment.',
            ];
        }

        $data = $response->json('data', []);
        $successful = $response->json('status') === true && ($data['status'] ?? null) === 'success';
        $authorization = $data['authorization'] ?? [];
        $authorizationCode = is_array($authorization)
            && filled($authorization['authorization_code'] ?? null)
            && filter_var($authorization['reusable'] ?? false, FILTER_VALIDATE_BOOLEAN)
                ? (string) $authorization['authorization_code']
                : null;

        return [
            'successful' => $successful,
            'reference' => (string) ($data['reference'] ?? $reference),
            'authorization_code' => $authorizationCode,
            'amount' => isset($data['amount']) ? ((float) $data['amount']) / 100 : null,
            'raw' => $response->json() ?: [],
            'message' => $successful ? 'Payment verified.' : ($response->json('message') ?: 'Payment was not successful.'),
        ];
    }

    /**
     * @return array{successful: bool, reference: string|null, authorization_code: string|null, amount: float|null, raw: array<string, mixed>, message: string}
     */
    private function verifyFlutterwaveTransaction(string $reference, array $gateway): array
    {
        $secretKey = $gateway['flutterwave_secret_key'];

        if (! $secretKey) {
            return $this->failedVerification('Flutterwave secret key is missing.');
        }

        $response = Http::withToken($secretKey)
            ->acceptJson()
            ->timeout(30)
            ->get($gateway['flutterwave_base_url'].'/transactions/verify_by_reference', [
                'tx_ref' => $reference,
            ]);

        if (! $response->successful()) {
            return [
                'successful' => false,
                'reference' => null,
                'authorization_code' => null,
                'amount' => null,
                'raw' => $response->json() ?: [],
                'message' => $response->json('message') ?: 'Unable to verify Flutterwave payment.',
            ];
        }

        $data = $response->json('data', []);
        $successful = $response->json('status') === 'success' && ($data['status'] ?? null) === 'successful';
        $authorizationCode = $data['card']['token'] ?? $data['token'] ?? null;

        return [
            'successful' => $successful,
            'reference' => (string) ($data['tx_ref'] ?? $reference),
            'authorization_code' => filled($authorizationCode) ? (string) $authorizationCode : null,
            'amount' => isset($data['amount']) ? (float) $data['amount'] : null,
            'raw' => $response->json() ?: [],
            'message' => $successful ? 'Payment verified.' : ($response->json('message') ?: 'Payment was not successful.'),
        ];
    }

    /**
     * @return array{successful: bool, reference: string|null, authorization_code: string|null, amount: float|null, raw: array<string, mixed>, message: string}
     */
    private function verifyMonnifyTransaction(GatewayPayment $payment, array $gateway): array
    {
        $token = $this->monnifyAccessToken($gateway);

        if (! $token) {
            return $this->failedVerification('Unable to authenticate with Monnify. Check API key and secret key.');
        }

        $transactionReference = $payment->provider_reference ?: $payment->reference;
        $response = Http::withToken($token)
            ->acceptJson()
            ->timeout(30)
            ->get($gateway['monnify_base_url'].'/api/v2/transactions/'.urlencode($transactionReference));

        if (! $response->successful()) {
            return [
                'successful' => false,
                'reference' => null,
                'authorization_code' => null,
                'amount' => null,
                'raw' => $response->json() ?: [],
                'message' => $response->json('responseMessage') ?: 'Unable to verify Monnify payment.',
            ];
        }

        $body = $response->json('responseBody', []);
        $status = strtoupper((string) ($body['paymentStatus'] ?? $body['status'] ?? ''));
        $successful = $response->json('requestSuccessful') === true && in_array($status, ['PAID', 'SUCCESS', 'SUCCESSFUL'], true);
        $cardDetails = $body['cardDetails'] ?? [];
        $cardToken = is_array($cardDetails) && filled($cardDetails['cardToken'] ?? null)
            && filter_var($cardDetails['reusable'] ?? false, FILTER_VALIDATE_BOOLEAN)
            ? (string) $cardDetails['cardToken']
            : null;

        return [
            'successful' => $successful,
            'reference' => (string) ($body['paymentReference'] ?? $payment->reference),
            'authorization_code' => $cardToken,
            'amount' => isset($body['amountPaid']) ? (float) $body['amountPaid'] : (isset($body['amount']) ? (float) $body['amount'] : null),
            'raw' => $response->json() ?: [],
            'message' => $successful ? 'Payment verified.' : ($response->json('responseMessage') ?: 'Payment was not successful.'),
        ];
    }

    /**
     * Charge a Paystack authorization code such as AUTH_xxxxx.
     *
     * This requires Paystack to be configured in Super Admin > Settings.
     *
     * @return array{successful: bool, reference: string|null, message: string}
     */
    private function chargePaystackAuthorization(Tenant $tenant, TenantPayment $payment, string $authorizationCode, array $gateway): array
    {
        $secretKey = $gateway['paystack_secret_key'];

        if (! $secretKey) {
            return [
                'successful' => false,
                'reference' => null,
                'message' => 'Paystack secret key is missing.',
            ];
        }

        $baseUrl = $gateway['paystack_base_url'];
        $response = Http::withToken($secretKey)
            ->acceptJson()
            ->timeout(30)
            ->post($baseUrl.'/transaction/charge_authorization', [
                'authorization_code' => $authorizationCode,
                'email' => $tenant->email,
                'amount' => (int) round(((float) $payment->amount) * 100),
                'reference' => $payment->reference,
                'metadata' => [
                    'tenant_id' => $tenant->id,
                    'tenant_name' => $tenant->name,
                    'billing_cycle' => $payment->billing_cycle,
                    'payment_id' => $payment->id,
                ],
            ]);

        if (! $response->successful()) {
            return [
                'successful' => false,
                'reference' => null,
                'message' => $response->json('message') ?: 'Payment gateway request failed.',
            ];
        }

        $data = $response->json('data', []);

        if (($response->json('status') === true) && (($data['status'] ?? null) === 'success')) {
            return [
                'successful' => true,
                'reference' => (string) ($data['reference'] ?? $payment->reference),
                'message' => 'Payment gateway charge succeeded.',
            ];
        }

        return [
            'successful' => false,
            'reference' => (string) ($data['reference'] ?? ''),
            'message' => $response->json('message') ?: 'Payment gateway did not confirm a successful charge.',
        ];
    }

    /**
     * @return array{successful: bool, reference: string|null, message: string}
     */
    private function chargeMonnifyCardToken(Tenant $tenant, TenantPayment $payment, string $cardToken, array $gateway): array
    {
        $token = $this->monnifyAccessToken($gateway);

        if (! $token) {
            return [
                'successful' => false,
                'reference' => null,
                'message' => 'Unable to authenticate with Monnify. Check API key and secret key.',
            ];
        }

        $response = Http::withToken($token)
            ->acceptJson()
            ->timeout(30)
            ->post($gateway['monnify_base_url'].'/api/v1/merchant/cards/charge-card-token', [
                'cardToken' => $cardToken,
                'amount' => (float) $payment->amount,
                'customerName' => $tenant->owner_name ?: $tenant->name,
                'customerEmail' => $tenant->email,
                'paymentReference' => $payment->reference,
                'paymentDescription' => 'Automatic '.$payment->billing_cycle.' subscription renewal for '.$tenant->name,
                'currencyCode' => $tenant->settings['currency'] ?? 'NGN',
            ]);

        $body = $response->json('responseBody', []);
        $status = strtoupper((string) ($body['paymentStatus'] ?? $body['transactionStatus'] ?? $body['status'] ?? ''));
        $successful = $response->successful()
            && $response->json('requestSuccessful') === true
            && in_array($status, ['PAID', 'SUCCESS', 'SUCCESSFUL'], true);

        if ($successful) {
            return [
                'successful' => true,
                'reference' => (string) ($body['paymentReference'] ?? $body['transactionReference'] ?? $payment->reference),
                'message' => 'Monnify automatic card charge succeeded.',
            ];
        }

        return [
            'successful' => false,
            'reference' => (string) ($body['paymentReference'] ?? ''),
            'message' => $response->json('responseMessage') ?: $response->json('message') ?: 'Monnify automatic card charge failed.',
        ];
    }

    /**
     * @return array{successful: bool, reference: string|null, message: string}
     */
    private function chargeFlutterwaveToken(Tenant $tenant, TenantPayment $payment, string $cardToken, array $gateway): array
    {
        $secretKey = $gateway['flutterwave_secret_key'];

        if (! $secretKey) {
            return [
                'successful' => false,
                'reference' => null,
                'message' => 'Flutterwave secret key is missing.',
            ];
        }

        $response = Http::withToken($secretKey)
            ->acceptJson()
            ->timeout(30)
            ->post($gateway['flutterwave_base_url'].'/tokenized-charges', [
                'token' => $cardToken,
                'email' => $tenant->email,
                'currency' => $tenant->settings['currency'] ?? 'NGN',
                'country' => 'NG',
                'amount' => (float) $payment->amount,
                'tx_ref' => $payment->reference,
                'narration' => 'Automatic '.$payment->billing_cycle.' subscription renewal for '.$tenant->name,
            ]);

        $data = $response->json('data', []);
        $successful = $response->successful()
            && $response->json('status') === 'success'
            && in_array(strtolower((string) ($data['status'] ?? '')), ['successful', 'success'], true);

        if ($successful) {
            return [
                'successful' => true,
                'reference' => (string) ($data['tx_ref'] ?? $payment->reference),
                'message' => 'Flutterwave automatic card charge succeeded.',
            ];
        }

        return [
            'successful' => false,
            'reference' => (string) ($data['tx_ref'] ?? ''),
            'message' => $response->json('message') ?: 'Flutterwave automatic card charge failed.',
        ];
    }

    private function gatewaySettingsForPayment(?GatewayPayment $payment): array
    {
        $tenant = in_array($payment?->purpose, [GatewayPayment::PURPOSE_SAVINGS_DEPOSIT, GatewayPayment::PURPOSE_LOAN_REPAYMENT], true)
            ? $payment?->tenant
            : null;

        if ($tenant) {
            return [
                'provider' => $this->normalizeProvider($payment?->provider ?: $this->provider($tenant)),
                'paystack_secret_key' => filled($tenant->settings['paystack_secret_key'] ?? null)
                    ? (string) $tenant->settings['paystack_secret_key']
                    : null,
                'paystack_base_url' => rtrim((string) ($tenant->settings['paystack_base_url'] ?? 'https://api.paystack.co'), '/'),
                'monnify_api_key' => filled($tenant->settings['monnify_api_key'] ?? null) ? (string) $tenant->settings['monnify_api_key'] : null,
                'monnify_secret_key' => filled($tenant->settings['monnify_secret_key'] ?? null) ? (string) $tenant->settings['monnify_secret_key'] : null,
                'monnify_contract_code' => filled($tenant->settings['monnify_contract_code'] ?? null) ? (string) $tenant->settings['monnify_contract_code'] : null,
                'monnify_base_url' => rtrim((string) ($tenant->settings['monnify_base_url'] ?? 'https://api.monnify.com'), '/'),
                'flutterwave_secret_key' => filled($tenant->settings['flutterwave_secret_key'] ?? null) ? (string) $tenant->settings['flutterwave_secret_key'] : null,
                'flutterwave_base_url' => rtrim((string) ($tenant->settings['flutterwave_base_url'] ?? 'https://api.flutterwave.com/v3'), '/'),
                'providus_client_id' => filled($tenant->settings['providus_client_id'] ?? null) ? (string) $tenant->settings['providus_client_id'] : null,
                'providus_client_secret' => filled($tenant->settings['providus_client_secret'] ?? null) ? (string) $tenant->settings['providus_client_secret'] : null,
                'providus_base_url' => filled($tenant->settings['providus_base_url'] ?? null) ? rtrim((string) $tenant->settings['providus_base_url'], '/') : null,
                'wema_client_id' => filled($tenant->settings['wema_client_id'] ?? null) ? (string) $tenant->settings['wema_client_id'] : null,
                'wema_client_secret' => filled($tenant->settings['wema_client_secret'] ?? null) ? (string) $tenant->settings['wema_client_secret'] : null,
                'wema_base_url' => filled($tenant->settings['wema_base_url'] ?? null) ? rtrim((string) $tenant->settings['wema_base_url'], '/') : null,
            ];
        }

        return $this->platformGatewaySettings($payment?->provider);
    }

    /**
     * @return array<string, mixed>
     */
    private function platformGatewaySettings(?string $provider = null): array
    {
        return [
            'provider' => $this->normalizeProvider($provider ?: $this->provider()),
            'paystack_secret_key' => $this->settings->paystackSecretKey(),
            'paystack_base_url' => $this->settings->paystackBaseUrl(),
            'monnify_api_key' => $this->filledSetting('monnify_api_key', config('services.monnify.api_key')),
            'monnify_secret_key' => $this->filledSetting('monnify_secret_key', config('services.monnify.secret_key')),
            'monnify_contract_code' => $this->filledSetting('monnify_contract_code', config('services.monnify.contract_code')),
            'monnify_base_url' => rtrim((string) $this->settings->value('monnify_base_url', config('services.monnify.base_url', 'https://api.monnify.com')), '/'),
            'flutterwave_secret_key' => $this->filledSetting('flutterwave_secret_key', config('services.flutterwave.secret_key')),
            'flutterwave_base_url' => rtrim((string) $this->settings->value('flutterwave_base_url', config('services.flutterwave.base_url', 'https://api.flutterwave.com/v3')), '/'),
            'providus_client_id' => $this->filledSetting('providus_client_id', config('services.providus.client_id')),
            'providus_client_secret' => $this->filledSetting('providus_client_secret', config('services.providus.client_secret')),
            'providus_base_url' => filled($this->settings->value('providus_base_url', config('services.providus.base_url')))
                ? rtrim((string) $this->settings->value('providus_base_url', config('services.providus.base_url')), '/')
                : null,
            'wema_client_id' => $this->filledSetting('wema_client_id', config('services.wema.client_id')),
            'wema_client_secret' => $this->filledSetting('wema_client_secret', config('services.wema.client_secret')),
            'wema_base_url' => filled($this->settings->value('wema_base_url', config('services.wema.base_url')))
                ? rtrim((string) $this->settings->value('wema_base_url', config('services.wema.base_url')), '/')
                : null,
        ];
    }

    /**
     * @return array{0: string, 1: string|null}
     */
    private function autoBillingProviderAndReference(Tenant $tenant): array
    {
        $reference = (string) $tenant->auto_billing_reference;

        if (str_contains($reference, ':')) {
            [$provider, $token] = explode(':', $reference, 2);

            return [$this->normalizeProvider($provider), $token !== '' ? $token : null];
        }

        return [$this->provider(), $reference !== '' ? $reference : null];
    }

    private function filledSetting(string $key, mixed $default = null): ?string
    {
        $value = $this->settings->value($key, $default);

        return filled($value) ? (string) $value : null;
    }

    private function normalizeProvider(mixed $provider): string
    {
        return match (strtolower((string) $provider)) {
            'providus' => 'providus_bank',
            'wema' => 'wema_bank',
            default => strtolower((string) $provider),
        };
    }

    private function monnifyAccessToken(array $gateway): ?string
    {
        if (! $gateway['monnify_api_key'] || ! $gateway['monnify_secret_key']) {
            return null;
        }

        $response = Http::withBasicAuth($gateway['monnify_api_key'], $gateway['monnify_secret_key'])
            ->acceptJson()
            ->timeout(30)
            ->post($gateway['monnify_base_url'].'/api/v1/auth/login');

        if (! $response->successful() || $response->json('requestSuccessful') !== true) {
            return null;
        }

        return $response->json('responseBody.accessToken');
    }

    /**
     * @return array{successful: bool, reference: string|null, authorization_code: string|null, amount: float|null, raw: array<string, mixed>, message: string}
     */
    private function failedVerification(string $message): array
    {
        return [
            'successful' => false,
            'reference' => null,
            'authorization_code' => null,
            'amount' => null,
            'raw' => [],
            'message' => $message,
        ];
    }

    private function callbackUrlWithReference(string $callbackUrl, string $reference): string
    {
        $separator = str_contains($callbackUrl, '?') ? '&' : '?';

        return $callbackUrl.$separator.'reference='.urlencode($reference);
    }
}
