<?php

declare(strict_types=1);

namespace App\Controller\School;

use App\Service\School\SchoolContext;
use App\Entity\CategorieDepense;
use App\Entity\Depense;
use App\Entity\Ecole;
use App\Entity\MouvementCaisse;
use App\Form\Finance\CategorieDepenseType;
use App\Form\Finance\DepenseType;
use App\Repository\AnneeScolaireRepository;
use App\Repository\CategorieDepenseRepository;
use App\Repository\DepenseRepository;
use App\Repository\MouvementCaisseRepository;
use App\Service\Finance\CaisseService;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;

#[Route('/school/depenses', name: 'school_depense_')]
#[IsGranted('ROLE_ECOLE_ADMIN')]
final class DepenseController extends AbstractController
{
    public function __construct(private readonly SchoolContext $schoolContext)
    {
    }

    #[Route('', name: 'index', methods: ['GET'])]
    public function index(
        Request $request,
        DepenseRepository $depenseRepository,
        CategorieDepenseRepository $categorieRepository,
        AnneeScolaireRepository $anneeRepository,
    ): Response {
        $ecole = $this->schoolContext->getEcole();
        $filters = $this->buildFilters($request, $anneeRepository, $categorieRepository, $ecole);

        return $this->render('school/depense/index.html.twig', [
            'ecole' => $ecole,
            'currentAnnee' => $anneeRepository->findCurrentForEcole($ecole),
            'depenses' => $depenseRepository->searchForEcole($ecole, $filters),
            'categories' => $categorieRepository->findByEcoleOrdered($ecole),
            'annees' => $anneeRepository->findBy(['ecole' => $ecole], ['dateDebut' => 'DESC', 'id' => 'DESC']),
            'filters' => $filters,
            'stats' => [
                'totalDepenses' => $depenseRepository->sumValidatedForEcole($ecole),
                'depensesMois' => $depenseRepository->sumValidatedForCurrentMonth($ecole),
                'nombreDepenses' => $depenseRepository->countForEcole($ecole),
                'annulees' => $depenseRepository->countForEcole($ecole, Depense::STATUT_ANNULEE),
            ],
        ]);
    }

    #[Route('/new', name: 'new', methods: ['GET', 'POST'])]
    public function new(Request $request, AnneeScolaireRepository $anneeRepository, CaisseService $caisseService, EntityManagerInterface $em): Response
    {
        $ecole = $this->schoolContext->getEcole();
        $depense = (new Depense())
            ->setEcole($ecole)
            ->setDateDepense(new \DateTimeImmutable())
            ->setStatut(Depense::STATUT_VALIDEE);

        $form = $this->createForm(DepenseType::class, $depense, ['ecole' => $ecole]);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $this->schoolContext->assertSameSchool($depense->getCategorie()?->getEcole());
            if ($depense->getAnneeScolaire()) {
                $this->schoolContext->assertSameSchool($depense->getAnneeScolaire()->getEcole());
            }
            $em->persist($depense);
            $em->flush();
            $caisseService->createExitFromDepense($depense);
            $this->addFlash('success', 'La dépense a été enregistrée et la sortie de caisse créée.');

            return $this->redirectToRoute('school_depense_show', ['id' => $depense->getId()]);
        }

        return $this->render('school/depense/form.html.twig', [
            'ecole' => $ecole,
            'currentAnnee' => $anneeRepository->findCurrentForEcole($ecole),
            'form' => $form,
            'depense' => $depense,
            'title' => 'Nouvelle dépense',
        ]);
    }

    #[Route('/categories', name: 'category_index', methods: ['GET'])]
    public function categories(CategorieDepenseRepository $repository, AnneeScolaireRepository $anneeRepository): Response
    {
        $ecole = $this->schoolContext->getEcole();

        return $this->render('school/depense/category_index.html.twig', [
            'ecole' => $ecole,
            'currentAnnee' => $anneeRepository->findCurrentForEcole($ecole),
            'categories' => $repository->findByEcoleOrdered($ecole),
        ]);
    }

    #[Route('/categories/new', name: 'category_new', methods: ['GET', 'POST'])]
    public function newCategory(Request $request, CategorieDepenseRepository $repository, AnneeScolaireRepository $anneeRepository, EntityManagerInterface $em): Response
    {
        $ecole = $this->schoolContext->getEcole();
        $categorie = (new CategorieDepense())->setEcole($ecole)->setCode('NOUVELLE')->setLibelle('Nouvelle catégorie')->setOrdre(1)->setActif(true);
        $form = $this->createForm(CategorieDepenseType::class, $categorie);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            if ($repository->findOneBy(['ecole' => $ecole, 'code' => $categorie->getCode()])) {
                $this->addFlash('danger', 'Ce code existe déjà pour cette madrassa.');
            } else {
                $em->persist($categorie);
                $em->flush();
                $this->addFlash('success', 'La catégorie de dépense a été créée.');

                return $this->redirectToRoute('school_depense_category_index');
            }
        }

        return $this->render('school/depense/category_form.html.twig', [
            'ecole' => $ecole,
            'currentAnnee' => $anneeRepository->findCurrentForEcole($ecole),
            'form' => $form,
            'title' => 'Nouvelle catégorie',
        ]);
    }

    #[Route('/categories/{id}/edit', name: 'category_edit', requirements: ['id' => '\d+'], methods: ['GET', 'POST'])]
    public function editCategory(Request $request, CategorieDepense $categorie, CategorieDepenseRepository $repository, AnneeScolaireRepository $anneeRepository, EntityManagerInterface $em): Response
    {
        $ecole = $this->schoolContext->getEcole();
        $this->schoolContext->assertSameSchool($categorie->getEcole());
        $form = $this->createForm(CategorieDepenseType::class, $categorie);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $existing = $repository->findOneBy(['ecole' => $ecole, 'code' => $categorie->getCode()]);
            if ($existing instanceof CategorieDepense && $existing->getId() !== $categorie->getId()) {
                $this->addFlash('danger', 'Ce code existe déjà pour cette madrassa.');
            } else {
                $em->flush();
                $this->addFlash('success', 'La catégorie a été mise à jour.');

                return $this->redirectToRoute('school_depense_category_index');
            }
        }

        return $this->render('school/depense/category_form.html.twig', [
            'ecole' => $ecole,
            'currentAnnee' => $anneeRepository->findCurrentForEcole($ecole),
            'form' => $form,
            'title' => 'Modifier une catégorie',
        ]);
    }

    #[Route('/categories/{id}/delete', name: 'category_delete', requirements: ['id' => '\d+'], methods: ['POST'])]
    public function deleteCategory(Request $request, CategorieDepense $categorie, DepenseRepository $depenseRepository, EntityManagerInterface $em): Response
    {
        $this->schoolContext->assertSameSchool($categorie->getEcole());
        if ($this->isCsrfTokenValid('delete_categorie_depense_'.$categorie->getId(), (string) $request->request->get('_token'))) {
            if ($depenseRepository->count(['categorie' => $categorie]) > 0) {
                $categorie->setActif(false);
                $this->addFlash('warning', 'Cette catégorie est déjà utilisée. Elle a été désactivée.');
            } else {
                $em->remove($categorie);
                $this->addFlash('success', 'La catégorie a été supprimée.');
            }
            $em->flush();
        }

        return $this->redirectToRoute('school_depense_category_index');
    }

    #[Route('/{id}', name: 'show', requirements: ['id' => '\d+'], methods: ['GET'])]
    public function show(Depense $depense, AnneeScolaireRepository $anneeRepository, MouvementCaisseRepository $mouvementRepository): Response
    {
        $this->schoolContext->assertSameSchool($depense->getEcole());
        $ecole = $this->schoolContext->getEcole();

        return $this->render('school/depense/show.html.twig', [
            'ecole' => $ecole,
            'currentAnnee' => $anneeRepository->findCurrentForEcole($ecole),
            'depense' => $depense,
            'mouvements' => $mouvementRepository->findBy(['depense' => $depense], ['dateMouvement' => 'DESC', 'id' => 'DESC']),
        ]);
    }

    #[Route('/{id}/edit', name: 'edit', requirements: ['id' => '\d+'], methods: ['GET', 'POST'])]
    public function edit(
        Request $request,
        Depense $depense,
        AnneeScolaireRepository $anneeRepository,
        MouvementCaisseRepository $mouvementRepository,
        EntityManagerInterface $em,
    ): Response {
        $ecole = $this->schoolContext->getEcole();
        $this->schoolContext->assertSameSchool($depense->getEcole());
        $form = $this->createForm(DepenseType::class, $depense, ['ecole' => $ecole]);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $this->schoolContext->assertSameSchool($depense->getCategorie()?->getEcole());
            if ($depense->getAnneeScolaire()) {
                $this->schoolContext->assertSameSchool($depense->getAnneeScolaire()->getEcole());
            }
            $mouvement = $mouvementRepository->findOneBy(['depense' => $depense, 'sourceType' => MouvementCaisse::SOURCE_DEPENSE]);
            if ($mouvement instanceof MouvementCaisse && $depense->getStatut() === Depense::STATUT_VALIDEE) {
                $mouvement
                    ->setAnneeScolaire($depense->getAnneeScolaire())
                    ->setMontant($depense->getMontant() ?: 0)
                    ->setDateMouvement($depense->getDateDepense() ?: new \DateTimeImmutable())
                    ->setLibelle($depense->getDescription() ?: 'Dépense');
            }
            $em->flush();
            $this->addFlash('success', 'La dépense a été mise à jour.');

            return $this->redirectToRoute('school_depense_show', ['id' => $depense->getId()]);
        }

        return $this->render('school/depense/form.html.twig', [
            'ecole' => $ecole,
            'currentAnnee' => $anneeRepository->findCurrentForEcole($ecole),
            'form' => $form,
            'depense' => $depense,
            'title' => 'Modifier une dépense',
        ]);
    }

    #[Route('/{id}/annuler', name: 'cancel', requirements: ['id' => '\d+'], methods: ['POST'])]
    public function cancel(Request $request, Depense $depense, CaisseService $caisseService, EntityManagerInterface $em): Response
    {
        $this->schoolContext->assertSameSchool($depense->getEcole());
        if (!$this->isCsrfTokenValid('annuler_depense_'.$depense->getId(), (string) $request->request->get('_token'))) {
            $this->addFlash('danger', 'Jeton de sécurité invalide.');

            return $this->redirectToRoute('school_depense_show', ['id' => $depense->getId()]);
        }

        $this->cancelDepense($depense, $caisseService, $em);

        return $this->redirectToRoute('school_depense_show', ['id' => $depense->getId()]);
    }

    #[Route('/{id}/delete', name: 'delete', requirements: ['id' => '\d+'], methods: ['POST'])]
    public function delete(Request $request, Depense $depense, MouvementCaisseRepository $mouvementRepository, CaisseService $caisseService, EntityManagerInterface $em): Response
    {
        $this->schoolContext->assertSameSchool($depense->getEcole());
        if ($this->isCsrfTokenValid('delete_depense_'.$depense->getId(), (string) $request->request->get('_token'))) {
            if ($mouvementRepository->count(['depense' => $depense]) > 0) {
                $this->cancelDepense($depense, $caisseService, $em);
            } else {
                $em->remove($depense);
                $em->flush();
                $this->addFlash('success', 'La dépense a été supprimée.');
            }
        }

        return $this->redirectToRoute('school_depense_index');
    }

    private function cancelDepense(Depense $depense, CaisseService $caisseService, EntityManagerInterface $em): void
    {
        if ($depense->getStatut() === Depense::STATUT_ANNULEE) {
            $this->addFlash('warning', 'Cette dépense est déjà annulée.');

            return;
        }

        $depense->setStatut(Depense::STATUT_ANNULEE);
        $em->flush();
        $caisseService->createCancellationEntryFromDepense($depense);
        $this->addFlash('success', 'La dépense a été annulée et un mouvement inverse d’entrée a été créé.');
    }

    private function buildFilters(Request $request, AnneeScolaireRepository $anneeRepository, CategorieDepenseRepository $categorieRepository, Ecole $ecole): array
    {
        $filters = [
            'q' => trim((string) $request->query->get('q', '')),
            'statut' => trim((string) $request->query->get('statut', '')),
            'categorie' => null,
            'categorieId' => (int) $request->query->get('categorie', 0),
            'anneeScolaire' => null,
            'anneeId' => (int) $request->query->get('annee', 0),
            'dateFromRaw' => trim((string) $request->query->get('dateFrom', '')),
            'dateToRaw' => trim((string) $request->query->get('dateTo', '')),
            'dateFrom' => null,
            'dateTo' => null,
        ];

        if ($filters['categorieId'] > 0) {
            $categorie = $categorieRepository->find($filters['categorieId']);
            if ($categorie && $categorie->getEcole()?->getId() === $ecole->getId()) {
                $filters['categorie'] = $categorie;
            }
        }

        if ($filters['anneeId'] > 0) {
            $annee = $anneeRepository->find($filters['anneeId']);
            if ($annee && $annee->getEcole()?->getId() === $ecole->getId()) {
                $filters['anneeScolaire'] = $annee;
            }
        }

        try {
            if ($filters['dateFromRaw'] !== '') {
                $filters['dateFrom'] = new \DateTimeImmutable($filters['dateFromRaw']);
            }
            if ($filters['dateToRaw'] !== '') {
                $filters['dateTo'] = new \DateTimeImmutable($filters['dateToRaw']);
            }
        } catch (\Exception) {
            $filters['dateFrom'] = null;
            $filters['dateTo'] = null;
        }

        return $filters;
    }

}
