<?php

declare(strict_types=1);

namespace App\Tests\Unit\Scolarite\Infrastructure\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\Administration\Domain\Model\SchoolCalendar\SchoolCalendar;
use App\Administration\Domain\Model\SchoolClass\AcademicYearId;
use App\Administration\Domain\Repository\HomeworkRulesRepository;
use App\Scolarite\Application\Port\CurrentCalendarProvider;
use App\Scolarite\Infrastructure\Service\AdministrationHomeworkRulesChecker;
use App\Shared\Domain\Tenant\TenantId;
use DateTimeImmutable;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
use RuntimeException;

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

    private HomeworkRulesValidator $validator;
    private ValidDueDateSuggester $suggester;

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

    #[Test]
    public function returnsOkWhenNoRulesConfigured(): void
    {
        $checker = $this->createChecker(rules: null);
        $tenantId = TenantId::fromString(self::TENANT_ID);

        $result = $checker->verifier(
            $tenantId,
            new DateTimeImmutable('2026-03-25'),
            new DateTimeImmutable('2026-03-18 10:00'),
        );

        self::assertTrue($result->estValide());
        self::assertFalse($result->bloquant);
        self::assertEmpty($result->suggestedDates);
    }

    #[Test]
    public function returnsOkWhenRulesRespected(): void
    {
        $rules = $this->creerRulesAvecDelai(3, EnforcementMode::HARD);
        $checker = $this->createChecker(rules: $rules);
        $tenantId = TenantId::fromString(self::TENANT_ID);

        $result = $checker->verifier(
            $tenantId,
            new DateTimeImmutable('2026-03-25'), // 7 days ahead, > 3 day min
            new DateTimeImmutable('2026-03-18 10:00'),
        );

        self::assertTrue($result->estValide());
        self::assertEmpty($result->suggestedDates);
    }

    #[Test]
    public function returnsBlockingResultWithSuggestedDatesInHardMode(): void
    {
        $rules = $this->creerRulesAvecDelai(7, EnforcementMode::HARD);
        $checker = $this->createChecker(rules: $rules);
        $tenantId = TenantId::fromString(self::TENANT_ID);

        $result = $checker->verifier(
            $tenantId,
            new DateTimeImmutable('2026-03-19'), // only 1 day ahead, < 7 day min
            new DateTimeImmutable('2026-03-18 10:00'),
        );

        self::assertTrue($result->estBloquant());
        self::assertTrue($result->bloquant);
        self::assertNotEmpty($result->suggestedDates);
        self::assertCount(3, $result->suggestedDates);

        // Suggested dates are formatted as Y-m-d strings
        foreach ($result->suggestedDates as $date) {
            self::assertMatchesRegularExpression('/^\d{4}-\d{2}-\d{2}$/', $date);
        }
    }

    #[Test]
    public function returnsWarningWithoutSuggestedDatesInSoftMode(): void
    {
        $rules = $this->creerRulesAvecDelai(7, EnforcementMode::SOFT);
        $checker = $this->createChecker(rules: $rules);
        $tenantId = TenantId::fromString(self::TENANT_ID);

        $result = $checker->verifier(
            $tenantId,
            new DateTimeImmutable('2026-03-19'), // only 1 day ahead, < 7 day min
            new DateTimeImmutable('2026-03-18 10:00'),
        );

        self::assertTrue($result->estAvertissement());
        self::assertFalse($result->bloquant);
        self::assertEmpty($result->suggestedDates);
    }

    #[Test]
    public function warningsContainRuleParams(): void
    {
        $rules = $this->creerRulesAvecDelai(5, EnforcementMode::SOFT);
        $checker = $this->createChecker(rules: $rules);
        $tenantId = TenantId::fromString(self::TENANT_ID);

        $result = $checker->verifier(
            $tenantId,
            new DateTimeImmutable('2026-03-19'),
            new DateTimeImmutable('2026-03-18 10:00'),
        );

        self::assertCount(1, $result->warnings);
        self::assertSame('minimum_delay', $result->warnings[0]->ruleType);
        self::assertSame(['days' => 5], $result->warnings[0]->params);
    }

    private function createChecker(?HomeworkRules $rules): AdministrationHomeworkRulesChecker
    {
        $repository = new class($rules) implements HomeworkRulesRepository {
            public function __construct(private readonly ?HomeworkRules $rules)
            {
            }

            public function save(HomeworkRules $homeworkRules): void
            {
            }

            public function get(TenantId $tenantId): HomeworkRules
            {
                return $this->rules ?? throw new RuntimeException('Not found');
            }

            public function findByTenantId(TenantId $tenantId): ?HomeworkRules
            {
                return $this->rules;
            }
        };

        $calendarProvider = new class implements CurrentCalendarProvider {
            public function forCurrentYear(TenantId $tenantId): SchoolCalendar
            {
                return SchoolCalendar::reconstitute(
                    tenantId: $tenantId,
                    academicYearId: AcademicYearId::fromString('550e8400-e29b-41d4-a716-446655440002'),
                    zone: null,
                    entries: [],
                );
            }
        };

        return new AdministrationHomeworkRulesChecker(
            $repository,
            $this->validator,
            $this->suggester,
            $calendarProvider,
        );
    }

    private function creerRulesAvecDelai(int $days, EnforcementMode $mode): HomeworkRules
    {
        $rules = HomeworkRules::creer(
            tenantId: TenantId::fromString(self::TENANT_ID),
            now: new DateTimeImmutable('2026-03-01'),
        );

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

        return $rules;
    }
}
