<?php
declare(strict_types=1);

namespace App\Modules\Cron;

use App\Modules\Automation\AutomationService;
use App\Modules\Shipments\ShipmentPackageRepository;
use App\Modules\Shipments\ShipmentTrackingRegistry;
use Throwable;

final class ShipmentTrackingHandler
{
    private const ALLEGRO_EDGE_RATE_LIMIT_SECONDS = 60;

    public function __construct(
        private readonly ShipmentTrackingRegistry $registry,
        private readonly ShipmentPackageRepository $repository,
        private readonly AutomationService $automationService
    ) {
    }

    /**
     * @param array<string, mixed> $payload
     * @return array<string, mixed>
     */
    public function handle(array $_payload): array
    {
        $packages = $this->repository->findActiveForTracking();
        $total = count($packages);
        $updated = 0;
        $errors = 0;

        $lastAllegroEdgeRequestTime = 0.0;

        foreach ($packages as $package) {
            $provider = trim((string) ($package['provider'] ?? ''));
            $packageId = (int) ($package['id'] ?? 0);

            $service = $this->registry->getForProvider($provider);
            if ($service === null) {
                continue;
            }

            try {
                if ($provider === 'allegro_wza') {
                    $carrierId = strtolower(trim((string) ($package['carrier_id'] ?? '')));
                    $isInpost = str_contains($carrierId, 'inpost') || str_contains($carrierId, 'paczkomat');

                    if (!$isInpost) {
                        $elapsed = microtime(true) - $lastAllegroEdgeRequestTime;
                        if ($elapsed < self::ALLEGRO_EDGE_RATE_LIMIT_SECONDS) {
                            $sleepTime = (int) ceil(self::ALLEGRO_EDGE_RATE_LIMIT_SECONDS - $elapsed);
                            sleep($sleepTime);
                        }
                        $lastAllegroEdgeRequestTime = microtime(true);
                    }
                }

                $result = $service->getDeliveryStatus($package);
                if ($result !== null) {
                    $previousStatus = trim((string) ($package['delivery_status'] ?? 'unknown'));
                    if ($previousStatus === '') {
                        $previousStatus = 'unknown';
                    }
                    $previousStatusRaw = trim((string) ($package['delivery_status_raw'] ?? ''));
                    $newStatus = trim((string) ($result['status'] ?? 'unknown'));
                    if ($newStatus === '') {
                        $newStatus = 'unknown';
                    }
                    $newStatusRaw = trim((string) ($result['status_raw'] ?? ''));
                    $statusChanged = $newStatus !== $previousStatus;
                    $statusRawChanged = $newStatusRaw !== $previousStatusRaw;
                    if (!$statusChanged && !$statusRawChanged) {
                        continue;
                    }

                    $this->repository->updateDeliveryStatus(
                        $packageId,
                        $newStatus,
                        $newStatusRaw !== '' ? $newStatusRaw : null
                    );
                    $updated++;

                    if ($statusChanged) {
                        $orderId = (int) ($package['order_id'] ?? 0);
                        if ($orderId > 0) {
                            $this->automationService->trigger('shipment.status_changed', $orderId, [
                                'package_id' => $packageId,
                                'provider' => $provider,
                                'delivery_status' => $newStatus,
                                'delivery_status_raw' => $newStatusRaw,
                                'previous_status' => $previousStatus,
                                'previous_status_raw' => $previousStatusRaw,
                            ]);
                        }
                    }
                }
            } catch (Throwable) {
                $errors++;
            }
        }

        return [
            'total' => $total,
            'updated' => $updated,
            'errors' => $errors,
        ];
    }
}
