<?php

declare(strict_types=1);

namespace App\Service\Finance;

use App\Entity\AnneeScolaire;
use App\Entity\EcheancePaiement;
use App\Entity\Ecole;
use App\Entity\Etudiant;
use App\Entity\MouvementCaisse;
use App\Entity\ParcoursEtudiant;
use App\Repository\EcheancePaiementRepository;
use App\Repository\LignePaiementRepository;
use App\Repository\MouvementCaisseRepository;
use Doctrine\ORM\EntityManagerInterface;

final class FinanceCalculator
{
    public function __construct(
        private readonly EcheancePaiementRepository $echeanceRepository,
        private readonly LignePaiementRepository $lignePaiementRepository,
        private readonly MouvementCaisseRepository $mouvementCaisseRepository,
        private readonly EntityManagerInterface $em,
    ) {
    }

    /** @return array{totalDu: float, totalPaye: float, reste: float, statut: string, echeances: list<EcheancePaiement>} */
    public function getStudentSummary(Etudiant $etudiant, ?AnneeScolaire $annee = null): array
    {
        return $this->summarize($this->echeanceRepository->findActiveByStudent($etudiant, $annee));
    }

    /** @return array{totalDu: float, totalPaye: float, reste: float, statut: string, echeances: list<EcheancePaiement>} */
    public function getParcoursSummary(ParcoursEtudiant $parcours): array
    {
        return $this->summarize($this->echeanceRepository->findActiveByParcours($parcours));
    }

    /** @return array{totalEntrees: float, totalSorties: float, solde: float} */
    public function getSchoolCashSummary(Ecole $ecole, ?AnneeScolaire $annee = null): array
    {
        $totalEntrees = $this->mouvementCaisseRepository->sumByEcoleAndType($ecole, MouvementCaisse::TYPE_ENTREE, $annee);
        $totalSorties = $this->mouvementCaisseRepository->sumByEcoleAndType($ecole, MouvementCaisse::TYPE_SORTIE, $annee);

        return [
            'totalEntrees' => $totalEntrees,
            'totalSorties' => $totalSorties,
            'solde' => $totalEntrees - $totalSorties,
        ];
    }

    public function recalculateEcheancePaidAmount(EcheancePaiement $echeance): void
    {
        $echeance->setMontantPaye($this->lignePaiementRepository->sumPaidByEcheance($echeance));
    }

    public function updateEcheanceStatus(EcheancePaiement $echeance): void
    {
        if ($echeance->getStatut() === EcheancePaiement::STATUT_ANNULE) {
            return;
        }

        $this->recalculateEcheancePaidAmount($echeance);

        $paid = (float) $echeance->getMontantPaye();
        $net = (float) ($echeance->getMontantNet() ?? 0);
        $remaining = max(0.0, $net - $paid);
        $today = new \DateTimeImmutable('today');
        $dueDate = $echeance->getDateEcheance();
        $overdue = $dueDate instanceof \DateTimeInterface && \DateTimeImmutable::createFromInterface($dueDate) < $today;

        if ($paid >= $net && $net > 0) {
            $echeance->setStatut(EcheancePaiement::STATUT_PAYE);
        } elseif ($overdue && $remaining > 0) {
            $echeance->setStatut(EcheancePaiement::STATUT_EN_RETARD);
        } elseif ($paid > 0) {
            $echeance->setStatut(EcheancePaiement::STATUT_PARTIEL);
        } else {
            $echeance->setStatut(EcheancePaiement::STATUT_NON_PAYE);
        }

        $this->em->persist($echeance);
    }

    /** @param list<EcheancePaiement> $echeances @return array{totalDu: float, totalPaye: float, reste: float, statut: string, echeances: list<EcheancePaiement>} */
    private function summarize(array $echeances): array
    {
        $totalDu = 0.0;
        $totalPaye = 0.0;
        $hasOverdue = false;
        $today = new \DateTimeImmutable('today');

        foreach ($echeances as $echeance) {
            $this->recalculateEcheancePaidAmount($echeance);
            $totalDu += (float) ($echeance->getMontantNet() ?? 0);
            $totalPaye += (float) $echeance->getMontantPaye();

            $dueDate = $echeance->getDateEcheance();
            if ($echeance->getStatut() !== EcheancePaiement::STATUT_ANNULE && $dueDate instanceof \DateTimeInterface && \DateTimeImmutable::createFromInterface($dueDate) < $today) {
                $hasOverdue = true;
            }
        }

        $reste = max(0.0, $totalDu - $totalPaye);

        if ($totalDu <= 0 || $totalPaye <= 0) {
            $statut = $hasOverdue && $reste > 0 ? EcheancePaiement::STATUT_EN_RETARD : EcheancePaiement::STATUT_NON_PAYE;
        } elseif ($reste <= 0) {
            $statut = EcheancePaiement::STATUT_PAYE;
        } elseif ($hasOverdue) {
            $statut = EcheancePaiement::STATUT_EN_RETARD;
        } else {
            $statut = EcheancePaiement::STATUT_PARTIEL;
        }

        return [
            'totalDu' => $totalDu,
            'totalPaye' => $totalPaye,
            'reste' => $reste,
            'statut' => $statut,
            'echeances' => $echeances,
        ];
    }
}
