<?php

declare(strict_types=1);

namespace App\Tests\Unit\Administration\Application\Query\GetPeriods;

use App\Administration\Application\Query\GetPeriods\GetPeriodsHandler;
use App\Administration\Application\Query\GetPeriods\GetPeriodsQuery;
use App\Administration\Domain\Model\AcademicYear\DefaultPeriods;
use App\Administration\Domain\Model\AcademicYear\PeriodType;
use App\Administration\Domain\Model\SchoolClass\AcademicYearId;
use App\Administration\Infrastructure\Persistence\InMemory\InMemoryPeriodConfigurationRepository;
use App\Shared\Domain\Clock;
use App\Shared\Domain\Tenant\TenantId;
use DateTimeImmutable;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

final class GetPeriodsHandlerTest 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;

    protected function setUp(): void
    {
        $this->repository = new InMemoryPeriodConfigurationRepository();
    }

    #[Test]
    public function itReturnsNullWhenNoPeriodsConfigured(): void
    {
        $handler = $this->createHandler('2025-10-15');

        $result = $handler(new GetPeriodsQuery(self::TENANT_ID, self::ACADEMIC_YEAR_ID));

        self::assertNull($result);
    }

    #[Test]
    public function itReturnsPeriodsWithCurrentPeriodInfo(): void
    {
        $this->seedTrimesterConfig();
        $handler = $this->createHandler('2025-10-15');

        $result = $handler(new GetPeriodsQuery(self::TENANT_ID, self::ACADEMIC_YEAR_ID));

        self::assertNotNull($result);
        self::assertSame('trimester', $result->type);
        self::assertCount(3, $result->periods);

        // T1 is current
        self::assertNotNull($result->currentPeriod);
        self::assertSame('T1', $result->currentPeriod->label);
        self::assertTrue($result->currentPeriod->isCurrent);
        self::assertSame(46, $result->currentPeriod->daysRemaining);
    }

    #[Test]
    public function itReturnsNullCurrentPeriodWhenOutOfRange(): void
    {
        $this->seedTrimesterConfig();
        $handler = $this->createHandler('2025-08-15');

        $result = $handler(new GetPeriodsQuery(self::TENANT_ID, self::ACADEMIC_YEAR_ID));

        self::assertNotNull($result);
        self::assertNull($result->currentPeriod);
    }

    #[Test]
    public function itMarksPastPeriods(): void
    {
        $this->seedTrimesterConfig();
        $handler = $this->createHandler('2026-04-15');

        $result = $handler(new GetPeriodsQuery(self::TENANT_ID, self::ACADEMIC_YEAR_ID));

        self::assertNotNull($result);
        self::assertTrue($result->periods[0]->isPast);
        self::assertTrue($result->periods[1]->isPast);
        self::assertFalse($result->periods[2]->isPast);
    }

    private function seedTrimesterConfig(): void
    {
        $config = DefaultPeriods::forType(PeriodType::TRIMESTER, 2025);
        $this->repository->save(
            TenantId::fromString(self::TENANT_ID),
            AcademicYearId::fromString(self::ACADEMIC_YEAR_ID),
            $config,
        );
    }

    private function createHandler(string $dateString): GetPeriodsHandler
    {
        $clock = new class($dateString) implements Clock {
            public function __construct(private readonly string $dateString)
            {
            }

            public function now(): DateTimeImmutable
            {
                return new DateTimeImmutable($this->dateString);
            }
        };

        return new GetPeriodsHandler($this->repository, $clock);
    }
}
