<?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\Order\EventListener;

use InPost\International\Api\Tracking\TrackingEvent;
use InPost\International\Configuration\Repository\OrdersConfigurationRepositoryInterface;
use InPost\International\Entity\Shipment;
use InPost\International\PrestaShop\ObjectModel\ObjectManagerInterface;
use InPost\International\Shipment\Event\LabelPrintedEvent;
use InPost\International\Shipment\Event\ShipmentStatusUpdatedEvent;
use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

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

final class UpdateOrderStatusListener implements EventSubscriberInterface
{
    /**
     * @var OrdersConfigurationRepositoryInterface
     */
    private $configuration;

    /**
     * @var ObjectManagerInterface
     */
    private $manager;

    /**
     * @var LoggerInterface
     */
    private $logger;

    public function __construct(OrdersConfigurationRepositoryInterface $configuration, ObjectManagerInterface $manager, LoggerInterface $logger)
    {
        $this->configuration = $configuration;
        $this->manager = $manager;
        $this->logger = $logger;
    }

    public static function getSubscribedEvents(): array
    {
        return [
            LabelPrintedEvent::class => 'onLabelPrinted',
            ShipmentStatusUpdatedEvent::class => 'onShipmentStatusUpdated',
        ];
    }

    public function onLabelPrinted(LabelPrintedEvent $event): void
    {
        if (null === $statusId = $this->configuration->getOrdersConfiguration()->getLabelPrintedStatusId()) {
            return;
        }

        $this->updateOrderStatus($event->getShipment(), $statusId);
    }

    public function onShipmentStatusUpdated(ShipmentStatusUpdatedEvent $event): void
    {
        $shipment = $event->getShipment();

        if (!in_array($shipment->getStatus(), TrackingEvent::getDeliveredEventCodes(), true)) {
            return;
        }

        if (null === $statusId = $this->configuration->getOrdersConfiguration()->getShipmentDeliveredStatusId()) {
            return;
        }

        $this->updateOrderStatus($shipment, $statusId);
    }

    private function updateOrderStatus(Shipment $shipment, int $statusId): void
    {
        if (null === $this->manager->find(\OrderState::class, $statusId)) {
            $this->logger->warning('Could not update order status: status #{statusId} does not exist.', [
                'statusId' => $statusId,
            ]);

            return;
        }

        $order = $this->manager->find(\Order::class, $shipment->getOrderId());

        if (null === $order || $statusId === (int) $order->getCurrentState()) {
            return;
        }

        if ($order->getHistory((int) $order->id_lang, $statusId)) {
            return;
        }

        try {
            $order->setCurrentState($statusId);

            $this->logger->info('Updated status of order #{orderId} to #{statusId}.', [
                'orderId' => $shipment->getOrderId(),
                'statusId' => $statusId,
            ]);
        } catch (\Throwable $e) {
            $this->logger->error('An error occurred while updating status of order #{orderId}: {exception}', [
                'orderId' => $shipment->getOrderId(),
                'statusId' => $statusId,
                'exception' => $e,
            ]);
        }
    }
}
