<?php

namespace App\Services;

use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Http;
use RuntimeException;
use Throwable;
use zfhassaan\Payfast\Services\Contracts\AuthenticationServiceInterface;

/**
 * Adapts zfhassaan/payfast's getToken() contract to PayFast Pakistan's
 * Ecommerce hosted-checkout GetAccessToken endpoint.
 *
 * The package's default authentication service appends "/token" to a base URL
 * and sends only lower-case direct-API fields. The hosted checkout endpoint
 * instead requires the exact GetAccessToken URL plus basket and amount fields.
 */
class PayFastHostedAuthenticationService implements AuthenticationServiceInterface
{
    public function getToken(): array
    {
        $endpoint = trim((string) config('payfast.token_url', ''));
        if ($endpoint === '') {
            $endpoint = trim((string) config('zt.payfast.token_url', ''));
        }

        if ($endpoint === '') {
            return [
                'code' => 'CONFIG_ERROR',
                'message' => 'PAYFAST_TOKEN_URL or ZT_PAYFAST_TOKEN_URL is missing.',
                'data' => [],
            ];
        }

        $basketId = trim((string) config('payfast.runtime.basket_id', ''));
        $amount = trim((string) config('payfast.runtime.amount', ''));
        $currency = strtoupper(trim((string) config('payfast.runtime.currency', 'PKR')));

        if ($basketId === '' || $amount === '') {
            return [
                'code' => 'CONTEXT_ERROR',
                'message' => 'PayFast runtime basket ID or amount is missing.',
                'data' => [],
            ];
        }

        $payload = [
            'MERCHANT_ID' => trim((string) config('payfast.merchant_id', '')),
            'SECURED_KEY' => trim((string) config('payfast.secured_key', '')),
            'BASKET_ID' => $basketId,
            'TXNAMT' => $amount,
            'currency_code' => $currency,
            'CURRENCY_CODE' => $currency,
        ];

        try {
            $response = Http::asForm()
                ->connectTimeout((int) config('zt.payfast.connect_timeout', 8))
                ->timeout((int) config('zt.payfast.timeout', 20))
                ->post($endpoint, $payload);
        } catch (Throwable $exception) {
            return [
                'code' => 'CONNECTION_ERROR',
                'message' => 'PayFast token request could not connect: '.$exception->getMessage(),
                'data' => [],
            ];
        }

        $decoded = $response->json();

        if (! $response->successful()) {
            return [
                'code' => (string) $response->status(),
                'message' => 'PayFast token request failed with HTTP status '.$response->status().'.',
                'data' => is_array($decoded) ? $decoded : ['raw' => $response->body()],
            ];
        }

        if (! is_array($decoded)) {
            return [
                'code' => 'INVALID_JSON',
                'message' => 'PayFast token response was not valid JSON.',
                'data' => ['raw' => $response->body()],
            ];
        }

        $token = '';
        foreach (['ACCESS_TOKEN', 'access_token', 'token', 'data.token', 'data.ACCESS_TOKEN'] as $path) {
            $candidate = trim((string) Arr::get($decoded, $path, ''));
            if ($candidate !== '') {
                $token = $candidate;
                break;
            }
        }

        $gatewayCode = trim((string) (
            Arr::get($decoded, 'CODE')
            ?? Arr::get($decoded, 'code')
            ?? Arr::get($decoded, 'ERROR_CODE')
            ?? ''
        ));

        if ($token === '') {
            return [
                'code' => $gatewayCode !== '' ? $gatewayCode : 'TOKEN_MISSING',
                'message' => trim((string) (
                    Arr::get($decoded, 'MESSAGE')
                    ?? Arr::get($decoded, 'message')
                    ?? 'PayFast response did not contain ACCESS_TOKEN.'
                )),
                'data' => $decoded,
            ];
        }

        // Normalize the hosted-checkout response to the shape expected by the
        // package's PayFast::getToken() method.
        return [
            'code' => '00',
            'message' => 'Token retrieved successfully.',
            'data' => array_merge($decoded, [
                'token' => $token,
                'access_token' => $token,
                'ACCESS_TOKEN' => $token,
            ]),
        ];
    }

    public function refreshToken(string $token, string $refreshToken): array
    {
        return [
            'code' => 'UNSUPPORTED',
            'message' => 'The PayFast hosted-checkout access token is one-time and is not refreshed.',
            'data' => [],
        ];
    }
}
