<?php

declare(strict_types=1);

namespace App\Repository;

use App\Entity\AnneeScolaire;
use App\Entity\Ecole;
use App\Entity\Etudiant;
use App\Entity\Paiement;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;

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

    /** @return list<Paiement> */
    public function searchForEcole(Ecole $ecole, array $filters = []): array
    {
        $qb = $this->createQueryBuilder('p')
            ->leftJoin('p.etudiant', 'e')
            ->addSelect('e')
            ->andWhere('p.ecole = :ecole')
            ->setParameter('ecole', $ecole)
            ->orderBy('p.datePaiement', 'DESC')
            ->addOrderBy('p.id', 'DESC');

        if (!empty($filters['q'])) {
            $qb->andWhere('LOWER(p.numeroRecu) LIKE :q OR LOWER(e.nom) LIKE :q OR LOWER(e.prenom) LIKE :q OR LOWER(e.matricule) LIKE :q')
                ->setParameter('q', '%'.mb_strtolower(trim((string) $filters['q'])).'%');
        }

        if (!empty($filters['statut'])) {
            $qb->andWhere('p.statut = :statut')->setParameter('statut', $filters['statut']);
        }

        if (!empty($filters['modePaiement'])) {
            $qb->andWhere('p.modePaiement = :mode')->setParameter('mode', $filters['modePaiement']);
        }

        if (($filters['anneeScolaire'] ?? null) instanceof AnneeScolaire) {
            $qb->andWhere('p.anneeScolaire = :annee')->setParameter('annee', $filters['anneeScolaire']);
        }

        if (($filters['dateFrom'] ?? null) instanceof \DateTimeInterface) {
            $qb->andWhere('p.datePaiement >= :dateFrom')->setParameter('dateFrom', $filters['dateFrom']);
        }

        if (($filters['dateTo'] ?? null) instanceof \DateTimeInterface) {
            $qb->andWhere('p.datePaiement <= :dateTo')->setParameter('dateTo', $filters['dateTo']);
        }

        return $qb->getQuery()->getResult();
    }

    /** @return list<Paiement> */
    public function findByStudent(Etudiant $etudiant): array
    {
        return $this->createQueryBuilder('p')
            ->andWhere('p.etudiant = :etudiant')
            ->setParameter('etudiant', $etudiant)
            ->orderBy('p.datePaiement', 'DESC')
            ->addOrderBy('p.id', 'DESC')
            ->getQuery()
            ->getResult();
    }

    /** @return list<Paiement> */
    public function findByEcoleAndYear(Ecole $ecole, ?AnneeScolaire $anneeScolaire): array
    {
        $qb = $this->createQueryBuilder('p')
            ->andWhere('p.ecole = :ecole')
            ->setParameter('ecole', $ecole)
            ->orderBy('p.datePaiement', 'DESC')
            ->addOrderBy('p.id', 'DESC');

        $anneeScolaire
            ? $qb->andWhere('p.anneeScolaire = :annee')->setParameter('annee', $anneeScolaire)
            : $qb->andWhere('p.anneeScolaire IS NULL');

        return $qb->getQuery()->getResult();
    }

    /** @return list<Paiement> */
    public function findRecentByEcole(Ecole $ecole, int $limit = 10): array
    {
        return $this->createQueryBuilder('p')
            ->andWhere('p.ecole = :ecole')
            ->setParameter('ecole', $ecole)
            ->orderBy('p.datePaiement', 'DESC')
            ->addOrderBy('p.id', 'DESC')
            ->setMaxResults($limit)
            ->getQuery()
            ->getResult();
    }

    public function findLastReceiptNumberWithPrefix(string $prefix): ?string
    {
        return $this->createQueryBuilder('p')
            ->select('MAX(p.numeroRecu)')
            ->andWhere('p.numeroRecu LIKE :prefix')
            ->setParameter('prefix', $prefix.'%')
            ->getQuery()
            ->getSingleScalarResult() ?: null;
    }

    public function sumValidatedForEcole(Ecole $ecole): float
    {
        return (float) ($this->createQueryBuilder('p')
            ->select('COALESCE(SUM(p.montantTotal), 0)')
            ->andWhere('p.ecole = :ecole')
            ->andWhere('p.statut = :statut')
            ->setParameter('ecole', $ecole)
            ->setParameter('statut', Paiement::STATUT_VALIDE)
            ->getQuery()
            ->getSingleScalarResult() ?? 0);
    }

    public function sumValidatedForCurrentMonth(Ecole $ecole): float
    {
        $start = new \DateTimeImmutable('first day of this month 00:00:00');
        $end = new \DateTimeImmutable('last day of this month 23:59:59');

        return (float) ($this->createQueryBuilder('p')
            ->select('COALESCE(SUM(p.montantTotal), 0)')
            ->andWhere('p.ecole = :ecole')
            ->andWhere('p.statut = :statut')
            ->andWhere('p.datePaiement BETWEEN :start AND :end')
            ->setParameter('ecole', $ecole)
            ->setParameter('statut', Paiement::STATUT_VALIDE)
            ->setParameter('start', $start)
            ->setParameter('end', $end)
            ->getQuery()
            ->getSingleScalarResult() ?? 0);
    }

    public function countForEcole(Ecole $ecole, ?string $statut = null): int
    {
        $qb = $this->createQueryBuilder('p')
            ->select('COUNT(p.id)')
            ->andWhere('p.ecole = :ecole')
            ->setParameter('ecole', $ecole);

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

        return (int) $qb->getQuery()->getSingleScalarResult();
    }
}
