<?php

namespace App\Clinic\Repository;

use App\Auth\Entity\User;
use App\Clinic\Entity\Clinic;
use App\Doctor\Entity\Doctor;
use App\Doctor\Entity\DoctorAddress;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\Tools\Pagination\Paginator;
use Doctrine\Persistence\ManagerRegistry;

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

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

    /** @return Clinic[] */
    public function findByDoctor(Doctor $doctor): array
    {
        return $this->createQueryBuilder('c')
            ->innerJoin('c.doctors', 'd')
            ->where('d.id = :doctorId')
            ->setParameter('doctorId', $doctor->getId())
            ->getQuery()
            ->getResult();
    }

    public function findByUser(User $user): ?Clinic
    {
        return $this->findOneBy(['user' => $user]);
    }

    public function findWithFilters(array $filters): array
    {
        $page  = max(1, (int) ($filters['page'] ?? 1));
        $limit = min(50, max(1, (int) ($filters['limit'] ?? 10)));
        $sort  = strtoupper($filters['sort'] ?? 'DESC') === 'ASC' ? 'ASC' : 'DESC';

        $qb = $this->createQueryBuilder('c')
            ->leftJoin('c.specialties', 's')
            ->distinct();

        // City/province live on the clinic's address (DoctorAddress.clinicId),
        // not on the clinic itself. DoctorAddress has no Doctrine relation to
        // Clinic, so join on the scalar clinicId with a WITH condition.
        if (!empty($filters['city']) || !empty($filters['state'])) {
            $qb->join(DoctorAddress::class, 'addr', Join::WITH, 'addr.clinicId = c.id');

            if (!empty($filters['city'])) {
                $qb->andWhere('IDENTITY(addr.city) = :city')->setParameter('city', (int) $filters['city']);
            }
            if (!empty($filters['state'])) {
                $qb->andWhere('IDENTITY(addr.province) = :state')->setParameter('state', (int) $filters['state']);
            }
        }
        if (!empty($filters['specialty'])) {
            $qb->andWhere('s.id = :specialty')->setParameter('specialty', (int) $filters['specialty']);
        }
        // Scope دامنه‌ی نماینده‌ی سراسری (تزریق‌شده توسط DomainContextResolver در کنترلر).
        if (!empty($filters['representation_id'])) {
            $qb->andWhere('c.representationId = :repId')->setParameter('repId', (int) $filters['representation_id']);
        }

        $qb->orderBy('c.id', $sort);

        $total   = (new Paginator($qb))->count();
        $results = $qb->setFirstResult(($page - 1) * $limit)
                      ->setMaxResults($limit)
                      ->getQuery()
                      ->getResult();

        return [
            'items'      => $results,
            'total'      => $total,
            'page'       => $page,
            'limit'      => $limit,
            'totalPages' => (int) ceil($total / $limit),
        ];
    }

    /**
     * City/province names for each clinic, taken from the clinic's address
     * (DoctorAddress.clinicId). Returns [clinicId => ['city' => ?string, 'state' => ?string]].
     *
     * @param int[] $clinicIds
     * @return array<int, array{city: ?string, state: ?string}>
     */
    public function findAddressLocations(array $clinicIds): array
    {
        if ($clinicIds === []) {
            return [];
        }

        $rows = $this->getEntityManager()->createQueryBuilder()
            ->select('addr.clinicId AS clinic_id', 'cityCat.name AS city', 'provinceCat.name AS state')
            ->from(DoctorAddress::class, 'addr')
            ->leftJoin('addr.city', 'cityCat')
            ->leftJoin('addr.province', 'provinceCat')
            ->where('addr.clinicId IN (:ids)')
            ->setParameter('ids', $clinicIds)
            ->getQuery()
            ->getResult();

        $map = [];
        foreach ($rows as $row) {
            $id = (int) $row['clinic_id'];
            if (!isset($map[$id])) {
                $map[$id] = ['city' => $row['city'], 'state' => $row['state']];
            }
        }

        return $map;
    }

    public function save(Clinic $clinic, bool $flush = true): void
    {
        $this->getEntityManager()->persist($clinic);
        if ($flush) {
            $this->getEntityManager()->flush();
        }
    }

    public function remove(Clinic $clinic, bool $flush = true): void
    {
        $this->getEntityManager()->remove($clinic);
        if ($flush) {
            $this->getEntityManager()->flush();
        }
    }
}
