<?php

declare(strict_types=1);

namespace App\Tests\Functional\Administration\Application;

use App\Administration\Application\Query\ValidateHomeworkDueDate\ValidateHomeworkDueDateHandler;
use App\Administration\Application\Query\ValidateHomeworkDueDate\ValidateHomeworkDueDateQuery;
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\SchoolCalendar\SchoolCalendarRepository;
use App\Administration\Domain\Model\SchoolClass\AcademicYearId;
use App\Shared\Domain\Tenant\TenantId;
use DateTimeImmutable;
use Doctrine\DBAL\Connection;
use PHPUnit\Framework\Attributes\Test;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;

/**
 * Tests fonctionnels pour ValidateHomeworkDueDate.
 *
 * Vérifie le comportement bout-en-bout avec calendrier persisté en BDD.
 * Complémente les tests unitaires qui utilisent un repository in-memory.
 *
 * @see Story 2.11 - AC3 (jours fériés), AC4 (périodes vacances)
 */
final class ValidateHomeworkDueDateFunctionalTest extends KernelTestCase
{
    private const string TENANT_ID = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890';
    private const string ACADEMIC_YEAR_ID = '11111111-1111-1111-1111-111111111111';

    private ValidateHomeworkDueDateHandler $handler;

    protected function setUp(): void
    {
        self::bootKernel();
        $this->handler = static::getContainer()->get(ValidateHomeworkDueDateHandler::class);
    }

    protected function tearDown(): void
    {
        /** @var Connection $connection */
        $connection = static::getContainer()->get(Connection::class);
        $connection->executeStatement(
            'DELETE FROM school_calendar_entries WHERE tenant_id = :tenant_id AND academic_year_id = :academic_year_id',
            ['tenant_id' => self::TENANT_ID, 'academic_year_id' => self::ACADEMIC_YEAR_ID],
        );

        parent::tearDown();
    }

    // =========================================================================
    // AC3 (P0) — Blocage jours fériés
    // =========================================================================

    #[Test]
    public function itRejectsHolidayAsHomeworkDueDate(): void
    {
        $this->persistCalendar([
            new CalendarEntry(
                id: CalendarEntryId::generate(),
                type: CalendarEntryType::HOLIDAY,
                startDate: new DateTimeImmutable('2024-12-25'),
                endDate: new DateTimeImmutable('2024-12-25'),
                label: 'Noël',
            ),
        ]);

        // 2024-12-25 is a Wednesday (weekday) but a holiday
        $result = ($this->handler)(new ValidateHomeworkDueDateQuery(
            tenantId: self::TENANT_ID,
            academicYearId: self::ACADEMIC_YEAR_ID,
            dueDate: '2024-12-25',
        ));

        self::assertFalse($result->valid);
        self::assertNotNull($result->reason);
        self::assertStringContainsString('férié', $result->reason);
    }

    // =========================================================================
    // AC3/AC4 (P0/P1) — Blocage vacances
    // =========================================================================

    #[Test]
    public function itRejectsVacationDayAsHomeworkDueDate(): void
    {
        $this->persistCalendar([
            new CalendarEntry(
                id: CalendarEntryId::generate(),
                type: CalendarEntryType::VACATION,
                startDate: new DateTimeImmutable('2025-02-15'),
                endDate: new DateTimeImmutable('2025-03-02'),
                label: 'Vacances d\'hiver',
            ),
        ]);

        // 2025-02-20 is a Thursday during the vacation
        $result = ($this->handler)(new ValidateHomeworkDueDateQuery(
            tenantId: self::TENANT_ID,
            academicYearId: self::ACADEMIC_YEAR_ID,
            dueDate: '2025-02-20',
        ));

        self::assertFalse($result->valid);
        self::assertNotNull($result->reason);
        self::assertStringContainsString('vacances', $result->reason);
    }

    // =========================================================================
    // AC4 (P1) — Warning retour vacances
    // =========================================================================

    #[Test]
    public function itAcceptsReturnDayWithWarning(): void
    {
        $this->persistCalendar([
            new CalendarEntry(
                id: CalendarEntryId::generate(),
                type: CalendarEntryType::VACATION,
                startDate: new DateTimeImmutable('2025-02-15'),
                endDate: new DateTimeImmutable('2025-03-02'),
                label: 'Vacances d\'hiver',
            ),
        ]);

        // 2025-03-03 is a Monday, the day after vacation ends (2025-03-02)
        $result = ($this->handler)(new ValidateHomeworkDueDateQuery(
            tenantId: self::TENANT_ID,
            academicYearId: self::ACADEMIC_YEAR_ID,
            dueDate: '2025-03-03',
        ));

        self::assertTrue($result->valid);
        self::assertNotEmpty($result->warnings);
        self::assertStringContainsString('retour de vacances', $result->warnings[0]);
    }

    // =========================================================================
    // Cas nominal — jour ouvré normal
    // =========================================================================

    #[Test]
    public function itAcceptsNormalWeekday(): void
    {
        $this->persistCalendar();

        // 2025-03-10 is a Monday with no calendar entry
        $result = ($this->handler)(new ValidateHomeworkDueDateQuery(
            tenantId: self::TENANT_ID,
            academicYearId: self::ACADEMIC_YEAR_ID,
            dueDate: '2025-03-10',
        ));

        self::assertTrue($result->valid);
        self::assertNull($result->reason);
        self::assertEmpty($result->warnings);
    }

    // =========================================================================
    // Helpers
    // =========================================================================

    private function persistCalendar(array $entries = []): void
    {
        $tenantId = TenantId::fromString(self::TENANT_ID);
        $academicYearId = AcademicYearId::fromString(self::ACADEMIC_YEAR_ID);

        $calendar = SchoolCalendar::initialiser($tenantId, $academicYearId);

        foreach ($entries as $entry) {
            $calendar->ajouterEntree($entry);
        }

        /** @var SchoolCalendarRepository $repository */
        $repository = static::getContainer()->get(SchoolCalendarRepository::class);
        $repository->save($calendar);
    }
}
