<?php

declare(strict_types=1);

namespace App\Controller\School;

use App\Service\School\SchoolContext;
use App\Entity\Ecole;
use App\Entity\Etudiant;
use App\Entity\Inscription;
use App\Entity\Section;
use App\Entity\Niveau;
use App\Entity\Classe;
use App\Form\EtudiantType;
use App\Repository\AnneeScolaireRepository;
use App\Repository\EtudiantRepository;
use App\Service\MatriculeGenerator;
use Doctrine\ORM\EntityManagerInterface;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;

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

    #[Route('', name: 'index', methods: ['GET'])]
    public function index(Request $request, EtudiantRepository $repository, AnneeScolaireRepository $anneeRepository): Response
    {
        $ecole = $this->schoolContext->getEcole();
        $annees = $anneeRepository->findForEcole($ecole);
        $currentOfficialAnnee = $anneeRepository->findCurrentForEcole($ecole);
        $scope = $this->scope($request);
        $selectedAnnee = $this->selectedAnnee($request, $annees, $scope === 'current' ? $currentOfficialAnnee : null);

        return $this->render('school/etudiant/index.html.twig', [
            'ecole' => $ecole,
            'currentAnnee' => $selectedAnnee,
            'anneeEnCours' => $currentOfficialAnnee,
            'annees' => $annees,
            'scope' => $scope,
            'etudiants' => $repository->findForEcole($ecole, $request->query->get('q'), $selectedAnnee, $scope, $currentOfficialAnnee),
            'search' => $request->query->get('q', ''),
        ]);
    }

    #[Route('/new', name: 'new', methods: ['GET', 'POST'])]
    public function new(Request $request, EntityManagerInterface $em, MatriculeGenerator $matriculeGenerator, AnneeScolaireRepository $anneeRepository): Response
    {
        $ecole = $this->schoolContext->getEcole();
        $currentAnnee = $anneeRepository->findCurrentForEcole($ecole);
        $etudiant = (new Etudiant())->setEcole($ecole)->setAnneeScolaire($currentAnnee)->setActif(true);
        $form = $this->createForm(EtudiantType::class, $etudiant, ['ecole' => $ecole]);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            if (!$etudiant->getMatricule()) {
                $etudiant->setMatricule($matriculeGenerator->generateMatriculeDefinitif($ecole));
            } else {
                $matriculeGenerator->syncDernierNumeroFromMatricule($ecole, $etudiant->getMatricule());
            }
            $em->persist($etudiant);
            $em->flush();
            $this->addFlash('success', 'L’étudiant a été créé.');

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

        return $this->render('school/etudiant/form.html.twig', ['ecole' => $ecole, 'currentAnnee' => $currentAnnee, 'form' => $form, 'title' => 'Nouvel étudiant']);
    }

    #[Route('/import', name: 'import', methods: ['GET', 'POST'])]
    public function import(Request $request, EntityManagerInterface $em, AnneeScolaireRepository $anneeRepository, MatriculeGenerator $matriculeGenerator): Response
    {
        $ecole = $this->schoolContext->getEcole();
        $annees = $anneeRepository->findForEcole($ecole);
        $currentOfficialAnnee = $anneeRepository->findCurrentForEcole($ecole);
        $selectedAnnee = $this->selectedAnnee($request, $annees, $currentOfficialAnnee);

        if ($request->isMethod('POST')) {
            $file = $request->files->get('file');
            $selectedAnnee = $this->selectedAnnee($request, $annees, $currentOfficialAnnee);

            if (!$file instanceof UploadedFile) {
                $this->addFlash('danger', 'Veuillez sélectionner un fichier Excel au format .xlsx.');
            } else {
                $extension = mb_strtolower($file->getClientOriginalExtension());
                if (!in_array($extension, ['xlsx', 'xls'], true)) {
                    $this->addFlash('danger', 'Le fichier doit être un document Excel .xlsx ou .xls.');
                } else {
                    $result = $this->importExcel($file, $ecole, $selectedAnnee, $currentOfficialAnnee, $em, $matriculeGenerator);
                    $this->addFlash('success', sprintf('%d étudiant(s) importé(s), %d ligne(s) ignorée(s).', $result['imported'], $result['skipped']));

                    $params = [];
                    if ($selectedAnnee) {
                        $params['annee'] = $selectedAnnee->getId();
                    }
                    $params['scope'] = ($selectedAnnee && $currentOfficialAnnee && $selectedAnnee->getId() !== $currentOfficialAnnee->getId()) ? 'archives' : 'current';

                    return $this->redirectToRoute('school_etudiant_index', $params);
                }
            }
        }

        return $this->render('school/etudiant/import.html.twig', [
            'ecole' => $ecole,
            'currentAnnee' => $selectedAnnee,
            'anneeEnCours' => $currentOfficialAnnee,
            'annees' => $annees,
        ]);
    }

    #[Route('/import/modele', name: 'import_modele', methods: ['GET'])]
    public function importTemplate(): StreamedResponse
    {
        $spreadsheet = new Spreadsheet();
        $sheet = $spreadsheet->getActiveSheet();
        $headers = [
            'matricule',
            'noms_et_prenoms',
            'sexe',
            'telephone_principal',
            'telephone_whatsapp',
            'email',
            'adresse',
            'a_des_enfants',
            'jours_disponibles',
            'plage_horaire',
            'section_slug',
            'niveau_slug',
            'classe_slug',
            'actif',
        ];

        foreach ($headers as $column => $header) {
            $sheet->setCellValueByColumnAndRow($column + 1, 1, $header);
        }

        $sheet->fromArray([
            ['26MQO001', 'Aïcha Ousman', 'FEMME', '697000000', '697000000', 'aicha@example.test', 'Yaoundé', 'non', 'Samedi, Dimanche', '20:00-21:30', 'section-femmes-en-ligne', 'qaida-nouraniyya', 'qaida-nouraniyya-groupe-a', 'oui'],
        ], null, 'A2');

        foreach (range(1, count($headers)) as $column) {
            $sheet->getColumnDimensionByColumn($column)->setAutoSize(true);
        }

        $response = new StreamedResponse(static function () use ($spreadsheet): void {
            (new Xlsx($spreadsheet))->save('php://output');
        });

        $filename = 'modele-import-etudiants.xlsx';
        $response->headers->set('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
        $response->headers->set('Content-Disposition', 'attachment; filename="'.$filename.'"');

        return $response;
    }

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

        return $this->render('school/etudiant/show.html.twig', ['ecole' => $ecole, 'currentAnnee' => $etudiant->getAnneeScolaire(), 'etudiant' => $etudiant]);
    }

    #[Route('/{id}/edit', name: 'edit', requirements: ['id' => '\\d+'], methods: ['GET', 'POST'])]
    public function edit(Request $request, Etudiant $etudiant, EntityManagerInterface $em, MatriculeGenerator $matriculeGenerator): Response
    {
        $ecole = $this->schoolContext->getEcole();
        $this->schoolContext->assertSameSchool($etudiant->getEcole());
        $form = $this->createForm(EtudiantType::class, $etudiant, ['ecole' => $ecole]);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $matriculeGenerator->syncDernierNumeroFromMatricule($ecole, $etudiant->getMatricule());
            $em->flush();
            $this->addFlash('success', 'L’étudiant a été mis à jour.');

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

        return $this->render('school/etudiant/form.html.twig', ['ecole' => $ecole, 'currentAnnee' => $etudiant->getAnneeScolaire(), 'form' => $form, 'title' => 'Modifier l’étudiant']);
    }

    #[Route('/{id}/delete', name: 'delete', requirements: ['id' => '\d+'], methods: ['POST'])]
    public function delete(Request $request, Etudiant $etudiant, EntityManagerInterface $em): Response
    {
        $ecole = $this->schoolContext->getEcole();
        $this->schoolContext->assertSameSchool($etudiant->getEcole());

        if ($this->isCsrfTokenValid('delete_etudiant_'.$etudiant->getId(), (string) $request->request->get('_token'))) {
            // Suppression réelle, mais sécurisée : les dossiers d’inscription liés sont conservés
            // et simplement détachés de l’étudiant pour éviter toute contrainte cassée.
            $linkedInscriptions = $em->getRepository(Inscription::class)->findBy([
                'ecole' => $ecole,
                'etudiant' => $etudiant,
            ]);

            foreach ($linkedInscriptions as $inscription) {
                $inscription->setEtudiant(null);
            }

            $em->remove($etudiant);
            $em->flush();
            $this->addFlash('success', 'L’étudiant a été supprimé définitivement. Les dossiers d’inscription liés ont été conservés.');
        }

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


    /** @param list<object> $annees */
    private function selectedAnnee(Request $request, array $annees, $default)
    {
        $id = $request->request->getInt('annee') ?: $request->query->getInt('annee');
        if ($id > 0) {
            foreach ($annees as $annee) {
                if ($annee->getId() === $id) {
                    return $annee;
                }
            }
        }

        return $default;
    }

    private function scope(Request $request): string
    {
        $scope = (string) $request->query->get('scope', 'current');

        return in_array($scope, ['current', 'archives', 'all'], true) ? $scope : 'current';
    }

    /** @return array{imported:int, skipped:int} */
    private function importExcel(UploadedFile $file, Ecole $ecole, $annee, $currentAnnee, EntityManagerInterface $em, MatriculeGenerator $matriculeGenerator): array
    {
        $spreadsheet = IOFactory::load($file->getPathname());
        $rows = $spreadsheet->getActiveSheet()->toArray(null, true, true, true);

        if ($rows === []) {
            return ['imported' => 0, 'skipped' => 0];
        }

        $headerRow = array_shift($rows);
        $headers = [];
        foreach ($headerRow as $column => $label) {
            $headers[$column] = $this->normalizeHeader((string) $label);
        }

        $imported = 0;
        $skipped = 0;
        $isArchiveAnnee = $annee && $currentAnnee && $annee->getId() !== $currentAnnee->getId();

        foreach ($rows as $row) {
            $data = [];
            foreach ($headers as $column => $key) {
                if ($key !== '') {
                    $data[$key] = trim((string) ($row[$column] ?? ''));
                }
            }

            $nom = $data['noms_et_prenoms'] ?? $data['noms'] ?? $data['nom'] ?? '';
            if ($nom === '') {
                ++$skipped;
                continue;
            }

            $matricule = $data['matricule'] ?? null;
            $matricule = $matricule !== null && trim($matricule) !== '' ? preg_replace('/\s+/', '', mb_strtoupper(trim($matricule))) : null;
            $etudiant = $matricule ? $em->getRepository(Etudiant::class)->findOneBy(['ecole' => $ecole, 'matricule' => $matricule]) : null;
            $etudiant ??= new Etudiant();

            $etudiant
                ->setEcole($ecole)
                ->setAnneeScolaire($annee)
                ->setMatricule($matricule)
                ->setNom($nom)
                ->setSexe($data['sexe'] ?? null)
                ->setTelephonePrincipal($data['telephone_principal'] ?? $data['telephone'] ?? null)
                ->setTelephoneWhatsapp($data['telephone_whatsapp'] ?? $data['whatsapp'] ?? null)
                ->setEmail($data['email'] ?? null)
                ->setAdresse($data['adresse'] ?? null)
                ->setADesEnfants(in_array(mb_strtolower($data['a_des_enfants'] ?? ''), ['1', 'oui', 'yes', 'true'], true))
                ->setJoursDisponibles($data['jours_disponibles'] ?? null)
                ->setPlageHoraireSouhaitee($data['plage_horaire'] ?? null)
                ->setAncien(true)
                ->setActif(!$this->isFalseValue($data['actif'] ?? 'oui'));

            if ($isArchiveAnnee) {
                $etudiant->setAncien(true);
            }

            if ($matricule) {
                $matriculeGenerator->syncDernierNumeroFromMatricule($ecole, $matricule);
            }

            if (!empty($data['section_slug'])) {
                $etudiant->setSection($em->getRepository(Section::class)->findOneBy(['ecole' => $ecole, 'slug' => $data['section_slug']]));
            }
            if (!empty($data['niveau_slug'])) {
                $etudiant->setNiveau($em->getRepository(Niveau::class)->findOneBy(['ecole' => $ecole, 'slug' => $data['niveau_slug']]));
            }
            if (!empty($data['classe_slug'])) {
                $etudiant->setClasse($em->getRepository(Classe::class)->findOneBy(['ecole' => $ecole, 'slug' => $data['classe_slug']]));
            }

            $em->persist($etudiant);
            ++$imported;
        }

        $em->flush();

        return ['imported' => $imported, 'skipped' => $skipped];
    }

    private function normalizeHeader(string $header): string
    {
        $header = trim(mb_strtolower($header));
        $header = strtr($header, [
            'é' => 'e', 'è' => 'e', 'ê' => 'e', 'ë' => 'e',
            'à' => 'a', 'â' => 'a', 'ä' => 'a',
            'î' => 'i', 'ï' => 'i',
            'ô' => 'o', 'ö' => 'o',
            'ù' => 'u', 'û' => 'u', 'ü' => 'u',
            'ç' => 'c',
        ]);
        $header = preg_replace('/[^a-z0-9]+/', '_', $header) ?? '';
        return trim($header, '_');
    }

    private function isFalseValue(string $value): bool
    {
        return in_array(mb_strtolower(trim($value)), ['0', 'non', 'false', 'inactif', 'inactive'], true);
    }
}
