<?php
namespace EasyChat\Library\Delivery;

use EasyChat\Library\Http\ExternalHttpClient;

 
final class CdekTrackingProvider implements TrackingProviderInterface
{
    private const API = 'https://api.cdek.ru/v2/';
    private $http;
    private $client_id;
    private $client_secret;
    private $access_token = '';

    public function __construct($registry, string $client_id, string $client_secret, ?ExternalHttpClient $http = null)
    {
        $this->http = $http ?: new ExternalHttpClient($registry);
        $this->client_id = trim($client_id);
        $this->client_secret = trim($client_secret);
    }

    public function code(): string { return 'cdek'; }

    public function supports(string $carrier, string $tracking_number): bool
    {
        $carrier = mb_strtolower($carrier, 'UTF-8');
        return preg_match('/cdek|сдэк|сдек/u', $carrier) === 1 && preg_match('/^[A-Za-z0-9-]{5,40}$/', trim($tracking_number)) === 1;
    }

    public function track(string $tracking_number): array
    {
        $tracking_number = trim($tracking_number);
        if ($this->client_id === '' || $this->client_secret === '') return array('status' => 'unavailable');
        $token = $this->token();
        if ($token === '') return array('status' => 'unavailable');
        $response = $this->http->request('GET', self::API . 'orders?cdek_number=' . rawurlencode($tracking_number), array(
            'headers' => array('Authorization: Bearer ' . $token, 'Accept: application/json'),
            'connect_timeout' => 4, 'timeout' => 12, 'trust_profile' => 'cdek_tracking',
        ));
        if (empty($response['transport_ok'])) return array('status' => 'unavailable');
        $status = (int)($response['http_code'] ?? 0);
        if ($status === 404) return array('status' => 'not_found');
        if ($status < 200 || $status >= 300) return array('status' => 'unavailable');
        $payload = json_decode((string)($response['raw'] ?? ''), true);
        $entity = is_array($payload['entity'] ?? null) ? $payload['entity'] : array();
        if (!$entity) return array('status' => 'not_found');
        $events = array();
        foreach ((array)($entity['statuses'] ?? array()) as $row) {
            $label = trim((string)($row['name'] ?? $row['code'] ?? ''));
            $events[] = array(
                'status' => TrackingStatus::fromText($label), 'label' => mb_substr($label, 0, 160, 'UTF-8'),
                'date' => (string)($row['date_time'] ?? ''), 'location' => mb_substr((string)($row['city'] ?? ''), 0, 160, 'UTF-8'),
            );
        }
        usort($events, static function (array $a, array $b): int { return strcmp((string)$b['date'], (string)$a['date']); });
        $latest = $events[0] ?? array('status' => TrackingStatus::UNKNOWN, 'label' => '', 'date' => '', 'location' => '');
        return array('status' => 'ready', 'shipment' => array(
            'carrier' => 'СДЭК', 'carrier_code' => $this->code(), 'tracking_number' => $tracking_number,
            'status' => $latest['status'], 'status_label' => $latest['label'] ?: TrackingStatus::label($latest['status']),
            'updated_at' => $latest['date'], 'location' => $latest['location'], 'tracking_url' => $this->trackingUrl($tracking_number),
        ), 'events' => array_slice($events, 0, 20));
    }

    public function trackingUrl(string $tracking_number): string
    {
        return 'https://www.cdek.ru/ru/tracking?order_id=' . rawurlencode(trim($tracking_number));
    }

    private function token(): string
    {
        if ($this->access_token !== '') return $this->access_token;
        $body = http_build_query(array('grant_type' => 'client_credentials', 'client_id' => $this->client_id, 'client_secret' => $this->client_secret), '', '&', PHP_QUERY_RFC3986);
        $response = $this->http->request('POST', self::API . 'oauth/token', array(
            'headers' => array('Content-Type: application/x-www-form-urlencoded', 'Accept: application/json'),
            'body' => $body, 'connect_timeout' => 4, 'timeout' => 10, 'trust_profile' => 'cdek_tracking',
        ));
        if (empty($response['transport_ok']) || (int)($response['http_code'] ?? 0) < 200 || (int)($response['http_code'] ?? 0) >= 300) return '';
        $payload = json_decode((string)($response['raw'] ?? ''), true);
        $token = trim((string)($payload['access_token'] ?? ''));
        if (!preg_match('/^[A-Za-z0-9._~-]{20,4096}$/', $token)) return '';
        return $this->access_token = $token;
    }
}
