<?php
/**
 * Copyright since 2025 InPost S.A.
 *
 * For the full license information, please view the LICENSE file bundled with the module.
 *
 * @author InPost S.A.
 * @copyright since 2025 InPost S.A.
 * @license MIT
 */

declare(strict_types=1);

namespace InPost\International\Shipment\MessageHandler;

use InPost\International\Api\Exception\ApiClientExceptionInterface;
use InPost\International\Api\Tracking\TrackingApiClientInterface;
use InPost\International\Shipment\Event\ShipmentStatusUpdatedEvent;
use InPost\International\Shipment\Exception\ShipmentException;
use InPost\International\Shipment\Exception\ShipmentNotFoundException;
use InPost\International\Shipment\Message\UpdateShipmentStatusCommand;
use InPost\International\Shipment\ShipmentRepositoryInterface;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;

if (!defined('_PS_VERSION_')) {
    exit;
}

final class UpdateShipmentStatusHandler
{
    /**
     * @var ShipmentRepositoryInterface
     */
    private $repository;

    /**
     * @var TrackingApiClientInterface
     */
    private $client;

    /**
     * @var EventDispatcherInterface
     */
    private $dispatcher;

    public function __construct(ShipmentRepositoryInterface $repository, TrackingApiClientInterface $client, EventDispatcherInterface $dispatcher)
    {
        $this->repository = $repository;
        $this->client = $client;
        $this->dispatcher = $dispatcher;
    }

    /**
     * @return string current shipment status
     *
     * @throws ShipmentException
     * @throws ApiClientExceptionInterface
     */
    public function __invoke(UpdateShipmentStatusCommand $command): string
    {
        if (null === $shipment = $this->repository->find($command->getShipmentId())) {
            throw ShipmentNotFoundException::create($command->getShipmentId());
        }

        // TODO: cleanup / not sure if we should use tracking events instead of the shipment status, but these are at least documented ¯\_(ツ)_/¯
        /* $apiShipment = $this->client->getShipment($shipment->getUuid());

        if ($apiShipment->getStatus() === $previousStatus = $shipment->getStatus()) {
            return $previousStatus;
        }

        $shipment->updateStatus($apiShipment); */

        $trackingNumber = $shipment->getTrackingNumber();
        $trackingDetails = $this->client->getTrackingDetails($trackingNumber);

        if (null === $parcel = $trackingDetails->getParcel($trackingNumber)) {
            return $shipment->getStatus();
        }

        $previousStatus = $shipment->getStatus();
        $shipment->updateStatus($parcel);

        if ($previousStatus === $shipment->getStatus()) {
            return $previousStatus;
        }

        $this->repository->add($shipment);
        $this->dispatcher->dispatch(new ShipmentStatusUpdatedEvent($shipment, $previousStatus));

        return $shipment->getStatus();
    }

    public function handle(UpdateShipmentStatusCommand $command): string
    {
        return ($this)($command);
    }
}
