<?php
declare(strict_types=1);

namespace App\Tests\Unit\Service\Mailer\Builder;

use App\Entity\Access\Access;
use App\Entity\Core\ContactPoint;
use App\Entity\Message\Email;
use App\Entity\Organization\Organization;
use App\Entity\Person\Person;
use App\Enum\Core\ContactPointTypeEnum;
use App\Enum\Core\EmailSendingTypeEnum;
use App\Enum\Message\ReportMessageStatusEnum;
use App\Repository\Core\ContactPointRepository;
use App\Service\Mailer\Builder\AbstractBuilder;
use App\Service\Mailer\EmailRecipient;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Twig\Environment;

/**
 * Classe AbstractBuilderTest qui test le service AbstractBuilder
 */
class AbstractBuilderTest extends TestCase
{
    private MockObject | ContactPointRepository $contactPointRepository;
    private MockObject | Environment $twig;
    private MockObject | Access $access;

    public function setUp(): void {
        $this->contactPointRepository = $this->getMockBuilder(ContactPointRepository::class)->disableOriginalConstructor()->getMock();
        $this->twig = $this->getMockBuilder(Environment::class)->disableOriginalConstructor()->getMock();
        $this->access = $this->getMockBuilder(Access::class)->getMock();
    }

    /**
     * AbstractBuilder mock maker
     * @param string $methodUnderTest
     * @return AbstractBuilder|MockObject
     */
    private function makeAbstractBuilderMock(string $methodUnderTest): AbstractBuilder | MockObject
    {
        $abstractBuilder = $this->getMockBuilder(AbstractBuilder::class)
            ->setMethodsExcept(['setTwig', 'setContactPointRepository', $methodUnderTest])
            ->getMock();

        $abstractBuilder->setContactPointRepository($this->contactPointRepository);
        $abstractBuilder->setTwig($this->twig);

        return $abstractBuilder;
    }

    /**
     * @see AbstractBuilder::buildEmailEntity()
     */
    public function testBuildEmailEntity(){
        $abstractBuilder = $this->makeAbstractBuilderMock('buildEmailEntity');
        $email = $abstractBuilder->buildEmailEntity('sujet', $this->access, 'contenu');

        $this->assertInstanceOf(Email::class, $email);
        $this->assertEquals('sujet', $email->getAbout());
        $this->assertEquals('contenu', $email->getText());
        $this->assertEquals($this->access, $email->getAuthor());
        $this->assertTrue($email->getIsSystem());
    }

    /**
     * @see AbstractBuilder::render()
     */
    public function testRender(){
        $abstractBuilder = $this->makeAbstractBuilderMock('render');

        $context = [];
        $template = 'test';

        $this->twig
            ->expects(self::once())
            ->method('render')
            ->with(sprintf('@templates/emails/%s.html.twig', $template), $context);

        $abstractBuilder->render($template, $context);
    }

    /**
     * @see AbstractBuilder::addRecipient()
     */
    public function testAddRecipient(){
        $abstractBuilder = $this->makeAbstractBuilderMock('addRecipient');
        $sendType = EmailSendingTypeEnum::TO()->getValue();

        //Access Target
        $person = $this->getMockBuilder(Person::class)->getMock();
        $person->method('getFullName')->willReturn('Marc Durand');
        $this->access->method('getPerson')->willReturn($person);

        $email = new \App\Service\Mailer\Email();
        $abstractBuilder->addRecipient($email, $this->access, $sendType, ContactPointTypeEnum::PRINCIPAL()->getValue());

        /** @var EmailRecipient $emailRecipient */
        $emailRecipient = $email->getEmailRecipients()->first();
        $this->assertEquals($sendType, $emailRecipient->getSendType());
        $this->assertEquals($person->getFullName(), $emailRecipient->getName());
        $this->assertEquals($this->access, $emailRecipient->getAccess());

        //Organization Target
        $organization = $this->getMockBuilder(Organization::class)->getMock();
        $organization->method('getName')->willReturn('Test');

        $email = new \App\Service\Mailer\Email();
        $abstractBuilder->addRecipient($email, $organization, $sendType, ContactPointTypeEnum::PRINCIPAL()->getValue());

        /** @var EmailRecipient $emailRecipient */
        $emailRecipient = $email->getEmailRecipients()->first();
        $this->assertEquals($organization, $emailRecipient->getOrganization());
        $this->assertEquals($organization->getName(), $emailRecipient->getName());

        //Custom Target
        $custom = "foo.bar@opentalent.fr";
        $email = new \App\Service\Mailer\Email();
        $abstractBuilder->addRecipient($email, $custom, $sendType, ContactPointTypeEnum::PRINCIPAL()->getValue());

        /** @var EmailRecipient $emailRecipient */
        $emailRecipient = $email->getEmailRecipients()->first();
        $this->assertEquals($custom, $emailRecipient->getEmailAddress());
        $this->assertEquals(ReportMessageStatusEnum::DELIVERED()->getValue(), $emailRecipient->getSendStatus());
    }

    /**
     * @see AbstractBuilder::setMailToRecipient()
     */
    public function testSetMailToRecipientWithoutEmail(){
        $abstractBuilder = $this->makeAbstractBuilderMock('setMailToRecipient');
        $contactPoint = $this->getMockBuilder(ContactPoint::class)->getMock();
        $abstractBuilder->method('getFirstContactPointWithEmail')->willReturn(null);

        $emailRecipient = new EmailRecipient();
        $abstractBuilder->setMailToRecipient([$contactPoint], $emailRecipient);

        $this->assertEquals(ReportMessageStatusEnum::MISSING()->getValue(), $emailRecipient->getSendStatus());
    }

    /**
     * @see AbstractBuilder::setMailToRecipient()
     */
    public function testSetMailToRecipientWithEmail(){
        $abstractBuilder = $this->makeAbstractBuilderMock('setMailToRecipient');
        $contactPoint = $this->getMockBuilder(ContactPoint::class)->getMock();

        $emailRecipient = new EmailRecipient();
        $contactPoint->method('getEmail')->willReturn('foo.bar@opentalent.fr');
        $abstractBuilder->method('getFirstContactPointWithEmail')->willReturn($contactPoint);
        $abstractBuilder->setMailToRecipient([$contactPoint], $emailRecipient);

        $this->assertEquals(ReportMessageStatusEnum::DELIVERED()->getValue(), $emailRecipient->getSendStatus());
        $this->assertEquals('foo.bar@opentalent.fr', $emailRecipient->getEmailAddress());
    }

    /**
     * @see AbstractBuilder::getFirstContactPointWithEmail()
     */
    public function testGetFirstContactPointWithEmail(){
        $abstractBuilder = $this->makeAbstractBuilderMock('getFirstContactPointWithEmail');

        $contactPoint = $this->getMockBuilder(ContactPoint::class)->getMock();
        $contactPoint->method('getEmail')->willReturn('foo.bar@opentalent.fr');

        $abstractBuilder->getFirstContactPointWithEmail([$contactPoint]);

        $this->assertEquals('foo.bar@opentalent.fr', $contactPoint->getEmail());

        $contactPoint2 = $this->getMockBuilder(ContactPoint::class)->getMock();
        $contactPoint->method('getEmail')->willReturn(null);
        $contactPoint2->method('getEmail')->willReturn('foo.bar@opentalent.fr');

        $abstractBuilder->getFirstContactPointWithEmail([$contactPoint, $contactPoint2]);

        $this->assertEquals('foo.bar@opentalent.fr', $contactPoint->getEmail());
    }
}
