<?php

declare(strict_types=1);

namespace App\Repository;

use App\Entity\Absence;
use App\Entity\Employee;
use App\Repository\Contract\AbsenceReadRepositoryInterface;
use DateTimeImmutable;
use DateTimeInterface;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;

/**
 * @extends ServiceEntityRepository<Absence>
 */
final class AbsenceRepository extends ServiceEntityRepository implements AbsenceReadRepositoryInterface
{
    public function __construct(ManagerRegistry $registry)
    {
        parent::__construct($registry, Absence::class);
    }

    /**
     * @param list<Employee> $employees
     *
     * @return list<Absence>
     */
    public function findForPrint(DateTimeImmutable $from, DateTimeImmutable $to, array $employees): array
    {
        if ([] === $employees) {
            return [];
        }

        $qb = $this->createQueryBuilder('a')
            ->leftJoin('a.employee', 'e')
            ->leftJoin('a.type', 't')
            ->addSelect('e', 't')
            ->andWhere('a.startDate <= :to')
            ->andWhere('a.endDate >= :from')
            ->andWhere('a.employee IN (:employees)')
            ->setParameter('from', $from)
            ->setParameter('to', $to)
            ->setParameter('employees', $employees)
        ;

        // @var list<Absence> $absences
        return $qb->getQuery()->getResult();
    }

    /**
     * @param list<Employee> $employees
     *
     * @return list<Absence>
     */
    public function findByDateAndEmployees(DateTimeImmutable $date, array $employees): array
    {
        if ([] === $employees) {
            return [];
        }

        $qb = $this->createQueryBuilder('a')
            ->leftJoin('a.employee', 'e')
            ->leftJoin('a.type', 't')
            ->addSelect('e', 't')
            ->andWhere('a.startDate <= :date')
            ->andWhere('a.endDate >= :date')
            ->andWhere('a.employee IN (:employees)')
            ->setParameter('date', $date)
            ->setParameter('employees', $employees)
        ;

        // @var list<Absence> $absences
        return $qb->getQuery()->getResult();
    }

    /**
     * @return list<Absence>
     */
    public function findByEmployeeAndDateRange(Employee $employee, DateTimeInterface $from, DateTimeInterface $to): array
    {
        $fromDate = DateTimeImmutable::createFromInterface($from);
        $toDate   = DateTimeImmutable::createFromInterface($to);

        $qb = $this->createQueryBuilder('a')
            ->leftJoin('a.employee', 'e')
            ->leftJoin('a.type', 't')
            ->addSelect('e', 't')
            ->andWhere('a.employee = :employee')
            ->andWhere('a.startDate >= :from')
            ->andWhere('a.startDate <= :to')
            ->setParameter('employee', $employee)
            ->setParameter('from', $fromDate)
            ->setParameter('to', $toDate)
            ->orderBy('a.startDate', 'ASC')
        ;

        // @var list<Absence> $absences
        return $qb->getQuery()->getResult();
    }

    /**
     * @return list<DateTimeImmutable> sorted maladie dates
     */
    public function findMaladieDatesByEmployee(
        Employee $employee,
        DateTimeImmutable $from,
        DateTimeImmutable $to
    ): array {
        $results = $this->createQueryBuilder('a')
            ->select('a.startDate')
            ->join('a.type', 't')
            ->andWhere('a.employee = :employee')
            ->andWhere('t.code = :code')
            ->andWhere('a.startDate >= :from')
            ->andWhere('a.startDate <= :to')
            ->setParameter('employee', $employee)
            ->setParameter('code', 'M')
            ->setParameter('from', $from)
            ->setParameter('to', $to)
            ->orderBy('a.startDate', 'ASC')
            ->getQuery()
            ->getArrayResult()
        ;

        return array_map(
            static fn (array $row): DateTimeImmutable => $row['startDate'] instanceof DateTimeImmutable
                ? $row['startDate']
                : DateTimeImmutable::createFromInterface($row['startDate']),
            $results
        );
    }

    /**
     * @return list<Absence>
     */
    public function findByEmployeeAndOverlappingDateRange(Employee $employee, DateTimeInterface $from, DateTimeInterface $to): array
    {
        $fromDate = DateTimeImmutable::createFromInterface($from);
        $toDate   = DateTimeImmutable::createFromInterface($to);

        $qb = $this->createQueryBuilder('a')
            ->leftJoin('a.employee', 'e')
            ->leftJoin('a.type', 't')
            ->addSelect('e', 't')
            ->andWhere('a.employee = :employee')
            ->andWhere('a.startDate <= :to')
            ->andWhere('a.endDate >= :from')
            ->setParameter('employee', $employee)
            ->setParameter('from', $fromDate)
            ->setParameter('to', $toDate)
            ->orderBy('a.startDate', 'ASC')
        ;

        // @var list<Absence> $absences
        return $qb->getQuery()->getResult();
    }
}
