<?php

declare(strict_types=1);

namespace App\Administration\Infrastructure\Service;

use DateTimeImmutable;

use function sprintf;

/**
 * Calcule les jours fériés français pour une année scolaire donnée.
 *
 * Couvre la période septembre → août (ex: "2024-2025" → sept 2024 à août 2025).
 * Les dates mobiles (Pâques, Ascension, Pentecôte) sont calculées via l'algorithme de Butcher.
 */
final readonly class FrenchPublicHolidaysCalculator
{
    /**
     * @return list<array{date: string, label: string}>
     */
    public function pourAnneeScolaire(string $academicYear): array
    {
        [$startYear, $endYear] = $this->parseAcademicYear($academicYear);

        $holidays = [];

        // Jours fériés de la première année (sept → déc)
        $holidays[] = ['date' => "$startYear-11-01", 'label' => 'Toussaint'];
        $holidays[] = ['date' => "$startYear-11-11", 'label' => 'Armistice'];
        $holidays[] = ['date' => "$startYear-12-25", 'label' => 'Noël'];

        // Jours fériés de la deuxième année (jan → août)
        $holidays[] = ['date' => "$endYear-01-01", 'label' => "Jour de l'an"];

        // Pâques et dates mobiles (basées sur l'année civile de Pâques = endYear)
        $easter = $this->easterDate($endYear);
        $holidays[] = [
            'date' => $easter->modify('+1 day')->format('Y-m-d'),
            'label' => 'Lundi de Pâques',
        ];

        $holidays[] = ['date' => "$endYear-05-01", 'label' => 'Fête du travail'];
        $holidays[] = ['date' => "$endYear-05-08", 'label' => 'Victoire 1945'];

        $holidays[] = [
            'date' => $easter->modify('+39 days')->format('Y-m-d'),
            'label' => 'Ascension',
        ];
        $holidays[] = [
            'date' => $easter->modify('+50 days')->format('Y-m-d'),
            'label' => 'Lundi de Pentecôte',
        ];

        $holidays[] = ['date' => "$endYear-07-14", 'label' => 'Fête nationale'];
        $holidays[] = ['date' => "$endYear-08-15", 'label' => 'Assomption'];

        // Trier par date
        usort($holidays, static fn (array $a, array $b): int => $a['date'] <=> $b['date']);

        return $holidays;
    }

    /**
     * @return array{int, int}
     */
    private function parseAcademicYear(string $academicYear): array
    {
        $parts = explode('-', $academicYear);

        return [(int) $parts[0], (int) $parts[1]];
    }

    /**
     * Calcule la date de Pâques via l'algorithme de Butcher (Anonymous Gregorian).
     */
    private function easterDate(int $year): DateTimeImmutable
    {
        $a = $year % 19;
        $b = intdiv($year, 100);
        $c = $year % 100;
        $d = intdiv($b, 4);
        $e = $b % 4;
        $f = intdiv($b + 8, 25);
        $g = intdiv($b - $f + 1, 3);
        $h = (19 * $a + $b - $d - $g + 15) % 30;
        $i = intdiv($c, 4);
        $k = $c % 4;
        $l = (32 + 2 * $e + 2 * $i - $h - $k) % 7;
        $m = intdiv($a + 11 * $h + 22 * $l, 451);
        $month = intdiv($h + $l - 7 * $m + 114, 31);
        $day = (($h + $l - 7 * $m + 114) % 31) + 1;

        return new DateTimeImmutable(sprintf('%04d-%02d-%02d', $year, $month, $day));
    }
}
