<?php

declare(strict_types=1);

namespace App\Tests\Unit\Administration\Domain\Model\AcademicYear;

use App\Administration\Domain\Exception\InvalidPeriodCountException;
use App\Administration\Domain\Exception\PeriodsCoverageGapException;
use App\Administration\Domain\Exception\PeriodsOverlapException;
use App\Administration\Domain\Model\AcademicYear\AcademicPeriod;
use App\Administration\Domain\Model\AcademicYear\PeriodConfiguration;
use App\Administration\Domain\Model\AcademicYear\PeriodType;
use DateTimeImmutable;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

final class PeriodConfigurationTest extends TestCase
{
    #[Test]
    public function itCreatesValidTrimesterConfiguration(): void
    {
        $config = $this->validTrimesterConfig();

        self::assertSame(PeriodType::TRIMESTER, $config->type);
        self::assertCount(3, $config->periods);
        self::assertSame('2025-09-01', $config->startDate()->format('Y-m-d'));
        self::assertSame('2026-06-30', $config->endDate()->format('Y-m-d'));
    }

    #[Test]
    public function itCreatesValidSemesterConfiguration(): void
    {
        $config = new PeriodConfiguration(PeriodType::SEMESTER, [
            new AcademicPeriod(1, 'S1', new DateTimeImmutable('2025-09-01'), new DateTimeImmutable('2026-01-31')),
            new AcademicPeriod(2, 'S2', new DateTimeImmutable('2026-02-01'), new DateTimeImmutable('2026-06-30')),
        ]);

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

    #[Test]
    public function itRejectsWrongPeriodCountForTrimester(): void
    {
        $this->expectException(InvalidPeriodCountException::class);

        new PeriodConfiguration(PeriodType::TRIMESTER, [
            new AcademicPeriod(1, 'S1', new DateTimeImmutable('2025-09-01'), new DateTimeImmutable('2026-01-31')),
            new AcademicPeriod(2, 'S2', new DateTimeImmutable('2026-02-01'), new DateTimeImmutable('2026-06-30')),
        ]);
    }

    #[Test]
    public function itRejectsWrongPeriodCountForSemester(): void
    {
        $this->expectException(InvalidPeriodCountException::class);

        new PeriodConfiguration(PeriodType::SEMESTER, [
            new AcademicPeriod(1, 'T1', new DateTimeImmutable('2025-09-01'), new DateTimeImmutable('2025-11-30')),
            new AcademicPeriod(2, 'T2', new DateTimeImmutable('2025-12-01'), new DateTimeImmutable('2026-02-28')),
            new AcademicPeriod(3, 'T3', new DateTimeImmutable('2026-03-01'), new DateTimeImmutable('2026-06-30')),
        ]);
    }

    #[Test]
    public function itRejectsOverlappingPeriods(): void
    {
        $this->expectException(PeriodsOverlapException::class);

        new PeriodConfiguration(PeriodType::TRIMESTER, [
            new AcademicPeriod(1, 'T1', new DateTimeImmutable('2025-09-01'), new DateTimeImmutable('2025-12-01')),
            new AcademicPeriod(2, 'T2', new DateTimeImmutable('2025-11-30'), new DateTimeImmutable('2026-02-28')),
            new AcademicPeriod(3, 'T3', new DateTimeImmutable('2026-03-01'), new DateTimeImmutable('2026-06-30')),
        ]);
    }

    #[Test]
    public function itRejectsCoverageGap(): void
    {
        $this->expectException(PeriodsCoverageGapException::class);

        new PeriodConfiguration(PeriodType::TRIMESTER, [
            new AcademicPeriod(1, 'T1', new DateTimeImmutable('2025-09-01'), new DateTimeImmutable('2025-11-28')),
            new AcademicPeriod(2, 'T2', new DateTimeImmutable('2025-12-01'), new DateTimeImmutable('2026-02-28')),
            new AcademicPeriod(3, 'T3', new DateTimeImmutable('2026-03-01'), new DateTimeImmutable('2026-06-30')),
        ]);
    }

    #[Test]
    public function itSortsPeriodsByStartDate(): void
    {
        $config = new PeriodConfiguration(PeriodType::TRIMESTER, [
            new AcademicPeriod(3, 'T3', new DateTimeImmutable('2026-03-01'), new DateTimeImmutable('2026-06-30')),
            new AcademicPeriod(1, 'T1', new DateTimeImmutable('2025-09-01'), new DateTimeImmutable('2025-11-30')),
            new AcademicPeriod(2, 'T2', new DateTimeImmutable('2025-12-01'), new DateTimeImmutable('2026-02-28')),
        ]);

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

    #[Test]
    public function itFindsCurrentPeriod(): void
    {
        $config = $this->validTrimesterConfig();

        $current = $config->currentPeriod(new DateTimeImmutable('2025-10-15'));
        self::assertNotNull($current);
        self::assertSame('T1', $current->label);

        $current = $config->currentPeriod(new DateTimeImmutable('2026-01-15'));
        self::assertNotNull($current);
        self::assertSame('T2', $current->label);

        $current = $config->currentPeriod(new DateTimeImmutable('2026-05-01'));
        self::assertNotNull($current);
        self::assertSame('T3', $current->label);
    }

    #[Test]
    public function itReturnsNullWhenNoCurrentPeriod(): void
    {
        $config = $this->validTrimesterConfig();

        self::assertNull($config->currentPeriod(new DateTimeImmutable('2025-08-01')));
        self::assertNull($config->currentPeriod(new DateTimeImmutable('2026-07-01')));
    }

    #[Test]
    public function itFindsPeriodBySequence(): void
    {
        $config = $this->validTrimesterConfig();

        $period = $config->periodBySequence(2);
        self::assertNotNull($period);
        self::assertSame('T2', $period->label);

        self::assertNull($config->periodBySequence(4));
    }

    private function validTrimesterConfig(): PeriodConfiguration
    {
        return new PeriodConfiguration(PeriodType::TRIMESTER, [
            new AcademicPeriod(1, 'T1', new DateTimeImmutable('2025-09-01'), new DateTimeImmutable('2025-11-30')),
            new AcademicPeriod(2, 'T2', new DateTimeImmutable('2025-12-01'), new DateTimeImmutable('2026-02-28')),
            new AcademicPeriod(3, 'T3', new DateTimeImmutable('2026-03-01'), new DateTimeImmutable('2026-06-30')),
        ]);
    }
}
