<?php
declare(strict_types=1);

namespace App\Service\Mailer\Builder;

use App\Entity\Access\Access;
use App\Entity\Core\ContactPoint;
use App\Entity\Organization\Organization;
use App\Enum\Message\ReportMessageStatusEnum;
use App\Repository\Core\ContactPointRepository;
use App\Service\Mailer\Email;
use App\Entity\Message\Email as EmailEntity;
use App\Service\Mailer\EmailRecipient;
use App\Tests\Service\Mailer\Builder\AbstractBuilderTest;
use Symfony\Contracts\Service\Attribute\Required;
use Twig\Environment;

/**
 * Classe AbstractBuilder qui définit les fonctions de bases de chaque builder
 */
class AbstractBuilder implements AbstractBuilderInterface
{
    protected Environment $twig;
    protected ContactPointRepository $contactPointRepository;

    #[Required]
    public function setTwig(Environment $twig): void
    { $this->twig = $twig; }

    #[Required]
    public function setContactPointRepository(ContactPointRepository $contactPointRepository): void
    { $this->contactPointRepository = $contactPointRepository; }


    /**
     * @param string $subject
     * @param Access $author
     * @param string $content
     * @return EmailEntity
     * @see AbstractBuilderTest::testBuildEmailEntity()
     */
    public function buildEmailEntity(string $subject, Access $author, string $content): EmailEntity
    {
        $emailEntity = new EmailEntity();
        $emailEntity
            ->setAuthor($author)
            ->setIsSystem(true)
            ->setAbout($subject)
            ->setDateSent(new \DateTime('now'))
            ->setText($content)
            ;
        return $emailEntity;
    }

    /**
     * @param string $template
     * @param mixed[] $context
     * @return string
     *
     * @see AbstractBuilderTest::testRender()
     */
    public function render(string $template, array $context): string
    {
        return $this->twig->render(sprintf('@templates/emails/%s.html.twig', $template), $context);
    }

    /**
     * @param Email $email
     * @param Access|Organization|string $target
     * @param string $sendType
     * @param string|null $contactPointType
     *
     * @see AbstractBuilderTest::testAddRecipient()
     */
    public function addRecipient(Email $email, Access|Organization|string $target, string $sendType, string $contactPointType = null): void{
        $emailRecipient = (new EmailRecipient())
            ->setSendType($sendType);

        if($target instanceof Access){
            $emailRecipient->setAccess($target);
            $emailRecipient->setName($target->getPerson()->getFullName() ?? $target->getOrganization()->getName());
            $this->setMailToRecipient($this->contactPointRepository->getByTypeAndPerson($contactPointType, $target->getPerson()), $emailRecipient);
        }else if ($target instanceof Organization){
            $emailRecipient->setOrganization($target);
            $emailRecipient->setName($target->getName());
            $this->setMailToRecipient($this->contactPointRepository->getByTypeAndOrganization($contactPointType, $target), $emailRecipient);
        }else{
            $emailRecipient->setSendStatus(ReportMessageStatusEnum::DELIVERED()->getValue());
            $emailRecipient->setEmailAddress($target);
        }

        $email->addEmailRecipient($emailRecipient);
    }

    /**
     * @param list<ContactPoint> $contactPoints
     * @param EmailRecipient $recipient
     *
     * @see AbstractBuilderTest::testSetMailToRecipient()
     */
    public function setMailToRecipient(array $contactPoints, EmailRecipient $recipient): void {
        if($contactPoint = $this->getFirstContactPointWithEmail($contactPoints)){
            $recipient->setSendStatus(ReportMessageStatusEnum::DELIVERED()->getValue());
            $recipient->setEmailAddress($contactPoint->getEmail());
        }else{
            $recipient->setSendStatus(ReportMessageStatusEnum::MISSING()->getValue());
        }
    }

    /**
     * @param array<ContactPoint> $contactPoints
     * @return ContactPoint|null
     *
     * @see AbstractBuilderTest::testGetFirstContactPointWithEmail()
     */
    public function getFirstContactPointWithEmail(array $contactPoints): ?ContactPoint
    {
        $find = false;
        $cmpt = 0;
        $contactPoint = null;

        while(count($contactPoints) > $cmpt && !$find){
            if($contactPoints[$cmpt]->getEmail()){
                $find = true;
                $contactPoint = $contactPoints[$cmpt];
            }
            $cmpt++;
        }
        return $contactPoint;
    }
}