<?php

declare(strict_types=1);

namespace App\Tests\Service\Leave;

use App\Entity\ContractSuspension;
use App\Service\Leave\SuspensionDaysCalculator;
use DateTimeImmutable;
use PHPUnit\Framework\TestCase;

/**
 * @internal
 */
final class SuspensionDaysCalculatorTest extends TestCase
{
    public function testNoSuspensionsReturnsZero(): void
    {
        $calc   = new SuspensionDaysCalculator();
        $result = $calc->countSuspendedDaysInMonth(
            new DateTimeImmutable('2026-03-01'),
            new DateTimeImmutable('2026-03-31'),
            []
        );

        self::assertSame(0, $result);
    }

    public function testFullMonthSuspension(): void
    {
        $calc       = new SuspensionDaysCalculator();
        $suspension = $this->buildSuspension('2026-03-01', '2026-03-31');

        $result = $calc->countSuspendedDaysInMonth(
            new DateTimeImmutable('2026-03-01'),
            new DateTimeImmutable('2026-03-31'),
            [$suspension]
        );

        self::assertSame(31, $result);
    }

    public function testPartialMonthSuspension(): void
    {
        $calc       = new SuspensionDaysCalculator();
        $suspension = $this->buildSuspension('2026-03-10', '2026-03-20');

        $result = $calc->countSuspendedDaysInMonth(
            new DateTimeImmutable('2026-03-01'),
            new DateTimeImmutable('2026-03-31'),
            [$suspension]
        );

        self::assertSame(11, $result);
    }

    public function testSuspensionSpanningMultipleMonths(): void
    {
        $calc       = new SuspensionDaysCalculator();
        $suspension = $this->buildSuspension('2026-02-15', '2026-04-10');

        // March fully covered
        $result = $calc->countSuspendedDaysInMonth(
            new DateTimeImmutable('2026-03-01'),
            new DateTimeImmutable('2026-03-31'),
            [$suspension]
        );

        self::assertSame(31, $result);
    }

    public function testSuspensionWithoutEndDate(): void
    {
        $calc       = new SuspensionDaysCalculator();
        $suspension = $this->buildSuspension('2026-03-15', null);

        $result = $calc->countSuspendedDaysInMonth(
            new DateTimeImmutable('2026-03-01'),
            new DateTimeImmutable('2026-03-31'),
            [$suspension]
        );

        self::assertSame(17, $result);
    }

    public function testMultipleSuspensionsInSameMonth(): void
    {
        $calc = new SuspensionDaysCalculator();
        $s1   = $this->buildSuspension('2026-03-01', '2026-03-10');
        $s2   = $this->buildSuspension('2026-03-20', '2026-03-25');

        $result = $calc->countSuspendedDaysInMonth(
            new DateTimeImmutable('2026-03-01'),
            new DateTimeImmutable('2026-03-31'),
            [$s1, $s2]
        );

        self::assertSame(16, $result);
    }

    public function testSuspensionOutsideMonthReturnsZero(): void
    {
        $calc       = new SuspensionDaysCalculator();
        $suspension = $this->buildSuspension('2026-01-01', '2026-01-31');

        $result = $calc->countSuspendedDaysInMonth(
            new DateTimeImmutable('2026-03-01'),
            new DateTimeImmutable('2026-03-31'),
            [$suspension]
        );

        self::assertSame(0, $result);
    }

    public function testCountSuspendedBusinessDays(): void
    {
        $calc = new SuspensionDaysCalculator();
        // March 2-6, 2026 = Mon-Fri = 5 business days
        $suspension = $this->buildSuspension('2026-03-02', '2026-03-06');

        $result = $calc->countSuspendedBusinessDays(
            new DateTimeImmutable('2026-01-01'),
            new DateTimeImmutable('2026-12-31'),
            [$suspension],
            []
        );

        self::assertSame(5, $result);
    }

    public function testFirstMonthGraceShiftsStartByOneMonth(): void
    {
        $calc       = new SuspensionDaysCalculator();
        $suspension = $this->buildSuspension('2026-03-15', '2026-06-30');

        $result = $calc->applyFirstMonthGrace([$suspension]);

        self::assertCount(1, $result);
        self::assertEquals(new DateTimeImmutable('2026-04-15'), $result[0]->getStartDate());
        self::assertEquals(new DateTimeImmutable('2026-06-30'), $result[0]->getEndDate());
    }

    public function testFirstMonthGraceRemovesSuspensionShorterThanOneMonth(): void
    {
        $calc       = new SuspensionDaysCalculator();
        $suspension = $this->buildSuspension('2026-03-10', '2026-03-25');

        $result = $calc->applyFirstMonthGrace([$suspension]);

        self::assertCount(0, $result);
    }

    public function testFirstMonthGraceOpenEndedSuspension(): void
    {
        $calc       = new SuspensionDaysCalculator();
        $suspension = $this->buildSuspension('2026-03-01', null);

        $result = $calc->applyFirstMonthGrace([$suspension]);

        self::assertCount(1, $result);
        self::assertEquals(new DateTimeImmutable('2026-04-01'), $result[0]->getStartDate());
        self::assertNull($result[0]->getEndDate());
    }

    private function buildSuspension(string $start, ?string $end): ContractSuspension
    {
        $s = new ContractSuspension();
        $s->setStartDate(new DateTimeImmutable($start));
        if (null !== $end) {
            $s->setEndDate(new DateTimeImmutable($end));
        }

        return $s;
    }
}
