<?php

declare(strict_types=1);

/*
 * This file is part of the VITICORE2 package.
 *
 * (c) CP Creation <web@cpcreation.fr>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace CpCreation\VitiCore\Bridge\Symfony\Bundle\Utils;

use CpCreation\VitiCore\Cart\Model\Cart;
use CpCreation\VitiCore\Cart\Model\CartItem;
use CpCreation\VitiCore\Cart\Model\Promo;
use CpCreation\VitiCore\Contact\Model\Contact;
use CpCreation\VitiCore\Contact\Repository\ContactRepository;
use CpCreation\VitiCore\Payment\Model\Payment;
use CpCreation\VitiCore\Product\Model\ProductGift;
use CpCreation\VitiCore\Product\Model\Product as CatalogProduct;
use CpCreation\VitiCore\Shop\Repository\ShopConfigRepository;
use CpCreation\VitiCore\User\Model\User;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Email;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use Twig\Environment;

class Mailer
{
    public function __construct(
        private Pdf $pdf,
        private PdfPuppeteer $pdfPuppeteer,
        private MailerInterface $mailer,
        private TranslatorInterface $translator,
        private ContactRepository $contactRepository,
        private ShopConfigRepository $shopConfigRepository,
        private RequestStack $requestStack,
        private Environment $twig,
        private UrlGeneratorInterface $urlGenerator,
        private string $emailToMaintenance,
    ) {
    }

    public function send(Email $message): void
    {
        $this->mailer->send($message);
    }

    /**
     * @throws \Twig\Error\LoaderError
     * @throws \Twig\Error\RuntimeError
     * @throws \Twig\Error\SyntaxError
     */
    public function sendAccountActivation(User $user): void
    {
        $locale = $this->resolveRequestLocale();
        $contact = $this->getContactOrNull();
        $company = $contact?->getCompany() ?: $this->translator->trans('security.mail.company_fallback', [], 'messages', $locale);
        $fromEmail = $contact?->getEmailFrom() ?: 'no-reply@localhost';
        $verificationUrl = $this->urlGenerator->generate('app_verify_account', [
            '_locale' => $locale,
            'token' => $user->getEmailVerificationToken(),
        ], UrlGeneratorInterface::ABSOLUTE_URL);

        $message = (new Email())
            ->subject($this->translator->trans('security.mail.activation.subject', [], 'messages', $locale))
            ->from(new Address($fromEmail, $company))
            ->sender($fromEmail)
            ->returnPath($fromEmail)
            ->to((string) $user->getEmail())
            ->html($this->twig->render('mail/account_verification.html.twig', [
                'contact' => $contact,
                'company' => $company,
                'user' => $user,
                'verification_url' => $verificationUrl,
                'expires_at' => $user->getEmailVerificationExpiresAt(),
                'locale' => $locale,
            ]))
            ->text($this->twig->render('mail/account_verification.txt.twig', [
                'contact' => $contact,
                'company' => $company,
                'user' => $user,
                'verification_url' => $verificationUrl,
                'expires_at' => $user->getEmailVerificationExpiresAt(),
                'locale' => $locale,
            ]));

        $this->mailer->send($message);
    }

    /**
     * @throws \Twig\Error\LoaderError
     * @throws \Twig\Error\RuntimeError
     * @throws \Twig\Error\SyntaxError
     */
    public function sendPasswordReset(User $user): void
    {
        $locale = $this->resolveRequestLocale();
        $contact = $this->getContactOrNull();
        $company = $contact?->getCompany() ?: $this->translator->trans('security.mail.company_fallback', [], 'messages', $locale);
        $fromEmail = $contact?->getEmailFrom() ?: 'no-reply@localhost';
        $resetUrl = $this->urlGenerator->generate('app_reset_password', [
            '_locale' => $locale,
            'token' => $user->getResetPasswordToken(),
        ], UrlGeneratorInterface::ABSOLUTE_URL);

        $message = (new Email())
            ->subject($this->translator->trans('security.mail.reset.subject', [], 'messages', $locale))
            ->from(new Address($fromEmail, $company))
            ->sender($fromEmail)
            ->returnPath($fromEmail)
            ->to((string) $user->getEmail())
            ->html($this->twig->render('mail/reset_password.html.twig', [
                'contact' => $contact,
                'company' => $company,
                'user' => $user,
                'reset_url' => $resetUrl,
                'expires_at' => $user->getResetPasswordExpiresAt(),
                'locale' => $locale,
            ]))
            ->text($this->twig->render('mail/reset_password.txt.twig', [
                'contact' => $contact,
                'company' => $company,
                'user' => $user,
                'reset_url' => $resetUrl,
                'expires_at' => $user->getResetPasswordExpiresAt(),
                'locale' => $locale,
            ]));

        $this->mailer->send($message);
    }

    /**
     * @param array{
     *     lastname?: string,
     *     firstname?: string,
     *     object?: string,
     *     email?: string,
     *     phone?: string,
     *     message?: string
     * } $data
     *
     * @throws \Twig\Error\LoaderError
     * @throws \Twig\Error\RuntimeError
     * @throws \Twig\Error\SyntaxError
     */
    public function sendContact(array $data): void
    {
        $contact = $this->getContact();
        $fullname = mb_trim(\sprintf(
            '%s %s',
            (string) ($data['firstname'] ?? ''),
            (string) ($data['lastname'] ?? '')
        ));
        $subject = mb_trim((string) ($data['object'] ?? ''));

        if ('' === $subject) {
            $subject = 'Prise de contact depuis le site '.$contact->getCompany();
        } else {
            $subject = '['.$contact->getCompany().'] '.$subject;
        }

        $message = (new Email())
            ->subject($subject)
            ->from(new Address($contact->getEmailFrom(), $contact->getCompany()))
            ->sender($contact->getEmailFrom())
            ->returnPath($contact->getEmailFrom())
            ->to(new Address($contact->getEmailToMessage(), $contact->getCompany()))
            ->replyTo(new Address(
                (string) ($data['email'] ?? $contact->getEmailFrom()),
                '' !== $fullname ? $fullname : $contact->getCompany()
            ))
            ->html($this->twig->render('mail/contact_admin.html.twig', [
                'contact' => $contact,
                'company' => $contact->getCompany(),
                'data' => $data,
            ]))
            ->text($this->twig->render('mail/contact_admin.txt.twig', [
                'contact' => $contact,
                'company' => $contact->getCompany(),
                'data' => $data,
            ]));

        $this->mailer->send($message);
    }

    /**
     * @throws \Twig\Error\LoaderError
     * @throws \Twig\Error\RuntimeError
     * @throws \Twig\Error\SyntaxError
     */
    public function sendOrder(Contact $contact, Cart $cart): void
    {
        if ($cart->isCheckoutPayed()) {
            $this->sendPaidOrder($contact, $cart);

            return;
        }

        $this->sendOfflineOrder($contact, $cart);
    }

    /**
     * @throws \Twig\Error\LoaderError
     * @throws \Twig\Error\RuntimeError
     * @throws \Twig\Error\SyntaxError
     */
    public function sendOfflineOrder(Contact $contact, Cart $cart): void
    {
        $this->mailer->send($this->createOrderCustomerMessage($contact, $cart, 'order.offline_mail', false));
        $this->mailer->send($this->createOrderAdminMessage($contact, $cart));
    }

    /**
     * @throws \Twig\Error\LoaderError
     * @throws \Twig\Error\RuntimeError
     * @throws \Twig\Error\SyntaxError
     */
    public function sendPaidOrder(Contact $contact, Cart $cart): void
    {
        $this->mailer->send($this->createOrderCustomerMessage($contact, $cart, 'order.paid_mail', true));
        $this->mailer->send($this->createOrderAdminMessage($contact, $cart));
    }

    /**
     * @throws \Twig\Error\LoaderError
     * @throws \Twig\Error\RuntimeError
     * @throws \Twig\Error\SyntaxError
     */
    public function sendOrderCustomer(Contact $contact, Cart $cart): void
    {
        if ($cart->isCheckoutPayed()) {
            $this->sendPaidOrderCustomer($contact, $cart);

            return;
        }

        $this->sendOfflineOrderCustomer($contact, $cart);
    }

    /**
     * @throws \Twig\Error\LoaderError
     * @throws \Twig\Error\RuntimeError
     * @throws \Twig\Error\SyntaxError
     */
    public function sendOfflineOrderCustomer(Contact $contact, Cart $cart): void
    {
        $this->mailer->send($this->createOrderCustomerMessage($contact, $cart, 'order.offline_mail', false));
    }

    /**
     * @throws \Twig\Error\LoaderError
     * @throws \Twig\Error\RuntimeError
     * @throws \Twig\Error\SyntaxError
     */
    public function sendPaidOrderCustomer(Contact $contact, Cart $cart): void
    {
        $this->mailer->send($this->createOrderCustomerMessage($contact, $cart, 'order.paid_mail', true));
    }

    /**
     * @throws \Twig\Error\LoaderError
     * @throws \Twig\Error\RuntimeError
     * @throws \Twig\Error\SyntaxError
     */
    public function sendDelivery(Contact $contact, Cart $cart): void
    {
        $locale = $this->resolveCartLocale($cart);
        $reference = $cart->getToken() ?: $cart->getId()->toString();
        $recipients = $this->resolveDeliveryNotificationRecipients($cart);

        $messageCustomer = (new Email())
            ->subject($this->translator->trans(
                'delivery.mail.subject_mail',
                [
                    '%company%' => $contact->getCompany(),
                    '%ref%' => $reference,
                ],
                null,
                $locale
            ))
            ->from(new Address($contact->getEmailFrom(), $contact->getCompany()))
            ->sender($contact->getEmailFrom())
            ->returnPath($contact->getEmailFrom())
            ->html($this->twig->render(
                'mail/delivery_customer.html.twig',
                [
                    'contact' => $contact,
                    'cart' => $cart,
                    'company' => $contact->getCompany(),
                    'locale' => $locale,
                    'reference' => $reference,
                ]
            ))
            ->text($this->twig->render(
                'mail/delivery_customer.txt.twig',
                [
                    'contact' => $contact,
                    'cart' => $cart,
                    'company' => $contact->getCompany(),
                    'locale' => $locale,
                    'reference' => $reference,
                ]
            ));

        $this->applyCustomerRecipients($messageCustomer, $recipients);

        $this->mailer->send($messageCustomer);
    }

    public function sendPayment(Payment $payment): void
    {
        $contact = $this->getContact();

        $html = 'Nom : '.$payment->getLastname().'<br />'
            .'Prénom : '.$payment->getFirstname().'<br />'
            .'Adresse : '.$payment->getStreet().'<br />'
            .'Code postal : '.$payment->getPostalCode().'<br />'
            .'Ville : '.$payment->getCity().'<br />'
            .'Email : <a href="mailto:'.$payment->getEmail().'">'.$payment->getEmail().'</a><br />'
            .'Téléphone : '.$payment->getPhone().'<br />'
            .'Référence Commande : '.$payment->getSlug().'<br />'
            .'Montant : '.number_format($payment->getAmount() / 100, 2).' € TTC <br />';

        $text = 'Nom : '.$payment->getLastname()
            .' Prénom : '.$payment->getFirstname()
            .' Adresse : '.$payment->getStreet()
            .' Code postal : '.$payment->getPostalCode()
            .' Ville : '.$payment->getCity()
            .' Email : '.$payment->getEmail()
            .' Téléphone : '.$payment->getPhone()
            .' Référence Commande : '.$payment->getSlug()
            .' Montant : '.number_format($payment->getAmount() / 100, 2).' € TTC';

        $message = (new Email())
            ->subject('Paiement en ligne sur le site '.$contact->getCompany().' - Référence '.$payment->getReference())
            ->from(new Address($contact->getEmailFrom(), $contact->getCompany()))
            ->sender($contact->getEmailFrom())
            ->returnPath($contact->getEmailFrom())
            ->to(new Address($contact->getEmailToShop(), $contact->getCompany()))
            ->cc(new Address($payment->getEmail(), $payment->getLastname().' '.$payment->getFirstname()))
            ->html($html)
            ->text($text);

        $this->mailer->send($message);
    }

    /**
     * @throws \Twig\Error\LoaderError
     * @throws \Twig\Error\RuntimeError
     * @throws \Twig\Error\SyntaxError
     */
    public function sendGiftCard(Contact $contact, Cart $cart): void
    {
        $locale = $this->resolveCartLocale($cart);
        $messageCustomer = (new Email())
            ->subject($this->translator->trans(
                'gift.mail.subject_mail',
                [],
                null,
                $locale
            ))
            ->from(new Address($contact->getEmailFrom(), $contact->getCompany()))
            ->to((string) $cart->getEmail())
            ->html($this->twig->render('mail/gift.html.twig', [
                'cart' => $cart,
                'locale' => $locale,
            ]))
            ->text($this->twig->render('mail/gift.txt.twig', [
                'cart' => $cart,
                'locale' => $locale,
            ]));

        /** @var CartItem $cartItem */
        foreach ($cart->getItems() as $cartItem) {
            /** @var ProductGift $gift */
            foreach ($cartItem->getGifts() as $gift) {
                $path = $this->pdfPuppeteer->createGiftCard($gift, $contact);
                $messageCustomer->attachFromPath('upload/gift/'.$path, $path, 'application/pdf');
            }
        }

        $this->mailer->send($messageCustomer);
    }

    /**
     * @throws \Twig\Error\LoaderError
     * @throws \Twig\Error\RuntimeError
     * @throws \Twig\Error\SyntaxError
     */
    public function sendPromoAssignment(Promo $promo): void
    {
        $recipientEmail = $promo->getRecipientEmail();

        if (null === $recipientEmail || '' === mb_trim($recipientEmail)) {
            return;
        }

        $locale = $this->resolveRequestLocale();
        $contact = $this->getContactOrNull();
        $company = $contact?->getCompany() ?: $this->translator->trans('security.mail.company_fallback', [], 'messages', $locale);
        $fromEmail = $contact?->getEmailFrom() ?: 'no-reply@localhost';

        $context = [
            'contact' => $contact,
            'company' => $company,
            'promo' => $promo,
            'benefit_label' => $this->formatPromoBenefit($promo, $locale),
            'validity_label' => $this->formatPromoValidity($promo, $locale),
            'condition_lines' => $this->buildPromoConditionLines($promo, $locale),
            'locale' => $locale,
        ];

        $message = (new Email())
            ->subject($this->translator->trans('promo.mail.subject', [
                '%code%' => (string) $promo->getTitle(),
            ], 'messages', $locale))
            ->from(new Address($fromEmail, $company))
            ->sender($fromEmail)
            ->returnPath($fromEmail)
            ->to($recipientEmail)
            ->html($this->twig->render('mail/promo_assignment.html.twig', $context))
            ->text($this->twig->render('mail/promo_assignment.txt.twig', $context));

        $this->mailer->send($message);
    }

    private function resolveRequestLocale(): string
    {
        $request = $this->requestStack->getCurrentRequest() ?? $this->requestStack->getMainRequest();

        return $request?->getLocale() ?: 'fr';
    }

    private function resolveCartLocale(Cart $cart): string
    {
        if ('' !== $cart->getLocale()) {
            return $cart->getLocale();
        }

        $request = $this->requestStack->getCurrentRequest() ?? $this->requestStack->getMainRequest();

        return $request?->getLocale() ?: 'fr';
    }

    /**
     * @throws \Twig\Error\LoaderError
     * @throws \Twig\Error\RuntimeError
     * @throws \Twig\Error\SyntaxError
     */
    private function createOrderCustomerMessage(Contact $contact, Cart $cart, string $mailTranslationPrefix, bool $isPaid): Email
    {
        $locale = $this->resolveCartLocale($cart);
        $reference = $cart->getToken() ?: $cart->getId()->toString();
        $documentLabelKey = $cart->getDocumentLabelKey();
        $documentLabel = $this->translator->trans($documentLabelKey, [], 'messages', $locale);
        $documentLabelLower = mb_strtolower($documentLabel);
        $attachmentNotice = $this->translator->trans($mailTranslationPrefix.'.attachment_notice', [
            '%document%' => $documentLabelLower,
        ], 'messages', $locale);
        $path = $this->pdf->createPdfCartDocument($contact, $cart, $locale);

        $message = (new Email())
            ->subject($this->resolveOrderCustomerSubject($contact, $documentLabelKey, $documentLabelLower, $cart->getToken() ?: $cart->getId()->toString(), $locale, $mailTranslationPrefix))
            ->from(new Address($contact->getEmailFrom(), $contact->getCompany()))
            ->sender($contact->getEmailFrom())
            ->returnPath($contact->getEmailFrom())
            ->attachFromPath('order/'.$path, $path, 'application/pdf')
            ->html($this->twig->render('mail/order_customer.html.twig', [
                'contact' => $contact,
                'cart' => $cart,
                'company' => $contact->getCompany(),
                'reference' => $reference,
                'locale' => $locale,
                'document_label' => $documentLabel,
                'document_label_lower' => $documentLabelLower,
                'attachment_notice' => $attachmentNotice,
                'mail_translation_prefix' => $mailTranslationPrefix,
                'is_paid' => $isPaid,
            ]))
            ->text($this->twig->render('mail/order_customer.txt.twig', [
                'contact' => $contact,
                'cart' => $cart,
                'company' => $contact->getCompany(),
                'reference' => $reference,
                'locale' => $locale,
                'document_label' => $documentLabel,
                'document_label_lower' => $documentLabelLower,
                'attachment_notice' => $attachmentNotice,
                'mail_translation_prefix' => $mailTranslationPrefix,
                'is_paid' => $isPaid,
            ]));

        $this->applyCustomerRecipients($message, $this->resolveBillingDocumentRecipients($cart));

        return $message;
    }

    private function resolveOrderCustomerSubject(Contact $contact, string $documentLabelKey, string $documentLabelLower, string $reference, string $locale, string $mailTranslationPrefix): string
    {
        if ('checkout_design.document_invoice' === $documentLabelKey) {
            return sprintf(
                'Votre facture %s de %s',
                mb_strtoupper($reference),
                $contact->getCompany()
            );
        }

        return $this->translator->trans($mailTranslationPrefix.'.subject_mail', [
            '%company%' => $contact->getCompany(),
            '%document%' => $documentLabelLower,
        ], 'messages', $locale);
    }

    /**
     * @throws \Twig\Error\LoaderError
     * @throws \Twig\Error\RuntimeError
     * @throws \Twig\Error\SyntaxError
     */
    private function createOrderAdminMessage(Contact $contact, Cart $cart): Email
    {
        $reference = $cart->getToken() ?: $cart->getId()->toString();
        $documentLabelKey = $cart->getDocumentLabelKey();
        $adminDocumentLabel = $this->translator->trans($documentLabelKey, [], 'messages', 'fr');
        $adminDocumentLabelLower = mb_strtolower($adminDocumentLabel);
        $attachmentNotice = $this->translator->trans('order.admin_mail.attachment_notice', [
            '%document%' => $adminDocumentLabelLower,
        ], 'messages', 'fr');
        $path = $this->pdf->createPdfCartDocumentAdmin($contact, $cart, 'fr');

        return (new Email())
            ->subject($this->resolveOrderAdminSubject($cart, $documentLabelKey, $adminDocumentLabelLower, $reference))
            ->from(new Address($contact->getEmailFrom(), $contact->getCompany()))
            ->sender($contact->getEmailFrom())
            ->returnPath($contact->getEmailFrom())
            ->to($contact->getEmailToShop())
            ->attachFromPath('order/'.$path, $path, 'application/pdf')
            ->html($this->twig->render('mail/order_admin.html.twig', [
                'contact' => $contact,
                'cart' => $cart,
                'company' => $contact->getCompany(),
                'reference' => $reference,
                'locale' => 'fr',
                'document_label' => $adminDocumentLabel,
                'document_label_lower' => $adminDocumentLabelLower,
                'attachment_notice' => $attachmentNotice,
            ]))
            ->text($this->twig->render('mail/order_admin.txt.twig', [
                'contact' => $contact,
                'cart' => $cart,
                'company' => $contact->getCompany(),
                'reference' => $reference,
                'locale' => 'fr',
                'document_label' => $adminDocumentLabel,
                'document_label_lower' => $adminDocumentLabelLower,
                'attachment_notice' => $attachmentNotice,
            ]));
    }

    private function resolveOrderAdminSubject(Cart $cart, string $documentLabelKey, string $documentLabelLower, string $reference): string
    {
        $firstname = mb_strtoupper(mb_trim((string) $cart->getFirstname()));
        $lastname = mb_strtoupper(mb_trim((string) $cart->getLastname()));
        $fullname = mb_trim($firstname.' '.$lastname);
        $referenceUpper = mb_strtoupper($reference);

        if ('checkout_design.document_invoice' === $documentLabelKey) {
            return sprintf(
                'Nouvelle facture avec la ref %s de %s',
                $referenceUpper,
                '' !== $fullname ? $fullname : 'CLIENT NON RENSEIGNE'
            );
        }

        return $this->translator->trans('order.admin_mail.subject_mail', [
            '%document%' => $documentLabelLower,
            '%firstname%' => (string) $cart->getFirstname(),
            '%lastname%' => (string) $cart->getLastname(),
        ], 'messages', 'fr');
    }

    private function getContactOrNull(): ?Contact
    {
        /** @var Contact|null $contact */
        $contact = $this->contactRepository->findOne();

        return $contact;
    }

    /**
     * @throws \Doctrine\ORM\NonUniqueResultException
     */
    public function sendErrorConnexionApiConnectSocialInstagram(string $exception): void
    {
        $contact = $this->getContact();

        $this->sendMaintenanceError(
            'Erreur Connexion à l\'api Connect Social CPCREATION '.$contact->getCompany(),
            $exception.' '.$contact->getCompany(),
            $contact
        );
    }

    /**
     * @throws \Doctrine\ORM\NonUniqueResultException
     */
    public function sendErrorInstagram(string $exception): void
    {
        $contact = $this->getContact();

        $this->sendMaintenanceError(
            'Erreur connexion à l\'api display Instagram '.$contact->getCompany(),
            $exception.' '.$contact->getCompany(),
            $contact
        );
    }

    /**
     * @throws \Doctrine\ORM\NonUniqueResultException
     */
    public function sendErrorFacebookInstagram(string $exception): void
    {
        $contact = $this->getContact();

        $this->sendMaintenanceError(
            'Erreur connexion à l\'api Facebook pour Instagram '.$contact->getCompany(),
            $exception.' '.$contact->getCompany(),
            $contact
        );
    }

    /**
     * @throws \Doctrine\ORM\NonUniqueResultException
     */
    public function sendErrorFacebookFacebook(string $exception): void
    {
        $contact = $this->getContact();

        $this->sendMaintenanceError(
            'Erreur connexion à l\'api Facebook pour Facebook '.$contact->getCompany(),
            $exception.' '.$contact->getCompany(),
            $contact
        );
    }

    /**
     * @throws \Doctrine\ORM\NonUniqueResultException
     */
    public function sendErrorException(string $exception): void
    {
        $contact = $this->getContact();

        $this->sendMaintenanceError(
            'Erreur '.$contact->getCompany(),
            $exception.' '.$contact->getCompany(),
            $contact
        );
    }

    /**
     * @throws \Doctrine\ORM\NonUniqueResultException
     */
    public function sendErrorValidReturnPayment(string $response): void
    {
        $contact = $this->getContact();

        $this->sendMaintenanceError(
            'Erreur Validation Paiement - retour de banque '.$contact->getCompany(),
            $response,
            $contact
        );
    }

    /**
     * @throws \Doctrine\ORM\NonUniqueResultException
     */
    public function sendErrorIpLocate(string $exception, string $ip): void
    {
        $contact = $this->getContact();

        $this->sendMaintenanceError(
            'Erreur location de l\'adresse IP '.$ip.' '.$contact->getCompany(),
            $exception.' '.$contact->getCompany(),
            $contact
        );
    }

    /**
     * @throws \Doctrine\ORM\NonUniqueResultException
     */
    private function getContact(): Contact
    {
        /** @var Contact|null $contact */
        $contact = $this->contactRepository->findOne();

        if (null === $contact) {
            throw new \RuntimeException('No contact configuration found.');
        }

        return $contact;
    }

    private function sendMaintenanceError(string $subject, string $body, Contact $contact): void
    {
        $message = (new Email())
            ->subject($subject)
            ->from(new Address($contact->getEmailFrom(), $contact->getCompany()))
            ->to($this->emailToMaintenance)
            ->html($body)
            ->text($body);

        $this->mailer->send($message);
    }

    private function formatPromoBenefit(Promo $promo, string $locale): string
    {
        if (null !== $promo->getPercent()) {
            $label = number_format($promo->getPercent() * 100, 2, ',', ' ').' %';

            if ($promo->isIncludeShippingFees()) {
                return $this->translator->trans('promo.mail.benefit_percent_with_shipping', [
                    '%amount%' => $label,
                ], 'messages', $locale);
            }

            return $this->translator->trans('promo.mail.benefit_percent', [
                '%amount%' => $label,
            ], 'messages', $locale);
        }

        if (null !== $promo->getAmountTTC()) {
            return $this->translator->trans('promo.mail.benefit_amount', [
                '%amount%' => number_format($promo->getAmountTTC() / 100, 2, ',', ' ').' €',
            ], 'messages', $locale);
        }

        if (null !== $promo->getPercentShippingCost()) {
            return $this->translator->trans('promo.mail.benefit_shipping', [
                '%amount%' => number_format($promo->getPercentShippingCost() * 100, 2, ',', ' ').' %',
            ], 'messages', $locale);
        }

        if (null !== $promo->getOfferedVariant()) {
            return $this->translator->trans('promo.mail.benefit_gift', [
                '%product%' => $promo->getOfferedVariant()->getFullName(),
            ], 'messages', $locale);
        }

        return $this->translator->trans('promo.mail.benefit_default', [
            '%code%' => (string) $promo->getTitle(),
        ], 'messages', $locale);
    }

    private function formatPromoValidity(Promo $promo, string $locale): string
    {
        $beginAt = $promo->getBeginAt();
        $endAt = $promo->getEndAt();

        if (null !== $beginAt && null !== $endAt) {
            return $this->translator->trans('promo.mail.validity_range', [
                '%begin%' => $beginAt->format('d/m/Y H:i'),
                '%end%' => $endAt->format('d/m/Y H:i'),
            ], 'messages', $locale);
        }

        if (null !== $beginAt) {
            return $this->translator->trans('promo.mail.validity_from', [
                '%begin%' => $beginAt->format('d/m/Y H:i'),
            ], 'messages', $locale);
        }

        if (null !== $endAt) {
            return $this->translator->trans('promo.mail.validity_until', [
                '%end%' => $endAt->format('d/m/Y H:i'),
            ], 'messages', $locale);
        }

        return $this->translator->trans('promo.mail.validity_unlimited', [], 'messages', $locale);
    }

    /**
     * @return list<string>
     */
    private function buildPromoConditionLines(Promo $promo, string $locale): array
    {
        $lines = [];

        if (null !== $promo->getMinEquivalency() && $promo->getMinEquivalency() > 0) {
            $lines[] = $this->translator->trans('promo.mail.condition_min_quantity', [
                '%quantity%' => $promo->getMinEquivalency(),
            ], 'messages', $locale);
        }

        if ($promo->getProducts()->count() > 0) {
            $productNames = [];

            foreach ($promo->getProducts() as $product) {
                if ($product instanceof CatalogProduct) {
                    $productNames[] = $product->getFullName();
                }
            }

            if ([] !== $productNames) {
                $lines[] = $this->translator->trans('promo.mail.condition_products', [
                    '%products%' => implode(', ', $productNames),
                ], 'messages', $locale);
            }
        }

        return $lines;
    }

    /**
     * @param list<string> $recipients
     */
    private function applyCustomerRecipients(Email $message, array $recipients): void
    {
        $primaryRecipient = array_shift($recipients);

        if (null === $primaryRecipient) {
            return;
        }

        $message->to($primaryRecipient);

        if ([] !== $recipients) {
            $message->cc(...$recipients);
        }
    }

    /**
     * @return list<string>
     */
    private function resolveBillingDocumentRecipients(Cart $cart): array
    {
        $invoiceEmail = $this->normalizeRecipients([$cart->getInvoiceAddress()?->getEmail()]);

        if ([] !== $invoiceEmail) {
            return $invoiceEmail;
        }

        $deliveryEmail = $this->normalizeRecipients([$cart->getDeliveryAddress()?->getEmail()]);

        if ([] !== $deliveryEmail) {
            return $deliveryEmail;
        }

        return $this->normalizeRecipients([$cart->getUser()?->getEmail()]);
    }

    /**
     * @return list<string>
     */
    private function resolveDeliveryNotificationRecipients(Cart $cart): array
    {
        $recipients = $this->normalizeRecipients([
            $cart->getDeliveryAddress()?->getEmail(),
            $cart->getInvoiceAddress()?->getEmail(),
        ]);

        if ([] !== $recipients) {
            return $recipients;
        }

        return $this->normalizeRecipients([$cart->getUser()?->getEmail()]);
    }

    /**
     * @param list<?string> $emails
     *
     * @return list<string>
     */
    private function normalizeRecipients(array $emails): array
    {
        $recipients = [];

        foreach ($emails as $email) {
            $normalizedEmail = null !== $email ? mb_strtolower(mb_trim($email)) : null;

            if (null === $normalizedEmail || '' === $normalizedEmail || \in_array($normalizedEmail, $recipients, true)) {
                continue;
            }

            $recipients[] = $normalizedEmail;
        }

        return $recipients;
    }
}
