<?php

namespace App\Tests\Unit\Service\Education;

use App\Entity\Access\Access;
use App\Entity\Education\CriteriaNotation;
use App\Entity\Education\EducationNotation;
use App\Entity\Education\EducationStudent;
use App\Entity\Organization\Organization;
use App\Entity\Organization\Parameters;
use App\Enum\Education\TypeCriteriaEnum;
use App\Service\Education\EducationNotationUtils;
use PHPUnit\Framework\TestCase;

class TestableEducationNotationUtils extends EducationNotationUtils {
    public function calculNotationByAMaxNote(EducationNotation $educationNotation, float $noteMax): ?float {
        return parent::calculNotationByAMaxNote($educationNotation, $noteMax);
    }
}

class EducationNotationUtilsTest extends TestCase
{
    /**
     * @see EducationNotationUtils::getNotationTransformed()
     */
    public function testGetNotationTransformed(): void
    {
        $educationNotationUtils = $this->getMockBuilder(TestableEducationNotationUtils::class)
            ->setMethodsExcept(['getNotationTransformed'])
            ->getMock();

        $noteMax = 80;

        $parameters = $this->getMockBuilder(Parameters::class)->getMock();
        $parameters->method('getAverage')->willReturn($noteMax);

        $organization = $this->getMockBuilder(Organization::class)->getMock();
        $organization->method('getParameters')->willReturn($parameters);

        $access = $this->getMockBuilder(Access::class)->getMock();
        $access->method('getOrganization')->willReturn($organization);

        $educationStudent = $this->getMockBuilder(EducationStudent::class)->getMock();
        $educationStudent->method('getAccess')->willReturn($access);

        $educationNotation = $this->getMockBuilder(EducationNotation::class)->getMock();
        $educationNotation->method('getEducationStudent')->willReturn($educationStudent);

        $educationNotationUtils->expects(self::once())->method('calculNotationByAMaxNote')->with($educationNotation, $noteMax);

        $educationNotationUtils->getNotationTransformed($educationNotation);
    }

    /**
     * @see EducationNotationUtils::getNotationOriginal()
     */
    public function testGetNotationOriginal(): void
    {
        $educationNotationUtils = $this->getMockBuilder(TestableEducationNotationUtils::class)
            ->setMethodsExcept(['getNotationOriginal'])
            ->getMock();

        $noteMax = 40;

        $criteriaNotation = $this->getMockBuilder(CriteriaNotation::class)->getMock();
        $criteriaNotation->method('getNoteMax')->willReturn($noteMax);

        $educationNotation = $this->getMockBuilder(EducationNotation::class)->getMock();
        $educationNotation->method('getCriteriaNotation')->willReturn($criteriaNotation);

        $educationNotationUtils->expects(self::once())->method('calculNotationByAMaxNote')->with($educationNotation, $noteMax);

        $educationNotationUtils->getNotationOriginal($educationNotation);
    }

    /**
     * @see EducationNotationUtils::getNotationOriginal()
     */
    public function testGetNotationOriginalWithoutCriteria(): void
    {
        $educationNotationUtils = $this->getMockBuilder(TestableEducationNotationUtils::class)
            ->setMethodsExcept(['getNotationOriginal'])
            ->getMock();

        $educationNotation = $this->getMockBuilder(EducationNotation::class)->getMock();
        $educationNotation->method('getCriteriaNotation')->willReturn(null);

        $educationNotationUtils->expects(self::never())->method('calculNotationByAMaxNote');

        $this->assertNull($educationNotationUtils->getNotationOriginal($educationNotation));
    }

    /**
     * @see EducationNotationUtils::calculNotationByAMaxNote()
     */
    public function testCalculNotationByAMaxNote(): void
    {
        $educationNotationUtils = $this->getMockBuilder(TestableEducationNotationUtils::class)
            ->setMethodsExcept(['calculNotationByAMaxNote'])
            ->getMock();

        $note1 = 60.0;
        $note2 = 0.0;
        $note3 = 100.0;
        $noteMax = 20;

        $criteriaNotation = $this->getMockBuilder(CriteriaNotation::class)->getMock();
        $criteriaNotation->method('getType')->willReturn(TypeCriteriaEnum::WITH_NOTATION()->getValue());

        $educationNotation1 = $this->getMockBuilder(EducationNotation::class)->getMock();
        $educationNotation1->method('getCriteriaNotation')->willReturn($criteriaNotation);
        $educationNotation1->method('getNote')->willReturn($note1);

        $educationNotation2 = $this->getMockBuilder(EducationNotation::class)->getMock();
        $educationNotation2->method('getCriteriaNotation')->willReturn($criteriaNotation);
        $educationNotation2->method('getNote')->willReturn($note2);

        $educationNotation3 = $this->getMockBuilder(EducationNotation::class)->getMock();
        $educationNotation3->method('getCriteriaNotation')->willReturn($criteriaNotation);
        $educationNotation3->method('getNote')->willReturn($note3);

        $this->assertEquals(
            12,
            $educationNotationUtils->calculNotationByAMaxNote($educationNotation1, $noteMax)
        );

        $this->assertEquals(
            0,
            $educationNotationUtils->calculNotationByAMaxNote($educationNotation2, $noteMax)
        );

        $this->assertEquals(
            20,
            $educationNotationUtils->calculNotationByAMaxNote($educationNotation3, $noteMax)
        );
    }

    /**
     * If $educationNotation has no note, it should return null
     * @see EducationNotationUtils::calculNotationByAMaxNote()
     */
    public function testCalculNotationByAMaxNoteWithoutNote(): void
    {
        $educationNotationUtils = $this->getMockBuilder(TestableEducationNotationUtils::class)
            ->setMethodsExcept(['calculNotationByAMaxNote'])
            ->getMock();

        $note = null;
        $noteMax = 20;

        $educationNotation = $this->getMockBuilder(EducationNotation::class)->getMock();
        $educationNotation->expects(self::atLeastOnce())->method('getNote')->willReturn($note);

        $this->assertEquals(
            null,
            $educationNotationUtils->calculNotationByAMaxNote($educationNotation, $noteMax)
        );
    }

    /**
     * If $educationNotation has no criteriaNotation, it should return null
     * @see EducationNotationUtils::calculNotationByAMaxNote()
     */
    public function testCalculNotationByAMaxNoteWithoutCriteria(): void
    {
        $educationNotationUtils = $this->getMockBuilder(TestableEducationNotationUtils::class)
            ->setMethodsExcept(['calculNotationByAMaxNote'])
            ->getMock();

        $note = 50.0;
        $noteMax = 20;

        $educationNotation = $this->getMockBuilder(EducationNotation::class)->getMock();
        $educationNotation->expects(self::atLeastOnce())->method('getNote')->willReturn($note);
        $educationNotation->expects(self::atLeastOnce())->method('getCriteriaNotation')->willReturn(null);

        $this->assertEquals(
            null,
            $educationNotationUtils->calculNotationByAMaxNote($educationNotation, $noteMax)
        );
    }

    /**
     * If $criteriaNotation has WITHOUT_NOTATION type, this should return null
     * @see EducationNotationUtils::calculNotationByAMaxNote()
     */
    public function testCalculNotationByAMaxNoteWithoutNotation(): void
    {
        $educationNotationUtils = $this->getMockBuilder(TestableEducationNotationUtils::class)
            ->setMethodsExcept(['calculNotationByAMaxNote'])
            ->getMock();

        $note = 60.0;
        $noteMax = 20;

        $criteriaNotation = $this->getMockBuilder(CriteriaNotation::class)->getMock();
        $criteriaNotation->expects(self::once())->method('getType')->willReturn(TypeCriteriaEnum::WITHOUT_NOTATION()->getValue());

        $educationNotation = $this->getMockBuilder(EducationNotation::class)->getMock();
        $educationNotation->expects(self::atLeastOnce())->method('getNote')->willReturn($note);
        $educationNotation->expects(self::atLeastOnce())->method('getCriteriaNotation')->willReturn($criteriaNotation);

        $this->assertEquals(
            null,
            $educationNotationUtils->calculNotationByAMaxNote($educationNotation, $noteMax)
        );
    }
}
