<?php

declare(strict_types=1);

namespace App\Tests\Service\Notification;

use App\Entity\Employee;
use App\Entity\EmployeeContractPeriod;
use App\Enum\ContractNature;
use App\Service\Notification\ContractEndNotificationPlanner;
use App\Service\Notification\WorkingDayCalculator;
use App\Service\PublicHolidayServiceInterface;
use DateTimeImmutable;
use PHPUnit\Framework\TestCase;

/**
 * @internal
 */
final class ContractEndNotificationPlannerTest extends TestCase
{
    public function testNotifiesContractEndingTomorrowOnAWeekday(): void
    {
        // Mardi 08/07 -> fin mercredi 09/07
        $notices = $this->planner()->plan(
            [$this->period('Jean', 'Dupont', '2025-07-09')],
            new DateTimeImmutable('2025-07-08'),
        );

        self::assertCount(1, $notices);
        self::assertSame('Fin de CDD de Jean Dupont le 09/07/2025', $notices[0]->message);
    }

    public function testFridayNotifiesContractsEndingOverTheWeekendAndMonday(): void
    {
        // Vendredi 11/07 ; lundi 14/07 férié -> prochain ouvré = mardi 15/07.
        // Fenêtre ]11/07 ; 15/07] -> samedi 12, dimanche 13, lundi 14, mardi 15.
        $notices = $this->planner()->plan(
            [
                $this->period('A', 'Sat', '2025-07-12'),     // samedi -> inclus
                $this->period('B', 'Mon', '2025-07-14'),     // lundi férié -> inclus
                $this->period('C', 'Tue', '2025-07-15'),     // mardi (= borne haute) -> inclus
                $this->period('D', 'Wed', '2025-07-16'),     // mercredi -> hors fenêtre
            ],
            new DateTimeImmutable('2025-07-11'),
        );

        self::assertCount(3, $notices);
    }

    public function testIgnoresOpenEndedContract(): void
    {
        $notices = $this->planner()->plan(
            [$this->period('Jean', 'Dupont', null, ContractNature::CDI)],
            new DateTimeImmutable('2025-07-08'),
        );

        self::assertSame([], $notices);
    }

    public function testIgnoresContractEndingToday(): void
    {
        // fin = today -> trop tard, pas de notif (on notifie la veille)
        $notices = $this->planner()->plan(
            [$this->period('Jean', 'Dupont', '2025-07-08')],
            new DateTimeImmutable('2025-07-08'),
        );

        self::assertSame([], $notices);
    }

    public function testReturnsNothingWhenTodayIsNotAWorkingDay(): void
    {
        // Samedi 12/07 -> aucun jour chômé ne génère de notif
        $notices = $this->planner()->plan(
            [$this->period('Jean', 'Dupont', '2025-07-14')],
            new DateTimeImmutable('2025-07-12'),
        );

        self::assertSame([], $notices);
    }

    public function testInterimNatureLabel(): void
    {
        $notices = $this->planner()->plan(
            [$this->period('Marie', 'Martin', '2025-07-09', ContractNature::INTERIM)],
            new DateTimeImmutable('2025-07-08'),
        );

        self::assertSame('Fin de Intérim de Marie Martin le 09/07/2025', $notices[0]->message);
    }

    private function planner(): ContractEndNotificationPlanner
    {
        $holidays = $this->createStub(PublicHolidayServiceInterface::class);
        $holidays->method('getHolidaysDayByYears')->willReturn([
            '2025-07-14' => 'Fête nationale', // lundi 14/07 férié
        ]);

        return new ContractEndNotificationPlanner(new WorkingDayCalculator($holidays));
    }

    private function period(
        string $firstName,
        string $lastName,
        ?string $endDate,
        ContractNature $nature = ContractNature::CDD,
    ): EmployeeContractPeriod {
        $employee = new Employee();
        $employee->setFirstName($firstName)->setLastName($lastName);

        $period = new EmployeeContractPeriod();
        $period->setEmployee($employee)
            ->setContractNature($nature)
            ->setEndDate(null === $endDate ? null : new DateTimeImmutable($endDate))
        ;

        return $period;
    }
}
