<?php

declare(strict_types=1);

/*
 * This file is part of the Ferienpass package.
 *
 * (c) Richard Henkenjohann <richard@ferienpass.online>
 *
 * For more information visit the project website <https://ferienpass.online>
 * or the documentation under <https://docs.ferienpass.online>.
 */

namespace Ferienpass\CoreBundle\Repository;

use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use Ferienpass\CoreBundle\Entity\Attendance;
use Ferienpass\CoreBundle\Entity\Offer\OfferInterface;
use Ferienpass\CoreBundle\Entity\Participant\ParticipantInterface;
use Ferienpass\CoreBundle\Entity\User;
use Ferienpass\CoreBundle\Enum\AttendanceStatus;
use Symfony\Bridge\Doctrine\Types\UuidType;
use Symfony\Component\Uid\Uuid;

class AttendanceRepository extends ServiceEntityRepository
{
    public function __construct(ManagerRegistry $registry)
    {
        parent::__construct($registry, Attendance::class);
    }

    /**
     * @return Attendance[]
     */
    public function findConfirmed(Uuid $offerUuid): array
    {
        return $this->createQueryBuilder('a')
            ->innerJoin('a.offer', 'o')
            ->andWhere('o.uuid = :offerUuid')
            ->andWhere('a.status = :status')
            // `o.uuid` is a BINARY(16) UuidType column. In raw DQL Doctrine does
            // not auto-apply the field's type to the parameter, so an untyped Uuid
            // binds as its 36-char string and never matches the 16 stored bytes —
            // the query then returns zero rows and a cancelled/relaunched offer
            // notifies nobody. Bind the type explicitly (cf. PaymentRepository).
            ->setParameter('offerUuid', $offerUuid, UuidType::NAME)
            ->setParameter('status', AttendanceStatus::CONFIRMED)
            ->getQuery()
            ->getResult()
        ;
    }

    public function findExisting(OfferInterface $offer, ParticipantInterface $participant): ?Attendance
    {
        return $this->findOneBy(['offer' => $offer, 'participant' => $participant]);
    }

    public function hasOutstandingConsentsForUser(User $user): bool
    {
        return (bool) $this->createQueryBuilder('a')
            ->select('COUNT(a.id)')
            ->join('a.participant', 'p')
            ->join('a.offer', 'o')
            ->join('o.dates', 'd')
            ->andWhere('p.user = :user')
            ->andWhere('a.consentOutstanding = true')
            ->andWhere('d.begin > :now')
            ->setParameter('user', $user)
            ->setParameter('now', new \DateTimeImmutable())
            ->getQuery()
            ->getSingleScalarResult()
        ;
    }

    public function countStashed(): int
    {
        return (int) $this->createQueryBuilder('a')
            ->select('COUNT(a.id)')
            ->where('a.stash IS NOT NULL')
            ->getQuery()
            ->getSingleScalarResult()
        ;
    }
}
