<?php

declare(strict_types=1);

namespace App\Tests\Unit\Administration\Application\Service;

use App\Administration\Application\Service\HomeworkRulesValidator;
use App\Administration\Application\Service\ValidDueDateSuggester;
use App\Administration\Domain\Model\HomeworkRules\EnforcementMode;
use App\Administration\Domain\Model\HomeworkRules\HomeworkRule;
use App\Administration\Domain\Model\HomeworkRules\HomeworkRules;
use App\Administration\Domain\Model\HomeworkRules\RuleType;
use App\Shared\Domain\Tenant\TenantId;

use function count;

use DateTimeImmutable;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

final class ValidDueDateSuggesterTest extends TestCase
{
    private ValidDueDateSuggester $suggester;

    protected function setUp(): void
    {
        $this->suggester = new ValidDueDateSuggester(new HomeworkRulesValidator());
    }

    #[Test]
    public function suggestsNextValidDatesForMinimumDelay(): void
    {
        $rules = $this->creerRulesAvecDelai(3);
        $creationDate = new DateTimeImmutable('2026-03-18 10:00'); // mercredi

        // Requested date is too soon (jeudi 19 mars, only 1 day)
        $suggestions = $this->suggester->suggerer(
            new DateTimeImmutable('2026-03-19'),
            $rules,
            $creationDate,
        );

        self::assertNotEmpty($suggestions);
        self::assertLessThanOrEqual(3, count($suggestions));

        // Each suggestion must be valid against the rules
        $validator = new HomeworkRulesValidator();
        foreach ($suggestions as $date) {
            self::assertInstanceOf(DateTimeImmutable::class, $date);
            $result = $validator->valider($rules, $date, $creationDate);
            self::assertTrue($result->estValide(), 'Suggested date ' . $date->format('Y-m-d') . ' should be valid');
        }
    }

    #[Test]
    public function suggestsUpToMaxSuggestions(): void
    {
        $rules = $this->creerRulesAvecDelai(3);
        $creationDate = new DateTimeImmutable('2026-03-18 10:00');

        $suggestions = $this->suggester->suggerer(
            new DateTimeImmutable('2026-03-19'),
            $rules,
            $creationDate,
            maxSuggestions: 5,
        );

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

    #[Test]
    public function skipsWeekends(): void
    {
        $rules = $this->creerRulesAvecDelai(3);
        // Jeudi 19 mars, creation date is mercredi 18 mars
        $creationDate = new DateTimeImmutable('2026-03-18 10:00');

        $suggestions = $this->suggester->suggerer(
            new DateTimeImmutable('2026-03-19'),
            $rules,
            $creationDate,
        );

        foreach ($suggestions as $date) {
            $dayOfWeek = (int) $date->format('N');
            self::assertLessThanOrEqual(5, $dayOfWeek, 'Suggested date ' . $date->format('Y-m-d') . ' should not be a weekend');
        }
    }

    #[Test]
    public function suggestsDatesInChronologicalOrder(): void
    {
        $rules = $this->creerRulesAvecDelai(3);
        $creationDate = new DateTimeImmutable('2026-03-18 10:00');

        $suggestions = $this->suggester->suggerer(
            new DateTimeImmutable('2026-03-19'),
            $rules,
            $creationDate,
        );

        for ($i = 1; $i < count($suggestions); ++$i) {
            self::assertGreaterThan(
                $suggestions[$i - 1]->format('Y-m-d'),
                $suggestions[$i]->format('Y-m-d'),
                'Suggestions should be in chronological order',
            );
        }
    }

    #[Test]
    public function returnsEmptyWhenRulesDisabled(): void
    {
        $rules = $this->creerRulesAvecDelai(3, EnforcementMode::DISABLED, false);
        $creationDate = new DateTimeImmutable('2026-03-18 10:00');

        $suggestions = $this->suggester->suggerer(
            new DateTimeImmutable('2026-03-19'),
            $rules,
            $creationDate,
        );

        self::assertEmpty($suggestions);
    }

    #[Test]
    public function searchIsBoundedAndDoesNotRunForever(): void
    {
        $rules = $this->creerRulesAvecDelai(30);
        $creationDate = new DateTimeImmutable('2026-03-18 10:00');

        $suggestions = $this->suggester->suggerer(
            new DateTimeImmutable('2026-03-19'),
            $rules,
            $creationDate,
            maxSuggestions: 100,
        );

        // La recherche est bornée à 60 jours. Avec un délai de 30 jours,
        // seules les dates après le 17 avril sont valides (~21 jours ouvrés).
        self::assertNotEmpty($suggestions);
        self::assertLessThan(100, count($suggestions));
    }

    #[Test]
    public function suggestsValidDatesForMondayRule(): void
    {
        $rules = $this->creerRulesAvecLundi();
        // Samedi 21 mars, trying to create homework for lundi 23 mars (after friday cutoff)
        $creationDate = new DateTimeImmutable('2026-03-21 10:00');

        $suggestions = $this->suggester->suggerer(
            new DateTimeImmutable('2026-03-23'), // lundi
            $rules,
            $creationDate,
        );

        self::assertNotEmpty($suggestions);

        $validator = new HomeworkRulesValidator();
        foreach ($suggestions as $date) {
            $result = $validator->valider($rules, $date, $creationDate);
            self::assertTrue($result->estValide(), 'Suggested date ' . $date->format('Y-m-d') . ' should be valid');
        }
    }

    private function creerRulesAvecDelai(
        int $days,
        EnforcementMode $mode = EnforcementMode::HARD,
        bool $enabled = true,
    ): HomeworkRules {
        $rules = HomeworkRules::creer(
            tenantId: TenantId::fromString('550e8400-e29b-41d4-a716-446655440001'),
            now: new DateTimeImmutable('2026-03-01'),
        );

        $rules->mettreAJour(
            rules: [new HomeworkRule(RuleType::MINIMUM_DELAY, ['days' => $days])],
            enforcementMode: $mode,
            enabled: $enabled,
            now: new DateTimeImmutable('2026-03-01'),
        );

        return $rules;
    }

    private function creerRulesAvecLundi(
        EnforcementMode $mode = EnforcementMode::HARD,
    ): HomeworkRules {
        $rules = HomeworkRules::creer(
            tenantId: TenantId::fromString('550e8400-e29b-41d4-a716-446655440001'),
            now: new DateTimeImmutable('2026-03-01'),
        );

        $rules->mettreAJour(
            rules: [new HomeworkRule(RuleType::NO_MONDAY_AFTER, ['day' => 'friday', 'time' => '12:00'])],
            enforcementMode: $mode,
            enabled: true,
            now: new DateTimeImmutable('2026-03-01'),
        );

        return $rules;
    }
}
