<?php

declare(strict_types=1);

namespace App\Tests\State;

use App\Entity\Employee;
use App\Entity\EmployeeContractPeriod;
use App\State\AbsencePrintProvider;
use DateTimeImmutable;
use PHPUnit\Framework\TestCase;
use ReflectionClass;

/**
 * The provider constructor takes final-class collaborators (Twig, repositories) that
 * PHPUnit cannot double. The pure contract-range helper is exercised via
 * newInstanceWithoutConstructor + reflection.
 *
 * @internal
 */
final class AbsencePrintProviderTest extends TestCase
{
    public function testHasContractInRangeFalseWhenContractEndedBeforeRange(): void
    {
        $provider = new ReflectionClass(AbsencePrintProvider::class)->newInstanceWithoutConstructor();
        $employee = $this->buildEmployeeWithPeriod('2025-01-01', '2026-04-30');

        // Imprime mai : l'employé parti le 30/04 ne doit pas être inclus.
        self::assertFalse($this->hasInRange($provider, $employee, '2026-05-01', '2026-05-31'));
    }

    public function testHasContractInRangeTrueWhenContractOverlapsRange(): void
    {
        $provider = new ReflectionClass(AbsencePrintProvider::class)->newInstanceWithoutConstructor();
        $employee = $this->buildEmployeeWithPeriod('2025-01-01', '2026-05-15');

        // Contrat finissant le 15/05 → chevauche le mois de mai → inclus.
        self::assertTrue($this->hasInRange($provider, $employee, '2026-05-01', '2026-05-31'));
    }

    public function testHasContractInRangeTrueForOpenEndedContract(): void
    {
        $provider = new ReflectionClass(AbsencePrintProvider::class)->newInstanceWithoutConstructor();
        $employee = $this->buildEmployeeWithPeriod('2020-01-01', null);

        self::assertTrue($this->hasInRange($provider, $employee, '2026-05-01', '2026-05-31'));
    }

    public function testHasContractInRangeFalseWhenContractStartsAfterRange(): void
    {
        $provider = new ReflectionClass(AbsencePrintProvider::class)->newInstanceWithoutConstructor();
        $employee = $this->buildEmployeeWithPeriod('2026-06-01', null);

        self::assertFalse($this->hasInRange($provider, $employee, '2026-05-01', '2026-05-31'));
    }

    public function testHasContractInRangeFalseWhenNoPeriods(): void
    {
        $provider = new ReflectionClass(AbsencePrintProvider::class)->newInstanceWithoutConstructor();
        $employee = new Employee();

        self::assertFalse($this->hasInRange($provider, $employee, '2026-05-01', '2026-05-31'));
    }

    public function testHasContractInRangeIncludesEmployeeEndingOnFromDayDespiteTimeComponent(): void
    {
        // Garde-fou : `from` portant une heure (cf. createFromFormat) ne doit pas exclure
        // un employé dont le contrat finit pile le jour de `from` (comparaison date seule).
        $provider = new ReflectionClass(AbsencePrintProvider::class)->newInstanceWithoutConstructor();
        $employee = $this->buildEmployeeWithPeriod('2025-01-01', '2026-05-01');

        $result = new ReflectionClass($provider::class)
            ->getMethod('hasContractInRange')
            ->invoke($provider, $employee, new DateTimeImmutable('2026-05-01 08:33:53'), new DateTimeImmutable('2026-05-31 23:59:59'))
        ;

        self::assertTrue($result);
    }

    private function hasInRange(object $provider, Employee $employee, string $from, string $to): bool
    {
        return new ReflectionClass($provider::class)
            ->getMethod('hasContractInRange')
            ->invoke($provider, $employee, new DateTimeImmutable($from), new DateTimeImmutable($to))
        ;
    }

    private function buildEmployeeWithPeriod(string $start, ?string $end): Employee
    {
        $employee = new Employee();
        $period   = new EmployeeContractPeriod();
        $period->setEmployee($employee);
        $period->setStartDate(new DateTimeImmutable($start));
        $period->setEndDate(null !== $end ? new DateTimeImmutable($end) : null);
        $employee->getContractPeriods()->add($period);

        return $employee;
    }
}
