<?php

declare(strict_types=1);

namespace App\Tests\Unit\Administration\Application\Command\UpdateHomeworkRules;

use App\Administration\Application\Command\UpdateHomeworkRules\UpdateHomeworkRulesCommand;
use App\Administration\Application\Command\UpdateHomeworkRules\UpdateHomeworkRulesHandler;
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\Infrastructure\Persistence\InMemory\InMemoryHomeworkRulesHistoryRepository;
use App\Administration\Infrastructure\Persistence\InMemory\InMemoryHomeworkRulesRepository;
use App\Shared\Domain\Clock;
use App\Shared\Domain\Tenant\TenantId;
use DateTimeImmutable;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

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

    private InMemoryHomeworkRulesRepository $rulesRepository;
    private InMemoryHomeworkRulesHistoryRepository $historyRepository;
    private Clock $clock;

    protected function setUp(): void
    {
        $this->rulesRepository = new InMemoryHomeworkRulesRepository();
        $this->historyRepository = new InMemoryHomeworkRulesHistoryRepository();
        $this->clock = new class implements Clock {
            public function now(): DateTimeImmutable
            {
                return new DateTimeImmutable('2026-03-17 10:00:00');
            }
        };
    }

    #[Test]
    public function itCreatesRulesWhenNoneExist(): void
    {
        $handler = $this->handler();

        $result = $handler($this->commandAvecDelai(3));

        self::assertCount(1, $result->rules);
        self::assertSame(RuleType::MINIMUM_DELAY, $result->rules[0]->type);
        self::assertSame(EnforcementMode::SOFT, $result->enforcementMode);
        self::assertTrue($result->enabled);
    }

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

        $handler = $this->handler();
        $result = $handler($this->commandAvecDelai(5, 'hard'));

        self::assertCount(1, $result->rules);
        self::assertSame(5, $result->rules[0]->params['days']);
        self::assertSame(EnforcementMode::HARD, $result->enforcementMode);
    }

    #[Test]
    public function itRecordsHistory(): void
    {
        $handler = $this->handler();
        $handler($this->commandAvecDelai(3));

        $history = $this->historyRepository->findByTenant(TenantId::fromString(self::TENANT_ID));

        self::assertCount(1, $history);
        self::assertSame('soft', $history[0]['enforcement_mode']);
    }

    #[Test]
    public function itSavesToRepository(): void
    {
        $handler = $this->handler();
        $handler($this->commandAvecDelai(3));

        $saved = $this->rulesRepository->findByTenantId(TenantId::fromString(self::TENANT_ID));
        self::assertNotNull($saved);
        self::assertCount(1, $saved->rules);
    }

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

        $handler = $this->handler();
        $result = $handler(new UpdateHomeworkRulesCommand(
            tenantId: self::TENANT_ID,
            rules: [],
            enforcementMode: 'disabled',
            enabled: false,
            changedBy: self::USER_ID,
        ));

        self::assertFalse($result->enabled);
        self::assertSame(EnforcementMode::DISABLED, $result->enforcementMode);
    }

    #[Test]
    public function itHandlesMultipleRules(): void
    {
        $handler = $this->handler();
        $result = $handler(new UpdateHomeworkRulesCommand(
            tenantId: self::TENANT_ID,
            rules: [
                ['type' => 'minimum_delay', 'params' => ['days' => 3]],
                ['type' => 'no_monday_after', 'params' => ['day' => 'friday', 'time' => '12:00']],
            ],
            enforcementMode: 'soft',
            enabled: true,
            changedBy: self::USER_ID,
        ));

        self::assertCount(2, $result->rules);
    }

    private function handler(): UpdateHomeworkRulesHandler
    {
        return new UpdateHomeworkRulesHandler(
            $this->rulesRepository,
            $this->historyRepository,
            $this->clock,
        );
    }

    private function commandAvecDelai(int $days, string $mode = 'soft'): UpdateHomeworkRulesCommand
    {
        return new UpdateHomeworkRulesCommand(
            tenantId: self::TENANT_ID,
            rules: [['type' => 'minimum_delay', 'params' => ['days' => $days]]],
            enforcementMode: $mode,
            enabled: true,
            changedBy: self::USER_ID,
        );
    }

    private function seedRules(): void
    {
        $rules = HomeworkRules::creer(
            tenantId: TenantId::fromString(self::TENANT_ID),
            now: new DateTimeImmutable('2026-03-01'),
        );

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

        $this->rulesRepository->save($rules);
    }
}
