<?php

declare(strict_types=1);

namespace App\Tests\Functional\Administration\Application;

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\Repository\HomeworkRulesRepository;
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 la persistence des HomeworkRules.
 *
 * Vérifie le round-trip DBAL : save → findByTenantId → hydration correcte.
 */
final class HomeworkRulesPersistenceFunctionalTest extends KernelTestCase
{
    private const string TENANT_ID = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeee01';

    private HomeworkRulesRepository $repository;

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

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

        parent::tearDown();
    }

    #[Test]
    public function saveAndRetrieveEmptyRules(): void
    {
        $tenantId = TenantId::fromString(self::TENANT_ID);
        $rules = HomeworkRules::creer(tenantId: $tenantId, now: new DateTimeImmutable('2026-03-17 10:00:00'));
        $this->repository->save($rules);

        $loaded = $this->repository->findByTenantId($tenantId);

        self::assertNotNull($loaded);
        self::assertTrue($loaded->tenantId->equals($tenantId));
        self::assertSame([], $loaded->rules);
        self::assertSame(EnforcementMode::SOFT, $loaded->enforcementMode);
        self::assertTrue($loaded->enabled);
    }

    #[Test]
    public function saveAndRetrieveWithRules(): void
    {
        $tenantId = TenantId::fromString(self::TENANT_ID);
        $rules = HomeworkRules::creer(tenantId: $tenantId, now: new DateTimeImmutable('2026-03-17 10:00:00'));

        $rules->mettreAJour(
            rules: [
                new HomeworkRule(RuleType::MINIMUM_DELAY, ['days' => 3]),
                new HomeworkRule(RuleType::NO_MONDAY_AFTER, ['day' => 'friday', 'time' => '12:00']),
            ],
            enforcementMode: EnforcementMode::HARD,
            enabled: true,
            now: new DateTimeImmutable('2026-03-17 11:00:00'),
        );

        $this->repository->save($rules);

        $loaded = $this->repository->findByTenantId($tenantId);

        self::assertNotNull($loaded);
        self::assertCount(2, $loaded->rules);
        self::assertSame(RuleType::MINIMUM_DELAY, $loaded->rules[0]->type);
        self::assertSame(3, $loaded->rules[0]->params['days']);
        self::assertSame(RuleType::NO_MONDAY_AFTER, $loaded->rules[1]->type);
        self::assertSame('friday', $loaded->rules[1]->params['day']);
        self::assertSame('12:00', $loaded->rules[1]->params['time']);
        self::assertSame(EnforcementMode::HARD, $loaded->enforcementMode);
    }

    #[Test]
    public function upsertOverwritesExistingConfig(): void
    {
        $tenantId = TenantId::fromString(self::TENANT_ID);
        $rules = HomeworkRules::creer(tenantId: $tenantId, now: new DateTimeImmutable('2026-03-17 10:00:00'));
        $this->repository->save($rules);

        $rules->mettreAJour(
            rules: [new HomeworkRule(RuleType::MINIMUM_DELAY, ['days' => 5])],
            enforcementMode: EnforcementMode::HARD,
            enabled: true,
            now: new DateTimeImmutable('2026-03-17 12:00:00'),
        );
        $this->repository->save($rules);

        $loaded = $this->repository->findByTenantId($tenantId);

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

    #[Test]
    public function findByTenantIdReturnsNullWhenNotFound(): void
    {
        $tenantId = TenantId::fromString('99999999-9999-9999-9999-999999999999');

        self::assertNull($this->repository->findByTenantId($tenantId));
    }

    #[Test]
    public function getThrowsWhenNotFound(): void
    {
        $this->expectException(\App\Administration\Domain\Exception\HomeworkRulesNotFoundException::class);

        $this->repository->get(TenantId::fromString('99999999-9999-9999-9999-999999999999'));
    }

    #[Test]
    public function disabledStatePersistedCorrectly(): void
    {
        $tenantId = TenantId::fromString(self::TENANT_ID);
        $rules = HomeworkRules::creer(tenantId: $tenantId, now: new DateTimeImmutable('2026-03-17 10:00:00'));
        $rules->desactiver(new DateTimeImmutable('2026-03-17 11:00:00'));
        $this->repository->save($rules);

        $loaded = $this->repository->findByTenantId($tenantId);

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