<?php

namespace Fitsmart\PaypalParcels\ExternalService\PayPal;

use Fitsmart\PaypalParcels\Config;
use Fitsmart\PaypalParcels\Contract\ApiClientInterface;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Monolog\Logger;

class PaypalApiClient implements ApiClientInterface
{
    private Client $client;
    private Client $oauthClient;
    private Logger $logger;

    private const TOKEN_TRANSIENT = 'paypal_token';
    private const TOKEN_LOCK = 'paypal_token_lock';

    public function __construct(Logger $logger)
    {
        $this->logger = $logger;
        $config = Config::getConfigPart('paypal') ?? [];
        $this->client = new Client(['base_uri' =>  $config['base_uri'] ?? '']);
        $this->oauthClient = new Client(['base_uri' => $config['oauth_uri'] ?? '']);
    }

    public function createTracking(array $payload): bool
    {
        $token = $this->getBearerToken();
        if (!$token) {
            return false;
        }

        try {
            $response = $this->client->post('/v1/shipping/trackers-batch', [
                'headers' => [
                    'Authorization' => 'Bearer ' . $token,
                    'Content-Type' => 'application/json',
                ],
                'json' => $payload,
            ]);

            $status = $response->getStatusCode();
            if ($status >= 200 && $status < 300) {
                $this->logger->info('Tracking sent', $payload);
                return true;
            }
            $this->logger->error('PayPal error response', [
                'status' => $status,
                'body' => $response->getBody()->getContents(),
            ]);
        } catch (GuzzleException $e) {
            $this->logger->error('PayPal request failed: ' . $e->getMessage());
        }
        return false;
    }

    private function getBearerToken(): ?string
    {
        $token = get_transient(self::TOKEN_TRANSIENT);
        if (!empty($token)) {
            return $token;
        }

        if (get_transient(self::TOKEN_LOCK)) {
            sleep(1);
            return get_transient(self::TOKEN_TRANSIENT);
        }

        set_transient(self::TOKEN_LOCK, 1, 30);
        try {
            $tokenData = $this->requestToken();
            if ($tokenData) {
                $ttl = max(60, (int)($tokenData['expires_in'] * 0.9));
                set_transient(self::TOKEN_TRANSIENT, $tokenData['access_token'], $ttl);
                return $tokenData['access_token'];
            }
        } finally {
            delete_transient(self::TOKEN_LOCK);
        }
        return null;
    }

    private function requestToken(): ?array
    {
        $clientId = Config::getEnv('PP_PAYPAL_CLIENT_ID', '');
        $secret = Config::getEnv('PP_PAYPAL_SECRET_KEY', '');
        if (empty($clientId) || empty($secret)) {
            $this->logger->error('PayPal credentials missing');
            return null;
        }

        try {
            $response = $this->oauthClient->post('/v1/oauth2/token', [
                'auth' => [$clientId, $secret],
                'form_params' => ['grant_type' => 'client_credentials'],
            ]);
            $body = json_decode($response->getBody()->getContents(), true);
            if (isset($body['access_token'], $body['expires_in'])) {
                $this->logger->info('New Bearer token received', ['expires_in' => $body['expires_in']]);
                return [
                    'access_token' => $body['access_token'],
                    'expires_in' => $body['expires_in'],
                ];
            }
            $this->logger->error('Invalid token response', ['body' => $body]);
        } catch (GuzzleException $e) {
            $this->logger->error('Failed to obtain token: ' . $e->getMessage());
        }
        return null;
    }

}
