<?php

declare(strict_types=1);

namespace App\Tests\Unit\Scolarite\Application\Query\GetBlockedDates;

use App\Administration\Domain\Model\SchoolCalendar\CalendarEntry;
use App\Administration\Domain\Model\SchoolCalendar\CalendarEntryId;
use App\Administration\Domain\Model\SchoolCalendar\CalendarEntryType;
use App\Administration\Domain\Model\SchoolCalendar\SchoolCalendar;
use App\Administration\Domain\Model\SchoolClass\AcademicYearId;
use App\Administration\Infrastructure\Persistence\InMemory\InMemorySchoolCalendarRepository;
use App\Scolarite\Application\Query\GetBlockedDates\GetBlockedDatesHandler;
use App\Scolarite\Application\Query\GetBlockedDates\GetBlockedDatesQuery;
use App\Shared\Domain\Tenant\TenantId;
use DateTimeImmutable;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

final class GetBlockedDatesHandlerTest extends TestCase
{
    private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440001';
    private const string ACADEMIC_YEAR_ID = '550e8400-e29b-41d4-a716-446655440050';

    private InMemorySchoolCalendarRepository $calendarRepository;
    private GetBlockedDatesHandler $handler;

    protected function setUp(): void
    {
        $this->calendarRepository = new InMemorySchoolCalendarRepository();
        $this->handler = new GetBlockedDatesHandler($this->calendarRepository);
    }

    #[Test]
    public function returnsWeekendsAsBlocked(): void
    {
        // 2026-03-02 est un lundi, 2026-03-08 est un dimanche
        $result = ($this->handler)(new GetBlockedDatesQuery(
            tenantId: self::TENANT_ID,
            academicYearId: self::ACADEMIC_YEAR_ID,
            startDate: '2026-03-02',
            endDate: '2026-03-08',
        ));

        $weekendDates = array_filter($result, static fn ($d) => $d->type === 'weekend');

        self::assertCount(2, $weekendDates);
        $dates = array_map(static fn ($d) => $d->date, array_values($weekendDates));
        self::assertContains('2026-03-07', $dates);
        self::assertContains('2026-03-08', $dates);
    }

    #[Test]
    public function returnsHolidaysAsBlocked(): void
    {
        $calendar = $this->createCalendarWithHoliday(
            new DateTimeImmutable('2026-03-04'),
            'Jour de test',
        );
        $this->calendarRepository->save($calendar);

        $result = ($this->handler)(new GetBlockedDatesQuery(
            tenantId: self::TENANT_ID,
            academicYearId: self::ACADEMIC_YEAR_ID,
            startDate: '2026-03-02',
            endDate: '2026-03-06',
        ));

        $holidays = array_filter($result, static fn ($d) => $d->type === CalendarEntryType::HOLIDAY->value);

        self::assertCount(1, $holidays);
        $holiday = array_values($holidays)[0];
        self::assertSame('2026-03-04', $holiday->date);
        self::assertSame('Jour de test', $holiday->reason);
    }

    #[Test]
    public function returnsEmptyForWeekWithNoBlockedDates(): void
    {
        // Lundi à vendredi sans calendrier configuré
        $result = ($this->handler)(new GetBlockedDatesQuery(
            tenantId: self::TENANT_ID,
            academicYearId: self::ACADEMIC_YEAR_ID,
            startDate: '2026-03-02',
            endDate: '2026-03-06',
        ));

        self::assertEmpty($result);
    }

    #[Test]
    public function returnsVacationsAsBlocked(): void
    {
        $calendar = $this->createCalendarWithVacation(
            new DateTimeImmutable('2026-03-02'),
            new DateTimeImmutable('2026-03-06'),
            'Vacances de printemps',
        );
        $this->calendarRepository->save($calendar);

        $result = ($this->handler)(new GetBlockedDatesQuery(
            tenantId: self::TENANT_ID,
            academicYearId: self::ACADEMIC_YEAR_ID,
            startDate: '2026-03-02',
            endDate: '2026-03-06',
        ));

        $vacations = array_filter($result, static fn ($d) => $d->type === CalendarEntryType::VACATION->value);

        self::assertCount(5, $vacations);
    }

    private function createCalendarWithHoliday(DateTimeImmutable $date, string $label): SchoolCalendar
    {
        $tenantId = TenantId::fromString(self::TENANT_ID);
        $academicYearId = AcademicYearId::fromString(self::ACADEMIC_YEAR_ID);

        $calendar = SchoolCalendar::initialiser($tenantId, $academicYearId);
        $calendar->ajouterEntree(new CalendarEntry(
            id: CalendarEntryId::generate(),
            type: CalendarEntryType::HOLIDAY,
            startDate: $date,
            endDate: $date,
            label: $label,
        ));

        return $calendar;
    }

    private function createCalendarWithVacation(
        DateTimeImmutable $startDate,
        DateTimeImmutable $endDate,
        string $label,
    ): SchoolCalendar {
        $tenantId = TenantId::fromString(self::TENANT_ID);
        $academicYearId = AcademicYearId::fromString(self::ACADEMIC_YEAR_ID);

        $calendar = SchoolCalendar::initialiser($tenantId, $academicYearId);
        $calendar->ajouterEntree(new CalendarEntry(
            id: CalendarEntryId::generate(),
            type: CalendarEntryType::VACATION,
            startDate: $startDate,
            endDate: $endDate,
            label: $label,
        ));

        return $calendar;
    }
}
