<?php

declare(strict_types=1);

/*
 * This file is part of the VITICORE2 package.
 *
 * (c) CP Creation <web@cpcreation.fr>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace CpCreation\VitiCore\Commerce\Catalog\Enrollment\Message;

use CpCreation\VitiCore\Commerce\Catalog\Enrollment\CatalogEnrollmentContext;
use CpCreation\VitiCore\Commerce\Catalog\Enrollment\CatalogEnrollmentPayloadFactory;
use CpCreation\VitiCore\Commerce\Catalog\Enrollment\CatalogEnrollmentPayloadProvider;
use CpCreation\VitiCore\Commerce\Catalog\Enrollment\Model\CatalogEnrollmentRequest;
use CpCreation\VitiCore\Commerce\Catalog\Enrollment\Repository\CatalogEnrollmentRequestRepository;
use CpCreation\VitiCore\Commerce\Catalog\Enrollment\Repository\CatalogEnrollmentRequestStore;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
use Symfony\Component\Messenger\MessageBusInterface;

#[AsMessageHandler]
final readonly class DispatchCatalogEnrollmentsMessageHandler
{
    public function __construct(
        #[Autowire(service: CatalogEnrollmentPayloadFactory::class)]
        private CatalogEnrollmentPayloadProvider $payloadFactory,
        #[Autowire(service: CatalogEnrollmentRequestRepository::class)]
        private CatalogEnrollmentRequestStore $requestStore,
        private EntityManagerInterface $entityManager,
        private MessageBusInterface $messageBus,
        private ?LoggerInterface $logger = null,
    ) {}

    public function __invoke(DispatchCatalogEnrollmentsMessage $message): void
    {
        $context = $this->payloadFactory->currentContext();
        if (!$context instanceof CatalogEnrollmentContext) {
            return;
        }

        $this->prepareActivationRequests($context, $message->limit);
        $this->ensureCurrentConfigurationRequest($context);

        $queuedAt = new \DateTimeImmutable('now', new \DateTimeZone('UTC'));
        $requests = $this->requestStore->findReadyForDelivery(
            $queuedAt->sub(new \DateInterval('PT15M')),
            $message->limit,
        );
        if ([] === $requests) {
            return;
        }

        foreach ($requests as $request) {
            $request->markQueued($queuedAt);
        }

        // Le claim est validé avant le dispatch. Les claims abandonnés sont repris
        // après quinze minutes par la requête findReadyForDelivery().
        $this->entityManager->flush();

        foreach ($requests as $request) {
            $this->messageBus->dispatch(new DeliverCatalogEnrollmentMessage($request->getRequestId()->toString()));
        }

        $this->logger?->info('Demandes d’enrôlement Commerce mises en file.', [
            'count' => \count($requests),
            'instance_id' => $context->instanceId,
        ]);
    }

    private function prepareActivationRequests(CatalogEnrollmentContext $context, int $limit): void
    {
        $requests = $this->requestStore->findUnprepared($limit);
        if ([] === $requests) {
            return;
        }

        foreach ($requests as $request) {
            $request->prepare($context->instanceId, $context->configurationFingerprint);
        }
        $this->entityManager->flush();
    }

    private function ensureCurrentConfigurationRequest(CatalogEnrollmentContext $context): void
    {
        if ($this->requestStore->existsForFingerprint($context->configurationFingerprint)) {
            return;
        }

        $this->entityManager->persist(CatalogEnrollmentRequest::prepared(
            $context->instanceId,
            $context->configurationFingerprint,
        ));
        $this->entityManager->flush();
    }
}
