<?php

namespace App\Billing\Repository;

use App\Billing\Entity\Claim;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\Tools\Pagination\Paginator;
use Doctrine\Persistence\ManagerRegistry;

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

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

    /**
     * @param array{status?:?string, insurance_id?:?int, from?:?int, to?:?int, q?:?string} $filters
     * @return Claim[]
     */
    public function findByTenant(string $entityType, int $entityId, array $filters = [], int $page = 1, int $limit = 50): array
    {
        // fetch-join items so enrichClaims()/toArray() don't lazy-load the
        // items collection per claim (N+1). fetchJoinCollection keeps the LIMIT
        // paginating by claim.
        $qb = $this->tenantQb($entityType, $entityId, $filters)
            ->addSelect('i')
            ->leftJoin('c.items', 'i')
            ->orderBy('c.id', 'DESC')
            ->setFirstResult(($page - 1) * $limit)
            ->setMaxResults($limit);

        return iterator_to_array(new Paginator($qb, fetchJoinCollection: true));
    }

    public function countByTenant(string $entityType, int $entityId, array $filters = []): int
    {
        return (int) $this->tenantQb($entityType, $entityId, $filters)
            ->select('COUNT(c.id)')
            ->getQuery()
            ->getSingleScalarResult();
    }

    private function tenantQb(string $entityType, int $entityId, array $filters): \Doctrine\ORM\QueryBuilder
    {
        $qb = $this->createQueryBuilder('c')
            ->where('c.entityType = :type')
            ->andWhere('c.entityId = :id')
            ->setParameter('type', $entityType)
            ->setParameter('id', $entityId);

        if (!empty($filters['status'])) {
            $qb->andWhere('c.status = :status')->setParameter('status', $filters['status']);
        }
        if (!empty($filters['insurance_id'])) {
            $qb->andWhere('c.insuranceId = :insId')->setParameter('insId', (int) $filters['insurance_id']);
        }
        if (!empty($filters['from'])) {
            $qb->andWhere('c.createdAt >= :from')->setParameter('from', (int) $filters['from']);
        }
        if (!empty($filters['to'])) {
            $qb->andWhere('c.createdAt <= :to')->setParameter('to', (int) $filters['to']);
        }

        // فیلتر بیمار: مطالباتی که آیتم‌شان به صورتحساب پرونده‌ی همان کاربر اشاره دارد (موبایل/کدملی/نام).
        if (!empty($filters['q'])) {
            $q = trim((string) $filters['q']);
            $qb->andWhere(
                'EXISTS (' .
                    'SELECT 1 FROM App\Billing\Entity\ClaimItem ci2 ' .
                    'JOIN App\Billing\Entity\InvoiceItem ii2 WITH ii2.id = ci2.invoiceItemId ' .
                    'JOIN App\Billing\Entity\Invoice inv2 WITH inv2 = ii2.invoice ' .
                    'JOIN App\Patient\Entity\PatientRecord pr2 WITH pr2.id = inv2.patientRecordId ' .
                    'JOIN App\Auth\Entity\User u2 WITH u2 = pr2.user ' .
                    'WHERE ci2.claim = c AND (' .
                        'u2.mobileNumber LIKE :q OR u2.nationalCode LIKE :q OR u2.realName LIKE :q' .
                    ')' .
                ')'
            )->setParameter('q', '%' . $q . '%');
        }

        return $qb;
    }

    /**
     * گزارش بدهی بیمه: جمع claimed/approved/paid بر اساس بیمه برای tenant.
     * @return array<int, array{insurance_id:int, claimed:int, approved:int, paid:int, debt:int}>
     */
    public function debtReport(string $entityType, int $entityId): array
    {
        $rows = $this->createQueryBuilder('c')
            ->select('c.insuranceId AS insurance_id')
            ->addSelect('SUM(c.totalClaimedRials) AS claimed')
            ->addSelect('SUM(COALESCE(c.totalApprovedRials, 0)) AS approved')
            ->addSelect('SUM(COALESCE(c.totalPaidRials, 0)) AS paid')
            ->where('c.entityType = :type')
            ->andWhere('c.entityId = :id')
            ->setParameter('type', $entityType)
            ->setParameter('id', $entityId)
            ->groupBy('c.insuranceId')
            ->getQuery()
            ->getArrayResult();

        return array_map(function (array $r) {
            $claimed  = (int) $r['claimed'];
            $paid     = (int) $r['paid'];
            return [
                'insurance_id' => (int) $r['insurance_id'],
                'claimed'      => $claimed,
                'approved'     => (int) $r['approved'],
                'paid'         => $paid,
                'debt'         => max(0, $claimed - $paid),
            ];
        }, $rows);
    }

    /** آیا برای این صورتحساب از قبل مطالبه‌ای ساخته شده؟ (از طریق آیتم‌های صورتحساب) */
    public function existsForInvoice(int $invoiceId): bool
    {
        $count = (int) $this->createQueryBuilder('c')
            ->select('COUNT(c.id)')
            ->join('c.items', 'ci')
            ->join('App\Billing\Entity\InvoiceItem', 'ii', 'WITH', 'ii.id = ci.invoiceItemId')
            ->where('IDENTITY(ii.invoice) = :invoiceId')
            ->setParameter('invoiceId', $invoiceId)
            ->getQuery()
            ->getSingleScalarResult();

        return $count > 0;
    }

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