<?php

declare(strict_types=1);

namespace Gls\GlsPoland\PrestaShop\EventListener;

use Gls\GlsPoland\Entity\Parcel;
use Gls\GlsPoland\MessageHandler\Pickup\PickupCreatedEvent;
use Gls\GlsPoland\PrestaShop\Message\SendTrackingUrlEmailCommand;
use Gls\GlsPoland\PrestaShop\ObjectModel\Repository\CarrierRepository;
use Gls\GlsPoland\PrestaShop\ObjectModel\Repository\OrderCarrierRepository;
use PrestaShop\PrestaShop\Core\CommandBus\CommandBusInterface;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\KernelEvent;
use Symfony\Component\HttpKernel\KernelEvents;

final class SendTrackingUrlMailsListener implements EventSubscriberInterface
{
    private $bus;
    private $orderCarrierRepository;
    private $carrierRepository;
    private $module;

    private $parcelNumbersByOrder = [];
    private $logger;

    public function __construct(CommandBusInterface $bus, OrderCarrierRepository $orderCarrierRepository, CarrierRepository $carrierRepository, \Module $module, LoggerInterface $logger = null)
    {
        $this->bus = $bus;
        $this->orderCarrierRepository = $orderCarrierRepository;
        $this->carrierRepository = $carrierRepository;
        $this->module = $module;
        $this->logger = $logger ?? new NullLogger();
    }

    public static function getSubscribedEvents(): array
    {
        return [
            PickupCreatedEvent::class => 'onPickupCreated',
            KernelEvents::TERMINATE => 'onKernelTerminate',
        ];
    }

    public function onPickupCreated(PickupCreatedEvent $event): void
    {
        foreach ($event->getConsignments() as $consignment) {
            if (!$consignment->hasParcelNumbers()) {
                continue;
            }

            $parcelNumbers = $consignment->getParcels()->map(static function (Parcel $parcel): string {
                return $parcel->getNumber();
            });

            $this->parcelNumbersByOrder[$consignment->getOrderId()][] = $parcelNumbers->toArray();
        }
    }

    /**
     * @param \Symfony\Component\HttpKernel\Event\TerminateEvent|\Symfony\Component\HttpKernel\Event\PostResponseEvent $event
     */
    public function onKernelTerminate(KernelEvent $event): void
    {
        if ([] === $this->parcelNumbersByOrder || !$event->isMasterRequest()) {
            return;
        }

        set_time_limit(0);

        foreach ($this->parcelNumbersByOrder as $orderId => $parcelNumbers) {
            $parcelNumbers = array_unique(array_merge(...$parcelNumbers));
            $this->processOrder($orderId, $parcelNumbers);
        }
    }

    private function processOrder(int $orderId, array $parcelNumbers): void
    {
        $orderCarrier = $this->orderCarrierRepository->findOneBy(['id_order' => $orderId]);
        if (null === $orderCarrier) {
            return;
        }

        $carrier = $this->carrierRepository->find((int) $orderCarrier->id_carrier);
        if (null === $carrier || $carrier->external_module_name !== $this->module->name) {
            return;
        }

        foreach ($parcelNumbers as $parcelNumber) {
            try {
                $this->bus->handle(new SendTrackingUrlEmailCommand($orderCarrier, $carrier, $parcelNumber));
            } catch (\Throwable $e) {
                $this->logger->error('Failed to send tracking email for order #{orderId}, parcel number: "{parcelNumber}"', [
                    'orderId' => $orderId,
                    'parcelNumber' => $parcelNumber,
                    'error' => $e,
                ]);
            }
        }
    }
}
