<?php
namespace EasyChat\Library\Delivery;

use EasyChat\Library\Http\ExternalHttpClient;

 
final class RussianPostTrackingProvider implements TrackingProviderInterface
{
    private const ENDPOINT = 'https://tracking.russianpost.ru/rtm34';
    private $http;
    private $login;
    private $password;

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

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

    public function supports(string $carrier, string $tracking_number): bool
    {
        $carrier = mb_strtolower($carrier, 'UTF-8');
        return preg_match('/^\d{14}$/', $tracking_number) === 1
            || preg_match('/^[A-Z]{2}\d{9}[A-Z]{2}$/i', $tracking_number) === 1
            || strpos($carrier, 'почт') !== false;
    }

    public function track(string $tracking_number): array
    {
        $tracking_number = strtoupper(preg_replace('/\s+/', '', trim($tracking_number)));
        if ($this->login === '' || $this->password === '' || !$this->supports('', $tracking_number)) {
            return array('status' => 'unavailable');
        }
        $xml = '<?xml version="1.0" encoding="UTF-8"?>'
            . '<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:oper="http://russianpost.org/operationhistory" xmlns:data="http://russianpost.org/operationhistory/data" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">'
            . '<soap:Body><oper:getOperationHistory><data:OperationHistoryRequest><data:Barcode>'
            . htmlspecialchars($tracking_number, ENT_XML1 | ENT_QUOTES, 'UTF-8')
            . '</data:Barcode><data:MessageType>0</data:MessageType><data:Language>RUS</data:Language></data:OperationHistoryRequest>'
            . '<data:AuthorizationHeader soapenv:mustUnderstand="1"><data:login>'
            . htmlspecialchars($this->login, ENT_XML1 | ENT_QUOTES, 'UTF-8')
            . '</data:login><data:password>'
            . htmlspecialchars($this->password, ENT_XML1 | ENT_QUOTES, 'UTF-8')
            . '</data:password></data:AuthorizationHeader></oper:getOperationHistory></soap:Body></soap:Envelope>';
        $response = $this->http->request('POST', self::ENDPOINT, array(
            'headers' => array('Content-Type: application/soap+xml; charset=utf-8', 'Accept: application/soap+xml'),
            'body' => $xml,
            'connect_timeout' => 4,
            'timeout' => 12,
            'trust_profile' => 'russian_post_tracking',
        ));
        if (empty($response['transport_ok'])) return array('status' => 'unavailable');
        $status = (int)($response['http_code'] ?? 0);
        if ($status < 200 || $status >= 300) return array('status' => 'unavailable');
        return $this->parse((string)($response['raw'] ?? ''), $tracking_number);
    }

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

    private function parse(string $xml, string $tracking_number): array
    {
        if ($xml === '' || stripos($xml, '<!DOCTYPE') !== false || stripos($xml, '<!ENTITY') !== false) {
            return array('status' => 'unavailable');
        }
        $previous = libxml_use_internal_errors(true);
        $document = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NONET | LIBXML_NOCDATA);
        libxml_clear_errors();
        libxml_use_internal_errors($previous);
        if ($document === false) return array('status' => 'unavailable');
        $records = $document->xpath('//*[local-name()="historyRecord"]');
        if (!$records) return array('status' => 'not_found');
        $events = array();
        foreach ($records as $record) {
            $name = trim((string)($record->xpath('.//*[local-name()="OperAttr"]/*[local-name()="Name"]')[0] ?? ''));
            if ($name === '') $name = trim((string)($record->xpath('.//*[local-name()="OperType"]/*[local-name()="Name"]')[0] ?? ''));
            $date = trim((string)($record->xpath('.//*[local-name()="OperDate"]')[0] ?? ''));
            $place = trim((string)($record->xpath('.//*[local-name()="OperationAddress"]/*[local-name()="Description"]')[0] ?? ''));
            $events[] = array('status' => TrackingStatus::fromText($name), 'label' => mb_substr($name, 0, 160, 'UTF-8'), 'date' => $date, 'location' => mb_substr($place, 0, 160, 'UTF-8'));
        }
        usort($events, static function (array $a, array $b): int { return strcmp((string)$b['date'], (string)$a['date']); });
        $latest = $events[0];
        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));
    }
}
