<?php
declare(strict_types=1);

namespace App\Modules\Shipments;

use App\Core\Http\SslCertificateResolver;
use App\Modules\Settings\InpostIntegrationRepository;
use Throwable;

final class AllegroTrackingService implements ShipmentTrackingInterface
{
    private const INPOST_API_PRODUCTION = 'https://api-shipx-pl.easypack24.net/v1';
    private const INPOST_API_SANDBOX = 'https://sandbox-api-shipx-pl.easypack24.net/v1';

    public function __construct(
        private readonly InpostIntegrationRepository $inpostRepository
    ) {
    }

    public function supports(string $provider): bool
    {
        return $provider === 'allegro_wza';
    }

    public function getDeliveryStatus(array $package): ?array
    {
        $trackingNumber = trim((string) ($package['tracking_number'] ?? ''));
        if ($trackingNumber === '') {
            return null;
        }

        $carrierId = strtolower(trim((string) ($package['carrier_id'] ?? '')));

        if (str_contains($carrierId, 'inpost') || str_contains($carrierId, 'paczkomat')) {
            return $this->fetchInpostStatus($trackingNumber);
        }

        return $this->fetchAllegroEdgeStatus($trackingNumber);
    }

    private function fetchInpostStatus(string $trackingNumber): ?array
    {
        try {
            $token = $this->resolveInpostToken();
            if ($token === null) {
                return null;
            }

            $settings = $this->inpostRepository->getSettings();
            $env = (string) ($settings['environment'] ?? 'sandbox');
            $baseUrl = strtolower(trim($env)) === 'production'
                ? self::INPOST_API_PRODUCTION
                : self::INPOST_API_SANDBOX;

            $url = $baseUrl . '/tracking/' . rawurlencode($trackingNumber);
            $response = $this->apiRequest($url, $token);

            $details = is_array($response['tracking_details'] ?? null) ? $response['tracking_details'] : [];
            if ($details === []) {
                return null;
            }

            $rawStatus = strtolower(trim((string) ($details[0]['status'] ?? '')));
            if ($rawStatus === '') {
                return null;
            }

            return [
                'status' => DeliveryStatus::normalize('inpost', $rawStatus),
                'status_raw' => $rawStatus,
                'description' => DeliveryStatus::description('inpost', $rawStatus),
            ];
        } catch (Throwable) {
            return null;
        }
    }

    private function fetchAllegroEdgeStatus(string $trackingNumber): ?array
    {
        try {
            $url = 'https://edge.allegro.pl/ad/tracking?packageNo=' . rawurlencode($trackingNumber);
            $response = $this->edgeApiRequest($url);

            $statuses = $response['status'] ?? [];
            if (!is_array($statuses) || $statuses === []) {
                return null;
            }

            $latest = end($statuses);
            $description = trim((string) ($latest['description'] ?? ''));
            if ($description === '') {
                return null;
            }

            $slug = DeliveryStatus::slugifyAllegroDescription($description);

            $normalized = DeliveryStatus::normalize('allegro_edge', $slug);

            if ($normalized === DeliveryStatus::UNKNOWN) {
                $normalized = DeliveryStatus::guessStatusFromDescription($description);

                error_log(sprintf(
                    '[AllegroTracking] Niezmapowany status: "%s" (slug: %s, guessed: %s)',
                    $description,
                    $slug,
                    $normalized
                ));
            }

            return [
                'status' => $normalized,
                'status_raw' => $description,
                'description' => $description,
            ];
        } catch (Throwable) {
            return null;
        }
    }

    /**
     * @return array<string, mixed>
     */
    private function edgeApiRequest(string $url): array
    {
        $ch = curl_init($url);
        if ($ch === false) {
            return [];
        }

        $opts = [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 15,
            CURLOPT_CONNECTTIMEOUT => 5,
            CURLOPT_SSL_VERIFYPEER => true,
            CURLOPT_SSL_VERIFYHOST => 2,
            CURLOPT_HTTPHEADER => [
                'Accept: application/vnd.allegro.internal.v1+json',
                'Content-Type: application/vnd.allegro.internal.v1+json',
            ],
        ];

        $caPath = SslCertificateResolver::resolve();
        if ($caPath !== null) {
            $opts[CURLOPT_CAINFO] = $caPath;
        }

        curl_setopt_array($ch, $opts);
        $body = curl_exec($ch);
        $httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $ch = null;

        if ($body === false || $httpCode < 200 || $httpCode >= 300) {
            return [];
        }

        $json = json_decode((string) $body, true);
        return is_array($json) ? $json : [];
    }

    private function resolveInpostToken(): ?string
    {
        try {
            $token = $this->inpostRepository->getDecryptedToken();
            return ($token !== null && trim($token) !== '') ? trim($token) : null;
        } catch (Throwable) {
            return null;
        }
    }

    /**
     * @return array<string, mixed>
     */
    private function apiRequest(string $url, string $token): array
    {
        $ch = curl_init($url);
        if ($ch === false) {
            return [];
        }

        $opts = [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 15,
            CURLOPT_CONNECTTIMEOUT => 5,
            CURLOPT_SSL_VERIFYPEER => true,
            CURLOPT_SSL_VERIFYHOST => 2,
            CURLOPT_HTTPHEADER => [
                'Authorization: Bearer ' . $token,
                'Accept: application/json',
            ],
        ];

        $caPath = SslCertificateResolver::resolve();
        if ($caPath !== null) {
            $opts[CURLOPT_CAINFO] = $caPath;
        }

        curl_setopt_array($ch, $opts);
        $body = curl_exec($ch);
        $httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $ch = null;

        if ($body === false || $httpCode < 200 || $httpCode >= 300) {
            return [];
        }

        $json = json_decode((string) $body, true);
        return is_array($json) ? $json : [];
    }

}
