<?php /** @noinspection PhpUnhandledExceptionInspection */

namespace App\Tests\Unit\Service\Organization;

use App\Entity\Organization\Organization;
use App\Entity\Organization\Parameters;
use App\Entity\Organization\Settings;
use App\Entity\Organization\Subdomain;
use App\Enum\Organization\OrganizationIdsEnum;
use App\Enum\Organization\SettingsProductEnum;
use App\Service\Organization\Utils as OrganizationUtils;
use App\Service\Utils\DatesUtils;
use Doctrine\Common\Collections\ArrayCollection;
use PHPUnit\Framework\TestCase;

class TestableOrganizationUtils extends OrganizationUtils {
    public function isOrganizationIdEqualTo(Organization $organization, OrganizationIdsEnum $organizationIdsEnum): bool {
        return parent::isOrganizationIdEqualTo($organization, $organizationIdsEnum);
    }
}

class UtilsTest extends TestCase
{
    /**
     * @see OrganizationUtils::isStructure()
     */
    public function testIsStructure(): void
    {
        $organizationUtils = $this->getMockBuilder(TestableOrganizationUtils::class)->setMethodsExcept(['isStructure'])->getMock();

        $settings = $this->getMockBuilder(Settings::class)->getMock();
        $organization = $this->getMockBuilder(Organization::class)->getMock();
        $organization->method('getSettings')->willReturn($settings);

        // Each cal to 'isStructure' provoke 2 calls on getProduct
        $settings->method('getProduct')->willReturnOnConsecutiveCalls(
            SettingsProductEnum::ARTIST()->getValue(),
            SettingsProductEnum::ARTIST()->getValue(),
            SettingsProductEnum::ARTIST_PREMIUM()->getValue(),
            SettingsProductEnum::ARTIST_PREMIUM()->getValue(),
            SettingsProductEnum::SCHOOL()->getValue(),
            SettingsProductEnum::SCHOOL()->getValue(),
            SettingsProductEnum::SCHOOL_PREMIUM()->getValue(),
            SettingsProductEnum::SCHOOL_PREMIUM()->getValue(),
            SettingsProductEnum::MANAGER()->getValue(),
            SettingsProductEnum::MANAGER()->getValue(),
            SettingsProductEnum::MANAGER_PREMIUM()->getValue(),
            SettingsProductEnum::MANAGER_PREMIUM()->getValue(),
        );

        $this->assertTrue($organizationUtils->isStructure($organization));
        $this->assertTrue($organizationUtils->isStructure($organization));
        $this->assertTrue($organizationUtils->isStructure($organization));
        $this->assertTrue($organizationUtils->isStructure($organization));
        $this->assertFalse($organizationUtils->isStructure($organization));
        $this->assertFalse($organizationUtils->isStructure($organization));
    }

    /**
     * @see OrganizationUtils::isStructure()
     */
    public function testIsManager(): void
    {
        $organizationUtils = $this->getMockBuilder(TestableOrganizationUtils::class)->setMethodsExcept(['isManager'])->getMock();

        $settings = $this->getMockBuilder(Settings::class)->getMock();
        $organization = $this->getMockBuilder(Organization::class)->getMock();
        $organization->method('getSettings')->willReturn($settings);

        $settings->method('getProduct')->willReturnOnConsecutiveCalls(
            SettingsProductEnum::ARTIST()->getValue(),
            SettingsProductEnum::ARTIST_PREMIUM()->getValue(),
            SettingsProductEnum::SCHOOL()->getValue(),
            SettingsProductEnum::SCHOOL_PREMIUM()->getValue(),
            SettingsProductEnum::MANAGER()->getValue(),
            SettingsProductEnum::MANAGER_PREMIUM()->getValue(),
        );

        $this->assertFalse($organizationUtils->isManager($organization));
        $this->assertFalse($organizationUtils->isManager($organization));
        $this->assertFalse($organizationUtils->isManager($organization));
        $this->assertFalse($organizationUtils->isManager($organization));
        $this->assertTrue($organizationUtils->isManager($organization));
        $this->assertFalse($organizationUtils->isManager($organization));
    }

    /**
     * @see OrganizationUtils::is2iosOrganization()
     */
    public function testIsOrganizationIs2ios(): void
    {
        $organizationUtils = $this->getMockBuilder(TestableOrganizationUtils::class)->setMethodsExcept(['is2iosOrganization'])->getMock();

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

        $organizationUtils->expects(self::once())->method('isOrganizationIdEqualTo')->with($organization, OrganizationIdsEnum::_2IOS());

        $organizationUtils->is2iosOrganization($organization);
    }

    /**
     * @see OrganizationUtils::isOrganizationCMF()
     */
    public function testIsOrganizationIsCMF(): void
    {
        $organizationUtils = $this->getMockBuilder(TestableOrganizationUtils::class)->setMethodsExcept(['isOrganizationCMF'])->getMock();

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

        $organizationUtils->expects(self::once())->method('isOrganizationIdEqualTo')->with($organization, OrganizationIdsEnum::CMF());

        $organizationUtils->isOrganizationCMF($organization);
    }


    /**
     * @see OrganizationUtils::is2iosOrganization()
     */
    public function testIsOrganizationIdEqualTo(): void
    {
        $organizationUtils = $this->getMockBuilder(TestableOrganizationUtils::class)->setMethodsExcept(['isOrganizationIdEqualTo'])->getMock();

        $organization = $this->getMockBuilder(Organization::class)->getMock();
        $organization->method('getId')->willReturnOnConsecutiveCalls(123, OrganizationIdsEnum::_2IOS()->getValue());

        $this->assertFalse($organizationUtils->isOrganizationIdEqualTo($organization, OrganizationIdsEnum::_2IOS()));
        $this->assertTrue($organizationUtils->isOrganizationIdEqualTo($organization, OrganizationIdsEnum::_2IOS()));
    }

    /**
     * @see OrganizationUtils::getOrganizationCurrentActivityYear()
     */
    public function testGetOrganizationCurrentActivityYear(): void
    {
        $organizationUtils = $this->getMockBuilder(TestableOrganizationUtils::class)->setMethodsExcept(['getOrganizationCurrentActivityYear'])->getMock();

        $parameters = $this->getMockBuilder(Parameters::class)->getMock();
        $parameters->method('getMusicalDate')->willReturnOnConsecutiveCalls(
                null,  // default should be '2020-08-31'
                new \DateTime('2020-10-01')
            );

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

        DatesUtils::setFakeDatetime('2020-03-01');

        $this->assertEquals(
            2019,
            $organizationUtils->getOrganizationCurrentActivityYear($organization)
        );

        DatesUtils::setFakeDatetime('2020-11-01');

        $this->assertEquals(
            2020,
            $organizationUtils->getOrganizationCurrentActivityYear($organization)
        );
    }

    /**
     * @see OrganizationUtils::getActivityPeriodsSwitchYear()
     */
    public function testGetActivityPeriodsSwitchYear(): void
    {
        $organizationUtils = $this->getMockBuilder(TestableOrganizationUtils::class)->setMethodsExcept(['getActivityPeriodsSwitchYear'])->getMock();

        $parameters = $this->getMockBuilder(Parameters::class)->getMock();
        $parameters->method('getMusicalDate')->willReturn(new \DateTime('2020-09-05'));

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

        $periods = $organizationUtils->getActivityPeriodsSwitchYear($organization, 2022);

        $this->assertEquals('2022-09-05', $periods['dateStart']);
        $this->assertEquals('2023-09-04', $periods['dateEnd']);
    }

    /**
     * @see OrganizationUtils::getActivityPeriodsSwitchYear()
     */
    public function testGetActivityPeriodsSwitchYearNoMusicalDate(): void
    {
        $organizationUtils = $this->getMockBuilder(TestableOrganizationUtils::class)->setMethodsExcept(['getActivityPeriodsSwitchYear'])->getMock();

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

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

        $periods = $organizationUtils->getActivityPeriodsSwitchYear($organization, 2022);

        $this->assertEquals('2022-09-01', $periods['dateStart']);
        $this->assertEquals('2023-08-31', $periods['dateEnd']);
    }

    /**
     * @see OrganizationUtils::getActivityYearSwitchDate()
     */
    public function testGetActivityYearSwitchDate(): void
    {
        $organizationUtils = $this->getMockBuilder(TestableOrganizationUtils::class)->setMethodsExcept(['getActivityYearSwitchDate'])->getMock();

        $parameters = $this->getMockBuilder(Parameters::class)->getMock();
        $parameters->method('getMusicalDate')->willReturn(new \DateTime('2020-09-05'));

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

        $this->assertEquals(2022, $organizationUtils->getActivityYearSwitchDate($organization, new \DateTime('2022-09-10')));
        $this->assertEquals(2021, $organizationUtils->getActivityYearSwitchDate($organization, new \DateTime('2022-09-02')));
    }

    /**
     * @see OrganizationUtils::getActivityYearSwitchDate()
     */
    public function testGetActivityYearSwitchDateNoMusicalDate(): void
    {
        $organizationUtils = $this->getMockBuilder(TestableOrganizationUtils::class)->setMethodsExcept(['getActivityYearSwitchDate'])->getMock();

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

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

        $this->assertEquals(2021, $organizationUtils->getActivityYearSwitchDate($organization, new \DateTime('2022-08-01')));
        $this->assertEquals(2022, $organizationUtils->getActivityYearSwitchDate($organization, new \DateTime('2022-09-02')));
    }

    /**
     * @see OrganizationUtils::getOrganizationActiveSubdomain()
     */
    public function testGetOrganizationActiveSubdomain(): void {
        $organizationUtils = $this->getMockBuilder(TestableOrganizationUtils::class)->setMethodsExcept(['getOrganizationActiveSubdomain'])->getMock();

        $subdomain1 = $this->getMockBuilder(Subdomain::class)->getMock();
        $subdomain1->method('isActive')->willReturn(false);

        $subdomain2 = $this->getMockBuilder(Subdomain::class)->getMock();
        $subdomain2->method('isActive')->willReturn(false);

        $subdomain3 = $this->getMockBuilder(Subdomain::class)->getMock();
        $subdomain3->method('isActive')->willReturn(true);
        $subdomain3->method('getSubdomain')->willReturn('foo');

        $organization = $this->getMockBuilder(Organization::class)->getMock();
        $organization->method('getSubdomains')->willReturn(new ArrayCollection([$subdomain1, $subdomain2, $subdomain3]));

        $this->assertEquals('foo', $organizationUtils->getOrganizationActiveSubdomain($organization));
    }

    /**
     * @see OrganizationUtils::getOrganizationActiveSubdomain()
     */
    public function testGetOrganizationActiveSubdomainNone(): void {
        $organizationUtils = $this->getMockBuilder(TestableOrganizationUtils::class)->setMethodsExcept(['getOrganizationActiveSubdomain'])->getMock();

        $subdomain1 = $this->getMockBuilder(Subdomain::class)->getMock();
        $subdomain1->method('isActive')->willReturn(false);

        $organization = $this->getMockBuilder(Organization::class)->getMock();
        $organization->method('getSubdomains')->willReturn(new ArrayCollection([$subdomain1]));

        $this->assertEquals(null, $organizationUtils->getOrganizationActiveSubdomain($organization));
    }

    /**
     * @see OrganizationUtils::getOrganizationWebsite()
     */
    public function testOrganizationWebsite(): void
    {
        $organizationUtils = $this->getMockBuilder(TestableOrganizationUtils::class)->setMethodsExcept(['getOrganizationWebsite'])->getMock();

        $parameters = $this->getMockBuilder(Parameters::class)->getMock();
        $parameters->method('getDesactivateOpentalentSiteWeb')->willReturn(false);
        $parameters->method('getCustomDomain')->willReturn(null);

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

        $organizationUtils->method('getOrganizationActiveSubdomain')->with($organization)->willReturn('foo');

        $this->assertEquals('https://foo.opentalent.fr', $organizationUtils->getOrganizationWebsite($organization));
    }

    /**
     * @see OrganizationUtils::getOrganizationWebsite()
     */
    public function testOrganizationWebsiteDeactivatedWithOther(): void
    {
        $organizationUtils = $this->getMockBuilder(TestableOrganizationUtils::class)->setMethodsExcept(['getOrganizationWebsite'])->getMock();

        $parameters = $this->getMockBuilder(Parameters::class)->getMock();
        $parameters->method('getDesactivateOpentalentSiteWeb')->willReturn(true);
        $parameters->method('getOtherWebsite')->willReturn('foo.net');

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

        $this->assertEquals('https://foo.net', $organizationUtils->getOrganizationWebsite($organization));
    }

    /**
     * @see OrganizationUtils::getOrganizationWebsite()
     */
    public function testOrganizationWebsiteDeactivatedNoOther(): void
    {
        $organizationUtils = $this->getMockBuilder(TestableOrganizationUtils::class)->setMethodsExcept(['getOrganizationWebsite'])->getMock();

        $parameters = $this->getMockBuilder(Parameters::class)->getMock();
        $parameters->method('getDesactivateOpentalentSiteWeb')->willReturn(true);
        $parameters->method('getOtherWebsite')->willReturn(null);

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

        $this->assertEquals(null, $organizationUtils->getOrganizationWebsite($organization));
    }

    /**
     * @see OrganizationUtils::getOrganizationWebsite()
     */
    public function testOrganizationWebsiteWithCustom(): void
    {
        $organizationUtils = $this->getMockBuilder(TestableOrganizationUtils::class)->setMethodsExcept(['getOrganizationWebsite'])->getMock();

        $parameters = $this->getMockBuilder(Parameters::class)->getMock();
        $parameters->method('getDesactivateOpentalentSiteWeb')->willReturn(false);
        $parameters->method('getCustomDomain')->willReturn('bar.net');

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

        $this->assertEquals('https://bar.net', $organizationUtils->getOrganizationWebsite($organization));
    }

    /**
     * @see OrganizationUtils::getOrganizationWebsite()
     */
    public function testOrganizationWebsiteNoSubdomains(): void
    {
        $organizationUtils = $this->getMockBuilder(TestableOrganizationUtils::class)->setMethodsExcept(['getOrganizationWebsite'])->getMock();

        $parameters = $this->getMockBuilder(Parameters::class)->getMock();
        $parameters->method('getDesactivateOpentalentSiteWeb')->willReturn(false);
        $parameters->method('getCustomDomain')->willReturn(null);

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

        $organizationUtils->method('getOrganizationActiveSubdomain')->with($organization)->willReturn(null);

        $this->assertEquals(null, $organizationUtils->getOrganizationWebsite($organization));
    }
}
