<?php

namespace Fitsmart\PaypalParcels;

use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Monolog\Logger;

class ApiClient
{
    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;
        $this->client = new Client(['base_uri' => ConfigHelper::getPayPalBaseUri()]);
        $this->oauthClient = new Client(['base_uri' => ConfigHelper::getPayPalOauthUri()]);
    }

    public function create_tracking(int $order_id, array $tracking_data): bool
    {
        $token = $this->getBearerToken();
        if (!$token) {
            return false;
        }

        $payload = [
            'trackers' => [
                [
                    'transaction_id' => $tracking_data['transaction_id'],
                    'tracking_number' => $tracking_data['tracking_number'] ?? '',
                    'status' => ConfigHelper::getPayPalStatus(),
                    'carrier' => $tracking_data['courier_code'] ?? '',
                ],
            ],
        ];

        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', [
                    'order_id' => $order_id,
                    'transaction_id' => $tracking_data['transaction_id'],
                ]);
                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(), [
                'order_id' => $order_id,
                'transaction_id' => $tracking_data['transaction_id'],
            ]);
        }
        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 {
            $token = $this->requestToken();
            if ($token) {
                // Bearer lives 9 h - 5 sec
                set_transient(self::TOKEN_TRANSIENT, $token, (3600 * 9 - 5));
            }
        } finally {
            delete_transient(self::TOKEN_LOCK);
        }
        return $token;
    }

    private function requestToken(): ?string
    {
        $clientId = EnvHelper::getPaypalClientId();
        $secret = EnvHelper::getPaypalSecretKey();
        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'])) {
                return $body['access_token'];
            }
            $this->logger->error('Invalid token response', ['body' => $body]);
        } catch (GuzzleException $e) {
            $this->logger->error('Failed to obtain token: ' . $e->getMessage());
        }
        return null;
    }
    
}