<?php

declare(strict_types=1);

namespace App\Tests\Unit\Scolarite\Domain\Service;

use App\Scolarite\Domain\Model\GradingMode;
use App\Scolarite\Domain\Service\SerenityScoreWeights;
use InvalidArgumentException;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

final class SerenityScoreWeightsTest extends TestCase
{
    #[Test]
    public function numericModeUsesStandardWeights(): void
    {
        $weights = SerenityScoreWeights::forMode(GradingMode::NUMERIC_20);

        self::assertSame(0.4, $weights->notesWeight);
        self::assertSame(0.3, $weights->absencesWeight);
        self::assertSame(0.3, $weights->devoirsWeight);
    }

    #[Test]
    public function competencyModeUsesAdaptedWeights(): void
    {
        $weights = SerenityScoreWeights::forMode(GradingMode::COMPETENCIES);

        self::assertSame(0.4, $weights->notesWeight);
        self::assertSame(0.3, $weights->absencesWeight);
        self::assertSame(0.3, $weights->devoirsWeight);
    }

    #[Test]
    public function noGradesModeExcludesNotesComponent(): void
    {
        $weights = SerenityScoreWeights::forMode(GradingMode::NO_GRADES);

        self::assertSame(0.0, $weights->notesWeight);
        self::assertSame(0.5, $weights->absencesWeight);
        self::assertSame(0.5, $weights->devoirsWeight);
    }

    #[Test]
    public function lettersModeUsesStandardWeights(): void
    {
        $weights = SerenityScoreWeights::forMode(GradingMode::LETTERS);

        self::assertSame(0.4, $weights->notesWeight);
        self::assertSame(0.3, $weights->absencesWeight);
        self::assertSame(0.3, $weights->devoirsWeight);
    }

    #[Test]
    public function weightsAlwaysSumToOne(): void
    {
        foreach (GradingMode::cases() as $mode) {
            $weights = SerenityScoreWeights::forMode($mode);
            $sum = $weights->notesWeight + $weights->absencesWeight + $weights->devoirsWeight;
            self::assertEqualsWithDelta(1.0, $sum, 0.001, "Weights for {$mode->value} must sum to 1.0");
        }
    }

    #[Test]
    public function competencyMappingConvertsToPercentage(): void
    {
        $weights = SerenityScoreWeights::forMode(GradingMode::COMPETENCIES);

        self::assertSame(100, $weights->competencyToScore('acquired'));
        self::assertSame(50, $weights->competencyToScore('in_progress'));
        self::assertSame(0, $weights->competencyToScore('not_acquired'));
    }

    #[Test]
    public function nonCompetencyModesReturnNullForMapping(): void
    {
        $weights = SerenityScoreWeights::forMode(GradingMode::NUMERIC_20);

        self::assertNull($weights->competencyToScore('acquired'));
    }

    #[Test]
    public function competencyMappingThrowsOnUnknownLevel(): void
    {
        $weights = SerenityScoreWeights::forMode(GradingMode::COMPETENCIES);

        $this->expectException(InvalidArgumentException::class);
        $this->expectExceptionMessage("Niveau de compétence inconnu : 'unknown_level'");

        $weights->competencyToScore('unknown_level');
    }
}
