<?php

declare(strict_types=1);

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

use App\Administration\Application\Command\ConfigurePeriods\ConfigurePeriodsCommand;
use App\Administration\Application\Command\ConfigurePeriods\ConfigurePeriodsHandler;
use App\Administration\Domain\Exception\PeriodesDejaConfigureesException;
use App\Administration\Domain\Model\AcademicYear\PeriodType;
use App\Administration\Infrastructure\Persistence\InMemory\InMemoryPeriodConfigurationRepository;
use App\Shared\Domain\Clock;
use DateTimeImmutable;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\MessageBusInterface;

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

    private InMemoryPeriodConfigurationRepository $repository;
    private ConfigurePeriodsHandler $handler;

    protected function setUp(): void
    {
        $this->repository = new InMemoryPeriodConfigurationRepository();
        $clock = new class implements Clock {
            public function now(): DateTimeImmutable
            {
                return new DateTimeImmutable('2026-01-31 10:00:00');
            }
        };
        $eventBus = new class implements MessageBusInterface {
            public function dispatch(object $message, array $stamps = []): Envelope
            {
                return new Envelope($message);
            }
        };
        $this->handler = new ConfigurePeriodsHandler($this->repository, $clock, $eventBus);
    }

    #[Test]
    public function itConfiguresTrimesterPeriods(): void
    {
        $command = new ConfigurePeriodsCommand(
            tenantId: self::TENANT_ID,
            academicYearId: self::ACADEMIC_YEAR_ID,
            periodType: 'trimester',
            startYear: 2025,
        );

        $config = ($this->handler)($command);

        self::assertSame(PeriodType::TRIMESTER, $config->type);
        self::assertCount(3, $config->periods);
        self::assertSame('T1', $config->periods[0]->label);
        self::assertSame('T2', $config->periods[1]->label);
        self::assertSame('T3', $config->periods[2]->label);
    }

    #[Test]
    public function itConfiguresSemesterPeriods(): void
    {
        $command = new ConfigurePeriodsCommand(
            tenantId: self::TENANT_ID,
            academicYearId: self::ACADEMIC_YEAR_ID,
            periodType: 'semester',
            startYear: 2025,
        );

        $config = ($this->handler)($command);

        self::assertSame(PeriodType::SEMESTER, $config->type);
        self::assertCount(2, $config->periods);
        self::assertSame('S1', $config->periods[0]->label);
        self::assertSame('S2', $config->periods[1]->label);
    }

    #[Test]
    public function itPersistsConfiguration(): void
    {
        $command = new ConfigurePeriodsCommand(
            tenantId: self::TENANT_ID,
            academicYearId: self::ACADEMIC_YEAR_ID,
            periodType: 'trimester',
            startYear: 2025,
        );

        ($this->handler)($command);

        $saved = $this->repository->findByAcademicYear(
            \App\Shared\Domain\Tenant\TenantId::fromString(self::TENANT_ID),
            \App\Administration\Domain\Model\SchoolClass\AcademicYearId::fromString(self::ACADEMIC_YEAR_ID),
        );
        self::assertNotNull($saved);
        self::assertSame(PeriodType::TRIMESTER, $saved->type);
    }

    #[Test]
    public function itRejectsDoubleConfiguration(): void
    {
        $command = new ConfigurePeriodsCommand(
            tenantId: self::TENANT_ID,
            academicYearId: self::ACADEMIC_YEAR_ID,
            periodType: 'trimester',
            startYear: 2025,
        );

        ($this->handler)($command);

        $this->expectException(PeriodesDejaConfigureesException::class);
        ($this->handler)($command);
    }

    #[Test]
    public function itAllowsDifferentTenantsToConfigureSameYear(): void
    {
        $otherTenantId = '550e8400-e29b-41d4-a716-446655440099';

        ($this->handler)(new ConfigurePeriodsCommand(
            tenantId: self::TENANT_ID,
            academicYearId: self::ACADEMIC_YEAR_ID,
            periodType: 'trimester',
            startYear: 2025,
        ));

        $config = ($this->handler)(new ConfigurePeriodsCommand(
            tenantId: $otherTenantId,
            academicYearId: self::ACADEMIC_YEAR_ID,
            periodType: 'semester',
            startYear: 2025,
        ));

        self::assertSame(PeriodType::SEMESTER, $config->type);
    }
}
