<?php

namespace App\Resource\Repository;

use App\Doctor\Entity\Doctor;
use App\Doctor\Entity\DoctorAddress;
use App\Branch\Entity\Room;
use App\Resource\Entity\ClinicResource;
use App\Resource\Entity\ResourceType;
use App\Staff\Entity\ClinicStaff;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;

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

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

    /**
     * فهرست منابع یک محیط با فیلترهای اختیاری.
     *
     * @param array{address?: ?DoctorAddress, type?: ?ResourceType, active?: ?bool, skillUuid?: ?string} $filters
     * @return ClinicResource[]
     */
    public function findForPair(string $entityType, int $entityId, array $filters = []): array
    {
        $qb = $this->createQueryBuilder('r')
            ->where('r.entityType = :tenantType')
            ->andWhere('r.entityId = :tenantId')
            ->setParameter('tenantType', $entityType)
            ->setParameter('tenantId', $entityId);

        if (($filters['address'] ?? null) !== null) {
            $qb->andWhere('r.address = :address')->setParameter('address', $filters['address']);
        }

        if (($filters['type'] ?? null) !== null) {
            $qb->andWhere('r.type = :type')->setParameter('type', $filters['type']);
        }

        if (($filters['active'] ?? null) !== null) {
            $qb->andWhere('r.active = :active')->setParameter('active', $filters['active']);
        }

        if (($filters['skillUuid'] ?? null) !== null) {
            $qb->join('r.skills', 'flt_rs')
                ->join('flt_rs.skill', 'flt_s')
                ->andWhere('flt_s.uuid = :skillUuid')
                ->setParameter('skillUuid', $filters['skillUuid']);
        }

        return $qb->orderBy('r.name', 'ASC')->getQuery()->getResult();
    }

    /** منابع فعال یک محیط — شرط آمادگیِ حالت نوبت‌دهی منبع‌محور. */
    public function countActiveForPair(string $entityType, int $entityId): int
    {
        return (int) $this->createQueryBuilder('r')
            ->select('COUNT(r.id)')
            ->where('r.entityType = :type')
            ->andWhere('r.entityId = :id')
            ->andWhere('r.active = true')
            ->setParameter('type', $entityType)
            ->setParameter('id', $entityId)
            ->getQuery()
            ->getSingleScalarResult();
    }

    /**
     * همهٔ منابع فعال یک شعبه — ورودی گزارش بهره‌وری.
     *
     * @return ClinicResource[]
     */
    public function findForAddress(DoctorAddress $address): array
    {
        return $this->createQueryBuilder('r')
            ->addSelect('t')
            ->join('r.type', 't')
            ->where('r.address = :address')
            ->andWhere('r.active = true')
            ->setParameter('address', $address)
            ->orderBy('t.code', 'ASC')
            ->addOrderBy('r.name', 'ASC')
            ->getQuery()
            ->getResult();
    }

    /**
     * پرس‌وجوی داغِ تسک ۰۶: «منابع فعالِ این شعبه از این نوع که **همهٔ** این مهارت‌ها
     * را دارند».
     *
     * `HAVING COUNT(DISTINCT …)` عمدی است: نیازمندی «مهارت الف و ب» یعنی هر دو، نه
     * یکی — که با یک `IN` ساده اشتباه پاسخ می‌گرفت.
     *
     * @param int[] $skillIds خالی یعنی بدون شرط مهارت
     * @return ClinicResource[]
     */
    public function findEligible(DoctorAddress $address, ResourceType $type, array $skillIds = []): array
    {
        $qb = $this->createQueryBuilder('r')
            ->where('r.address = :address')
            ->andWhere('r.type = :type')
            ->andWhere('r.active = true')
            ->setParameter('address', $address)
            ->setParameter('type', $type);

        if ($skillIds !== []) {
            $qb->join('r.skills', 'rs')
                ->andWhere('rs.skill IN (:skills)')
                ->setParameter('skills', $skillIds)
                ->groupBy('r.id')
                ->having('COUNT(DISTINCT rs.skill) = :skillCount')
                ->setParameter('skillCount', count(array_unique($skillIds)));
        }

        return $qb->orderBy('r.name', 'ASC')->getQuery()->getResult();
    }

    public function findForSubject(Doctor|ClinicStaff|Room $subject, ?DoctorAddress $address = null): ?ClinicResource
    {
        $field = match (true) {
            $subject instanceof Doctor      => 'doctor',
            $subject instanceof ClinicStaff => 'staff',
            $subject instanceof Room        => 'room',
        };

        $qb = $this->createQueryBuilder('r')
            ->where("r.$field = :subject")
            ->setParameter('subject', $subject);

        // اتاق فقط در یک آدرس است، پس آدرس برایش شرط اضافه نیست.
        if ($address !== null && !$subject instanceof Room) {
            $qb->andWhere('r.address = :address')->setParameter('address', $address);
        }

        return $qb->setMaxResults(1)->getQuery()->getOneOrNullResult();
    }

    /**
     * همهٔ منابعِ یک موجودیت در همهٔ شعبه‌ها — پزشکی که در دو شعبه کار می‌کند دو منبع
     * دارد و غیرفعال شدنش باید هر دو را ببندد.
     *
     * @return ClinicResource[]
     */
    public function findAllForSubject(Doctor|ClinicStaff|Room $subject): array
    {
        $field = match (true) {
            $subject instanceof Doctor      => 'doctor',
            $subject instanceof ClinicStaff => 'staff',
            $subject instanceof Room        => 'room',
        };

        return $this->createQueryBuilder('r')
            ->where("r.$field = :subject")
            ->setParameter('subject', $subject)
            ->getQuery()
            ->getResult();
    }

    public function countForType(ResourceType $type): int
    {
        return (int) $this->createQueryBuilder('r')
            ->select('COUNT(r.id)')
            ->where('r.type = :type')
            ->setParameter('type', $type)
            ->getQuery()
            ->getSingleScalarResult();
    }

    /**
     * @param int[] $typeIds
     * @return array<int, int> شناسهٔ نوع => تعداد منبع
     */
    public function countByTypeIds(array $typeIds): array
    {
        if ($typeIds === []) {
            return [];
        }

        $rows = $this->createQueryBuilder('r')
            ->select('IDENTITY(r.type) AS type_id, COUNT(r.id) AS total')
            ->where('r.type IN (:ids)')
            ->setParameter('ids', $typeIds)
            ->groupBy('r.type')
            ->getQuery()
            ->getArrayResult();

        $counts = [];
        foreach ($rows as $row) {
            $counts[(int) $row['type_id']] = (int) $row['total'];
        }

        return $counts;
    }
}
