<?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\Repository;

use CpCreation\VitiCore\Commerce\Catalog\Model\CatalogChange;
use CpCreation\VitiCore\Commerce\Catalog\Model\CatalogDeliveryStatus;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use Ramsey\Uuid\UuidInterface;

/**
 * @extends ServiceEntityRepository<CatalogChange>
 */
final class CatalogChangeRepository extends ServiceEntityRepository
{
    public function __construct(ManagerRegistry $registry)
    {
        parent::__construct($registry, CatalogChange::class);
    }

    /**
     * @return list<CatalogChange>
     */
    public function findAfterCursor(int $after, int $limit): array
    {
        return $this->createQueryBuilder('change')
            ->andWhere('change.cursor > :after')
            ->setParameter('after', max(0, $after))
            ->orderBy('change.cursor', 'ASC')
            ->setMaxResults(max(1, min(250, $limit)))
            ->getQuery()
            ->getResult();
    }

    public function findOneByEventId(UuidInterface|string $eventId): ?CatalogChange
    {
        return $this->findOneBy(['eventId' => $eventId]);
    }

    /**
     * Retourne les événements jamais mis en file ainsi que ceux dont la mise en file
     * est suffisamment ancienne pour récupérer un crash entre le claim et le dispatch.
     *
     * @return list<CatalogChange>
     */
    public function findReadyForDelivery(\DateTimeImmutable $staleQueuedBefore, int $limit): array
    {
        return $this->createQueryBuilder('change')
            ->andWhere('change.deliveryStatus = :pending')
            ->orWhere('(change.deliveryStatus = :queued AND change.lastQueuedAt <= :staleQueuedBefore)')
            ->setParameter('pending', CatalogDeliveryStatus::PENDING)
            ->setParameter('queued', CatalogDeliveryStatus::QUEUED)
            ->setParameter('staleQueuedBefore', $staleQueuedBefore)
            ->orderBy('change.cursor', 'ASC')
            ->setMaxResults(max(1, min(250, $limit)))
            ->getQuery()
            ->getResult();
    }

    public function purgeCompletedBefore(\DateTimeImmutable $before): int
    {
        return $this->createQueryBuilder('change')
            ->delete()
            ->andWhere('(
                change.deliveredAt IS NOT NULL
                AND change.deliveredAt < :before
            ) OR (
                change.permanentlyFailedAt IS NOT NULL
                AND change.permanentlyFailedAt < :before
            )')
            ->setParameter('before', $before)
            ->getQuery()
            ->execute();
    }
}
