<?php

namespace App\Tests\Unit\Service\Organization;

use App\ApiResources\Profile\OrganizationProfile;
use App\Entity\Network\Network;
use App\Entity\Network\NetworkOrganization;
use App\Entity\Organization\Organization;
use App\Entity\Organization\Parameters;
use App\Entity\Organization\Settings;
use App\Enum\Organization\PrincipalTypeEnum;
use App\Service\Network\Tree;
use App\Service\Organization\OrganizationProfileCreator;
use App\Service\Organization\Utils as OrganizationUtils;
use App\Service\Security\Module;
use Doctrine\Common\Collections\ArrayCollection;
use PHPUnit\Framework\TestCase;

class OrganizationProfileCreatorTest extends TestCase
{
    private Module $module;
    private Tree $tree;
    private OrganizationUtils $organizationUtils;

    public function setUp():void
    {
        $this->module = $this->getMockBuilder(Module::class)->disableOriginalConstructor()->getMock();
        $this->tree = $this->getMockBuilder(Tree::class)->disableOriginalConstructor()->getMock();
        $this->organizationUtils = $this->getMockBuilder(OrganizationUtils::class)->disableOriginalConstructor()->getMock();
    }

    /**
     * @see OrganizationProfileCreator::createCompleteOrganizationProfile()
     */
    public function testCreateCompleteOrganizationProfile(): void
    {
        $organizationProfileCreator = $this->getMockBuilder(OrganizationProfileCreator::class)
            ->setConstructorArgs([$this->module, $this->tree, $this->organizationUtils])
            ->setMethodsExcept(['createCompleteOrganizationProfile'])
            ->getMock();

        $organization = $this->getMockBuilder(Organization::class)->getMock();
        $organizationProfile = $this->getMockBuilder(OrganizationProfile::class)->getMock();

        $modules = ['1'];
        $this->module->method('getOrganizationModules')->with($organization)->willReturn($modules);
        $organizationProfile->expects(self::once())->method('setModules')->with($modules);

        $product = "product";
        $settings = $this->getMockBuilder(Settings::class)->getMock();
        $settings->method('getProduct')->willReturn($product);
        $organization->method('getSettings')->willReturn($settings);
        $organizationProfile->expects(self::once())->method('setProduct')->with($product);

        $parametersId = 101;
        $parameters = $this->getMockBuilder(Parameters::class)->getMock();
        $parameters->method('getId')->willReturn($parametersId);
        $organization->method('getParameters')->willReturn($parameters);
        $organizationProfile->expects(self::once())->method('setParametersId')->with($parametersId);

        $organization->method('getLegalStatus')->willReturn('foo');
        $organizationProfile->expects(self::once())->method('setLegalStatus')->with('foo');

        $organization->method('getNetworkOrganizationChildren')->willReturn(new ArrayCollection([1, 2, 3]));
        $organizationProfile->expects(self::once())->method('setHasChildren')->with(true);

        $parameters->method('getShowAdherentList')->willReturn(true);
        $organization->method('getPrincipalType')->willReturn(PrincipalTypeEnum::LOCAL_FEDERATION()->getValue());
        $organizationProfile->expects(self::once())->method('setShowAdherentList')->with(true);

        $network1 = $this->getMockBuilder(Network::class)->getMock();
        $network1->method('getName')->willReturn('Net 1');
        $networkOrganization1 = $this->getMockBuilder(NetworkOrganization::class)->getMock();
        $networkOrganization1->method('getNetwork')->willReturn($network1);

        $network2 = $this->getMockBuilder(Network::class)->getMock();
        $network2->method('getName')->willReturn('Net 2');
        $networkOrganization2 = $this->getMockBuilder(NetworkOrganization::class)->getMock();
        $networkOrganization2->method('getNetwork')->willReturn($network2);

        $organization->method('getNetworkOrganizations')->willReturn(new ArrayCollection([$networkOrganization1, $networkOrganization2]));

        $organizationProfile->expects(self::exactly(2))->method('addNetwork')->withConsecutive(['Net 1'], ['Net 2']);

        $parent1 = $this->getMockBuilder(Organization::class)->getMock();
        $parent2 = $this->getMockBuilder(Organization::class)->getMock();
        $this->tree->method('findAllParentsAndSortByType')->willReturn([$parent1, $parent2]);

        $parentProfile1 = $this->getMockBuilder(OrganizationProfile::class)->getMock();
        $parentProfile2 = $this->getMockBuilder(OrganizationProfile::class)->getMock();

        // Called 3 times, once for the organization, two for its parents
        $organizationProfileCreator
            ->expects(self::exactly(3))
            ->method('createLightOrganizationProfile')
            ->withConsecutive([$organization], [$parent1], [$parent2])
            ->willReturnOnConsecutiveCalls($organizationProfile, $parentProfile1, $parentProfile2);

        $organizationProfile
            ->expects(self::exactly(2))
            ->method('addParent')
            ->withConsecutive([$parentProfile1], [$parentProfile2]);

        $this->organizationUtils->method('getOrganizationCurrentActivityYear')->with($organization)->willReturn(2022);
        $organizationProfile->expects(self::once())->method('setCurrentYear')->with();

        $returned = $organizationProfileCreator->createCompleteOrganizationProfile($organization);

        $this->assertSame(
            $organizationProfile,
            $returned
        );
    }

    /**
     * If the organization principal type is ARTISTIC_EDUCATION_ONLY, then showAdherentList should be set to false
     * @see OrganizationProfileCreator::createCompleteOrganizationProfile()
     */
    public function testCreateOrganizationProfileWithoutAdherentListBecauseOfPrincipalType(): void
    {
        $organizationProfileCreator = $this->getMockBuilder(OrganizationProfileCreator::class)
            ->setConstructorArgs([$this->module, $this->tree, $this->organizationUtils])
            ->setMethodsExcept(['createCompleteOrganizationProfile'])
            ->getMock();

        $organization = $this->getMockBuilder(Organization::class)->getMock();
        $organizationProfile = $this->getMockBuilder(OrganizationProfile::class)->getMock();
        $organizationProfileCreator->method('createLightOrganizationProfile')->with($organization)->willReturn($organizationProfile);

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

        $parameters->method('getShowAdherentList')->willReturn(true);
        $organization->method('getPrincipalType')->willReturn(PrincipalTypeEnum::ARTISTIC_EDUCATION_ONLY()->getValue());
        $organizationProfile->expects(self::once())->method('setShowAdherentList')->with(false);

        $organization->method('getNetworkOrganizations')->willReturn(new ArrayCollection([]));
        $this->tree->method('findAllParentsAndSortByType')->willReturn([]);

        $organizationProfileCreator->createCompleteOrganizationProfile($organization);

    }

    /**
     * If the parameters::showAdherentList is false, then organizationProfile::showAdherentList should be set to false
     * @see OrganizationProfileCreator::createCompleteOrganizationProfile()
     */
    public function testCreateOrganizationProfileWithoutAdherentList(): void
    {
        $organizationProfileCreator = $this->getMockBuilder(OrganizationProfileCreator::class)
            ->setConstructorArgs([$this->module, $this->tree, $this->organizationUtils])
            ->setMethodsExcept(['createCompleteOrganizationProfile'])
            ->getMock();

        $organization = $this->getMockBuilder(Organization::class)->getMock();
        $organizationProfile = $this->getMockBuilder(OrganizationProfile::class)->getMock();
        $organizationProfileCreator->method('createLightOrganizationProfile')->with($organization)->willReturn($organizationProfile);

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

        $parameters->method('getShowAdherentList')->willReturn(false);
        $organization->method('getPrincipalType')->willReturn(PrincipalTypeEnum::LOCAL_FEDERATION()->getValue());
        $organizationProfile->expects(self::once())->method('setShowAdherentList')->with(false);

        $organization->method('getNetworkOrganizations')->willReturn(new ArrayCollection([]));
        $this->tree->method('findAllParentsAndSortByType')->willReturn([]);

        $organizationProfileCreator->createCompleteOrganizationProfile($organization);
    }

    /**
     * @see OrganizationProfileCreator::createLightOrganizationProfile()
     */
    public function testCreateLightOrganizationProfile(): void {
        $organizationProfileCreator = $this->getMockBuilder(OrganizationProfileCreator::class)
            ->setConstructorArgs([$this->module, $this->tree, $this->organizationUtils])
            ->setMethodsExcept(['createLightOrganizationProfile'])
            ->getMock();

        $organization = $this->getMockBuilder(Organization::class)->getMock();

        $organization->method('getId')->willReturn(123);
        $organization->method('getName')->willReturn('Foo');
        $this->organizationUtils->method('getOrganizationWebsite')->with($organization)->willReturn('foo.net');

        $result = $organizationProfileCreator->createLightOrganizationProfile($organization);

        $this->assertEquals(123, $result->getId());
        $this->assertEquals('Foo', $result->getName());
        $this->assertEquals('foo.net', $result->getWebsite());
    }
}
