<?php /** @noinspection PhpUnhandledExceptionInspection */

namespace App\Tests\Unit\Service\Export;

use App\ApiResources\Export\ExportRequest;
use App\ApiResources\Export\LicenceCmf\LicenceCmfOrganizationER;
use App\Entity\Access\Access;
use App\Entity\Core\File;
use App\Entity\Network\NetworkOrganization;
use App\Entity\Organization\Organization;
use App\Entity\Organization\Parameters;
use App\Entity\Person\Person;
use App\Enum\Access\FunctionEnum;
use App\Repository\Access\AccessRepository;
use App\Repository\Organization\OrganizationRepository;
use App\Service\Export\Encoder\PdfEncoder;
use App\Service\Export\LicenceCmfExporter;
use App\Service\Export\Model\LicenceCmf;
use App\Service\File\FileManager;
use App\Service\File\Storage\LocalStorage;
use App\Service\ServiceIterator\EncoderIterator;
use App\Tests\Unit\TestToolsTrait;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use RuntimeException;
use Twig\Environment;

class LicenceCmfExporterTest extends TestCase
{
    use TestToolsTrait;

    private MockObject | LicenceCmfOrganizationER $exportRequest;
    private MockObject | AccessRepository $accessRepo;
    private MockObject | Environment $twig;
    private MockObject | EncoderIterator $encoderIterator;
    private MockObject | EntityManagerInterface $em;
    private MockObject | LocalStorage $localStorage;
    private MockObject | FileManager $fileManager;
    private MockObject | OrganizationRepository $organizationRepo;
    private MockObject | Access $access;
    private MockObject | Organization $organization;
    private MockObject | Organization $cmf;
    private MockObject | NetworkOrganization $networkOrgs;
    private MockObject | Collection $collection;
    private MockObject | Organization $parent;
    private MockObject | File $logo;
    private MockObject | Access $presidentAccess;
    private MockObject | Person $president;
    private MockObject | Parameters $cmfParameters;
    private MockObject | File $qrCode;
    private MockObject | PdfEncoder $encoder;

    public function setUp(): void {
        $this->exportRequest = $this->getMockBuilder(LicenceCmfOrganizationER::class)->getMock();
        $this->accessRepo = $this->getMockBuilder(AccessRepository::class)->disableOriginalConstructor()->getMock();
        $this->twig = $this->getMockBuilder(Environment::class)->disableOriginalConstructor()->getMock();
        $this->encoderIterator = $this->getMockBuilder(EncoderIterator::class)->disableOriginalConstructor()->getMock();
        $this->encoder = $this->getMockBuilder(PdfEncoder::class)->disableOriginalConstructor()->getMock();
        $this->em = $this->getMockBuilder(EntityManagerInterface::class)->disableOriginalConstructor()->getMock();
        $this->localStorage = $this->getMockBuilder(LocalStorage::class)->disableOriginalConstructor()->getMock();
        $this->fileManager = $this->getMockBuilder(FileManager::class)->disableOriginalConstructor()->getMock();
        $this->organizationRepo = $this->getMockBuilder(OrganizationRepository::class)->disableOriginalConstructor()->getMock();
        $this->access = $this->getMockBuilder(Access::class)->getMock();
        $this->organization = $this->getMockBuilder(Organization::class)->getMock();
        $this->cmf = $this->getMockBuilder(Organization::class)->getMock();
        $this->networkOrgs = $this->getMockBuilder(NetworkOrganization::class)->getMock();
        $this->collection = $this->getMockBuilder(Collection::class)->getMock();
        $this->parent = $this->getMockBuilder(Organization::class)->getMock();
        $this->logo = $this->getMockBuilder(File::class)->getMock();
        $this->presidentAccess = $this->getMockBuilder(Access::class)->getMock();
        $this->president = $this->getMockBuilder(Person::class)->getMock();
        $this->cmfParameters = $this->getMockBuilder(Parameters::class)->getMock();
        $this->qrCode = $this->getMockBuilder(File::class)->getMock();
    }

    private function makeExporterMock(string $methodUnderTest): LicenceCmfExporter | MockObject
    {
        $exporter = $this->getMockBuilder(LicenceCmfExporter::class)
            ->setConstructorArgs([$this->organizationRepo])
            ->setMethodsExcept(['setAccessRepository', 'setTwig', 'setEncoderIterator',
                'setEntityManager', 'setEntityManager', 'setLocalStorage', 'setFileManager', $methodUnderTest])
            ->getMock();

        $exporter->setAccessRepository($this->accessRepo);
        $exporter->setTwig($this->twig);
        $exporter->setEncoderIterator($this->encoderIterator);
        $exporter->setEntityManager($this->em);
        $exporter->setLocalStorage($this->localStorage);
        $exporter->setFileManager($this->fileManager);

        return $exporter;
    }

    /**
     * @see LicenceCmfExporter::support()
     */
    public function testSupport(): void
    {
        $exporter = $this->makeExporterMock('support');

        $unsupportedExportRequest = $this->getMockBuilder(ExportRequest::class)->disableOriginalConstructor()->getMock();

        $this->assertTrue($exporter->support($this->exportRequest));
        $this->assertFalse($exporter->support($unsupportedExportRequest));
    }

    private function prepareModelBuilding(): void
    {
        $this->exportRequest->method('getRequesterId')->willReturn(1);
        $this->exportRequest->method('getYear')->willReturn(2020);
        $this->exportRequest->method('getFormat')->willReturn('pdf');

        $this->accessRepo->method('find')->with(1)->willReturn($this->access);
        $this->access->method('getOrganization')->willReturn($this->organization);
        $this->organization->method('getId')->willReturn(1);
        $this->organization->method('getName')->willReturn('my_organization');
        $this->organization->method('getIdentifier')->willReturn('org1');
        $this->organization->method('getNetworkOrganizations')->willReturn($this->collection);
        $this->collection->method('get')->willReturn($this->networkOrgs);
        $this->networkOrgs->expects(self::once())->method('getParent')->willReturn($this->parent);
        $this->parent->expects(self::once())->method('getName')->willReturn('my_network');
        $this->organization->method('getLogo')->willReturn($this->logo);
        $this->logo->method('getId')->willReturn(1);
        $this->fileManager->method('getDownloadIri')->willReturn('http:://foo.bar/1');
        $this->president->method('getId')->willReturn(1);
        $this->president->method('getGender')->willReturn('M');
        $this->president->method('getGivenName')->willReturn('Joe');
        $this->president->method('getName')->willReturn('Dalton');
        $this->accessRepo
            ->expects(self::once())
            ->method('findByOrganizationAndMission')
            ->with($this->organization, FunctionEnum::PRESIDENT()->getValue())
            ->willReturn([$this->presidentAccess]);
        $this->presidentAccess->method('getPerson')->willReturn($this->president);
        $this->cmf->method('getParameters')->willReturn($this->cmfParameters);
        $this->cmfParameters->expects(self::once())->method('getQrCode')->willReturn($this->qrCode);
        $this->qrCode->method('getId')->willReturn(1);
        $this->organizationRepo->expects(self::once())->method('find')->with(12097)->willReturn($this->cmf);
    }

    /**
     * @see LicenceCmfExporter::buildModel()
     */
    public function testBuildModel(): void
    {
        $exporter = $this->makeExporterMock('buildModel');

        $this->prepareModelBuilding();

        $modelCollection = $this->invokeMethod($exporter, 'buildModel', [$this->exportRequest]);

        $licence = $modelCollection->getLicences()[0];

        $logo = $this->getMockBuilder(File::class)->getMock();
        $qrCode = $this->getMockBuilder(File::class)->getMock();

        $this->assertEquals('my_network', $licence->getFederationName());
        $this->assertEquals(true, $licence->isOrganizationLicence());
        $this->assertEquals(2020, $licence->getYear());
        $this->assertEquals('931572', $licence->getColor());
        $this->assertEquals($qrCode, $licence->getQrCode());
        $this->assertEquals($logo, $licence->getLogo());
    }

    /**
     * If the organization cannot be determined from the exportRequest author, an error should be thrown
     * @see LicenceCmfExporter::buildModel()
     */
    public function testBuildModelWithoutOrganization(): void
    {
        $exporter = $this->makeExporterMock('buildModel');

        $this->exportRequest->method('getRequesterId')->willReturn(1);
        $this->accessRepo->method('find')->with(1)->willReturn(null);

        $this->expectException(RuntimeException::class);

        $this->invokeMethod($exporter, 'buildModel', [$this->exportRequest]);
    }

    /**
     * @see LicenceCmfExporter::getFileBasename()
     */
    public function testGetFileBasename(): void
    {
        $exporter = $this->makeExporterMock('getFileBasename');

        $licence = $this->getMockBuilder(LicenceCmf::class)->getMock();
        $licence->method('getYear')->willReturn(2020);

        $exportRequest = $this->getMockBuilder(LicenceCmfOrganizationER::class)->getMock();
        $exportRequest->method('getYear')->willReturn(2020);

        $result = $this->invokeMethod($exporter, 'getFileBasename', [$exportRequest]);

        $this->assertEquals(
            'licence_cmf_2020.pdf',
            $result
        );
    }

    /**
     * @see LicenceCmfExporter::getFileType()
     */
    public function testGetFileType(): void
    {
        $exporter = $this->makeExporterMock('getFileType');

        $this->assertEquals(
            'LICENCE_CMF',
            $this->invokeMethod($exporter, 'getFileType')
        );
    }

    /**
     * Given year is before the start year, first color is used
     * @see LicenceCmfExporter::getLicenceColor()
     */
    public function testGetLicenceColorPastYear(): void
    {
        $exporter = $this->makeExporterMock('getLicenceColor');

        $year = LicenceCmfExporter::LICENCE_CMF_COLOR_START_YEAR - 10;

        $this->assertEquals(
            LicenceCmfExporter::LICENCE_CMF_COLOR[0],
            $this->invokeMethod($exporter, 'getLicenceColor', [$year])
        );
    }

    /**
     * Given year is within the 5 first years
     * @see LicenceCmfExporter::getLicenceColor()
     */
    public function testGetLicenceColorFirstYears(): void
    {
        $exporter = $this->makeExporterMock('getLicenceColor');

        $year = LicenceCmfExporter::LICENCE_CMF_COLOR_START_YEAR + 3;

        $this->assertEquals(
            LicenceCmfExporter::LICENCE_CMF_COLOR[3],
            $this->invokeMethod($exporter, 'getLicenceColor', [$year])
        );
    }

    /**
     * Given year is beyond the 5 first years
     * @see LicenceCmfExporter::getLicenceColor()
     */
    public function testGetLicenceColorNExtYears(): void
    {
        $exporter = $this->makeExporterMock('getLicenceColor');

        $year = LicenceCmfExporter::LICENCE_CMF_COLOR_START_YEAR + 8;

        $this->assertEquals(
            LicenceCmfExporter::LICENCE_CMF_COLOR[3],
            $this->invokeMethod($exporter, 'getLicenceColor', [$year])
        );
    }
}
