<?php

namespace App\Pricing\Repository;

use App\Doctor\Entity\DoctorAddress;
use App\Pricing\Entity\PriceList;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;

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

    public function findByUuid(string $uuid): ?PriceList
    {
        return $this->findOneBy(['uuid' => $uuid]);
    }

    /** @return PriceList[] */
    public function findForPair(string $entityType, int $entityId): array
    {
        return $this->createQueryBuilder('p')
            ->where('p.entityType = :type')
            ->andWhere('p.entityId = :id')
            ->setParameter('type', $entityType)
            ->setParameter('id', $entityId)
            ->orderBy('p.startsAt', 'DESC')
            ->getQuery()
            ->getResult();
    }

    /**
     * لیست قیمتِ حاکم بر یک لحظه.
     *
     * لیستِ مخصوصِ همان شعبه بر لیست عمومیِ محیط مقدم است — وگرنه تعریف استثنا برای
     * یک شعبه هیچ اثری نداشت.
     */
    public function findCovering(string $entityType, int $entityId, ?DoctorAddress $address, int $at): ?PriceList
    {
        $rows = $this->createQueryBuilder('p')
            ->where('p.entityType = :type')
            ->andWhere('p.entityId = :id')
            ->andWhere('p.active = true')
            ->andWhere('p.startsAt <= :at')
            ->andWhere('p.endsAt > :at')
            ->setParameter('type', $entityType)
            ->setParameter('id', $entityId)
            ->setParameter('at', $at)
            ->getQuery()
            ->getResult();

        $general = null;

        foreach ($rows as $list) {
            if ($address !== null && $list->getAddress()?->getId() === $address->getId()) {
                return $list;
            }

            if ($list->getAddress() === null) {
                $general = $list;
            }
        }

        return $general;
    }

    /**
     * لیست‌های فعالِ هم‌پوشان با یک بازه — برای جلوگیری از دو قیمتِ هم‌زمان.
     *
     * @return PriceList[]
     */
    public function findOverlapping(PriceList $candidate): array
    {
        $qb = $this->createQueryBuilder('p')
            ->where('p.entityType = :type')
            ->andWhere('p.entityId = :id')
            ->andWhere('p.active = true')
            ->andWhere('p.startsAt < :ends')
            ->andWhere('p.endsAt > :starts')
            ->setParameter('type', $candidate->getEntityType())
            ->setParameter('id', $candidate->getEntityId())
            ->setParameter('starts', $candidate->getStartsAt())
            ->setParameter('ends', $candidate->getEndsAt());

        if ($candidate->getId() !== null) {
            $qb->andWhere('p.id != :self')->setParameter('self', $candidate->getId());
        }

        // فقط لیست‌هایی که دامنهٔ یکسانی دارند با هم تداخل دارند: لیست عمومی و لیست
        // یک شعبه عمداً کنار هم زندگی می‌کنند و اولویت دارند، نه تداخل.
        return array_values(array_filter(
            $qb->getQuery()->getResult(),
            static fn (PriceList $other): bool => $other->getAddress()?->getId() === $candidate->getAddress()?->getId(),
        ));
    }
}
