<?php

namespace Mautic\EmailBundle\Controller;

use Mautic\CoreBundle\Controller\FormController as CommonFormController;
use Mautic\CoreBundle\Helper\ThemeHelper;
use Mautic\CoreBundle\Helper\TrackingPixelHelper;
use Mautic\CoreBundle\Twig\Helper\AnalyticsHelper;
use Mautic\CoreBundle\Twig\Helper\AssetsHelper;
use Mautic\EmailBundle\EmailEvents;
use Mautic\EmailBundle\Entity\Stat;
use Mautic\EmailBundle\Event\EmailSendEvent;
use Mautic\EmailBundle\Event\TransportWebhookEvent;
use Mautic\EmailBundle\Helper\EmailConfig;
use Mautic\EmailBundle\Helper\MailHashHelper;
use Mautic\EmailBundle\Helper\MailHelper;
use Mautic\EmailBundle\Model\EmailModel;
use Mautic\FormBundle\Model\FormModel;
use Mautic\LeadBundle\Controller\FrequencyRuleTrait;
use Mautic\LeadBundle\Entity\DoNotContact;
use Mautic\LeadBundle\Entity\Lead;
use Mautic\LeadBundle\Helper\FakeContactHelper;
use Mautic\LeadBundle\Model\LeadModel;
use Mautic\LeadBundle\Tracker\ContactTracker;
use Mautic\MessengerBundle\Message\EmailHitNotification;
use Mautic\PageBundle\Entity\Page;
use Mautic\PageBundle\Event\PageDisplayEvent;
use Mautic\PageBundle\EventListener\BuilderSubscriber;
use Mautic\PageBundle\Model\PageModel;
use Mautic\PageBundle\PageEvents;
use Mautic\PluginBundle\Helper\IntegrationHelper;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Contracts\Translation\LocaleAwareInterface;
use Symfony\Contracts\Translation\TranslatorInterface;

class PublicController extends CommonFormController
{
    use FrequencyRuleTrait;

    /**
     * @return Response
     */
    public function indexAction(Request $request, AnalyticsHelper $analyticsHelper, $idHash)
    {
        /** @var EmailModel $model */
        $model = $this->getModel('email');
        $stat  = $model->getEmailStatus($idHash);

        if (!empty($stat)) {
            if ($this->security->isAnonymous()) {
                $model->hitEmail($stat, $request, true);
            }

            $tokens = $stat->getTokens();
            if (is_array($tokens)) {
                // Override tracking_pixel so as to not cause a double hit
                $tokens['{tracking_pixel}'] = MailHelper::getBlankPixel();
            }

            if ($copy = $stat->getStoredCopy()) {
                $subject = $copy->getSubject();
                $content = $copy->getBody();

                // Replace tokens
                if (is_array($tokens)) {
                    $content = str_ireplace(array_keys($tokens), $tokens, $content);
                    $subject = str_ireplace(array_keys($tokens), $tokens, $subject);
                }
            } else {
                $subject = '';
                $content = '';
            }

            $content = $analyticsHelper->addCode($content);

            // Add subject as title
            if (!empty($subject)) {
                if (str_contains($content, '<title></title>')) {
                    $content = str_replace('<title></title>', "<title>$subject</title>", $content);
                } elseif (!str_contains($content, '<title>')) {
                    $content = str_replace('<head>', "<head>\n<title>$subject</title>", $content);
                }
            }

            return new Response($content);
        }

        return $this->notFound();
    }

    public function trackingImageAction(
        Request $request,
        MessageBusInterface $messageBus,
        LoggerInterface $logger,
        string $idHash,
    ): Response {
        try {
            $messageBus->dispatch(new EmailHitNotification($idHash, $request));
        } catch (\Exception $exception) {
            $logger->error($exception->getMessage(), ['idHash' => $idHash]);
            $emailModel = $this->getModel('email');
            assert($emailModel instanceof EmailModel);

            $emailModel->hitEmail($idHash, $request);
        }

        return TrackingPixelHelper::getResponse($request);
    }

    /**
     * @return Response
     *
     * @throws \Exception
     * @throws \Mautic\CoreBundle\Exception\FileNotFoundException
     */
    public function unsubscribeAction(Request $request, ContactTracker $contactTracker, EmailModel $model, LeadModel $leadModel, FormModel $formModel, PageModel $pageModel, MailHashHelper $mailHash, ThemeHelper $themeHelper, $idHash, ?string $urlEmail = null, ?string $secretHash = null)
    {
        $stat                   = $model->getEmailStatus($idHash);
        $message                = '';
        $email                  = null;
        $lead                   = null;
        $template               = null;
        $session                = $request->getSession();
        $isOneClickUnsubscribe  = $request->isMethod(Request::METHOD_POST) && 'One-Click' === $request->get('List-Unsubscribe');
        $isUnsubscribeAll       = $request->get('unsubscribe_all');
        $showContactPreferences = $this->coreParametersHelper->get('show_contact_preferences');

        if (!empty($stat)) {
            if ($isOneClickUnsubscribe) {
                // RFC 8058 One-Click unsubscribe
                $unsubscribeComment = $this->translator->trans('mautic.email.dnc.unsubscribed');
                $model->setDoNotContact($stat, $unsubscribeComment, DoNotContact::UNSUBSCRIBED);

                return new Response($this->translator->trans('mautic.lead.do.not.contact_unsubscribed'));
            }

            $email = $stat->getEmail();
        }

        $isCorrectHash = $secretHash && $urlEmail && $mailHash->getEmailHash($urlEmail) === $secretHash;

        if ($email) {
            $template = $email->getTemplate();
            if ('mautic_code_mode' === $template) {
                // Use system default
                $template = null;
            }

            /** @var \Mautic\FormBundle\Entity\Form $unsubscribeForm */
            $unsubscribeForm = $email->getUnsubscribeForm();
            if (null != $unsubscribeForm && $unsubscribeForm->isPublished()) {
                $formTemplate = $unsubscribeForm->getTemplate();
                $formContent  = '<div class="mautic-unsubscribeform">'.$formModel->getContent($unsubscribeForm).'</div>';
            }
        } else {
            if ($isOneClickUnsubscribe) {
                return new Response($this->translator->trans('mautic.email.stat_record.not_found'), Response::HTTP_NOT_FOUND);
            }
        }

        if (empty($template) && empty($formTemplate)) {
            $template = $this->coreParametersHelper->get('theme');
        } elseif (!empty($formTemplate)) {
            $template = $formTemplate;
        }

        $theme = $themeHelper->getTheme($template);
        if ($theme->getTheme() != $template) {
            $template = $theme->getTheme();
        }
        $contentTemplate = $themeHelper->checkForTwigTemplate('@themes/'.$template.'/html/message.html.twig');
        if (!empty($stat) || $isCorrectHash) {
            $successSessionName = 'mautic.email.prefscenter.success';

            if (!empty($stat) && $lead = $stat->getLead()) {
                // Set the lead as current lead
                $contactTracker->setTrackedContact($lead);

                // Set lead lang
                if ($language = $lead->getPreferredLocale()) {
                    $this->translator->setLocale($language);
                }

                // Add contact ID to the session name in case more contacts
                // share the same session/device and the contact is known.
                $successSessionName .= ".{$lead->getId()}";
            } elseif (empty($stat)) {
                $leadRepo = $leadModel->getRepository();
                $contacts = $leadRepo->getContactsByEmail($urlEmail);
                $lead     = null;
                if (is_array($contacts) && count($contacts) > 0) {
                    $lead  = array_pop($contacts);
                } else {
                    $message = $this->translator->trans('mautic.email.stat_record.not_found');
                }
            }

            if (!$showContactPreferences || $isUnsubscribeAll) {
                if (!empty($stat)) {
                    $message = $this->getUnsubscribeMessage($idHash, $model, $stat, $this->translator);
                } elseif ($lead && $lead instanceof Lead) {
                    $message = $this->getUnsubscribeMessageLead($idHash, $model, $lead, $this->translator, $urlEmail);
                }
            } elseif ($lead) {
                $params = ['idHash' => $idHash, 'urlEmail' => $urlEmail];

                if ($urlEmail) {
                    $params['secretHash'] = $mailHash->getEmailHash($urlEmail);
                }

                $action         = $this->generateUrl('mautic_email_unsubscribe', $params);
                $viewParameters = [
                    'lead'                         => $lead,
                    'idHash'                       => $idHash,
                    'showContactFrequency'         => $this->coreParametersHelper->get('show_contact_frequency'),
                    'showContactPauseDates'        => $this->coreParametersHelper->get('show_contact_pause_dates'),
                    'showContactPreferredChannels' => $this->coreParametersHelper->get('show_contact_preferred_channels'),
                    'showContactCategories'        => $this->coreParametersHelper->get('show_contact_categories'),
                    'showContactSegments'          => $this->coreParametersHelper->get('show_contact_segments'),
                    'dncUrl'                       => $this->generateUrl('mautic_email_unsubscribe_all', $params),
                ];

                if ($session->get($successSessionName)) {
                    $viewParameters['successMessage'] = $this->translator->trans('mautic.email.preferences_center_success_message.text');
                }

                $form = $this->getFrequencyRuleForm($lead, $viewParameters, $data, true, $action, true);
                if (true === $form) {
                    $session->set($successSessionName, 1);

                    return $this->postActionRedirect(
                        [
                            'returnUrl'       => $action,
                            'viewParameters'  => $viewParameters,
                            'contentTemplate' => $contentTemplate,
                        ]
                    );
                } else {
                    // success message should not persist on page refresh
                    $session->set($successSessionName, 0);
                }

                $formView = $form->createView();
                /** @var Page $prefCenter */
                if ($email && ($prefCenter = $email->getPreferenceCenter()) && $prefCenter->getIsPreferenceCenter()) {
                    // Set the page language if there is no lead preferred locale
                    if (empty($language) && $language = $prefCenter->getLanguage()) {
                        $this->translator->setLocale($language);
                    }

                    $html = $prefCenter->getCustomHtml();
                    // check if tokens are present
                    if (str_contains($html, BuilderSubscriber::saveprefsRegex)) {
                        // set custom tag to inject end form
                        // update show pref center tokens by looking for their presence in the html
                        $showParameters  = $this->buildShowParametersBasedOnContent($html, $viewParameters);
                        $eventParameters = array_merge(
                            $viewParameters,
                            $showParameters,
                            [
                                'form'       => $formView,
                                'startform'  => $this->renderView('@MauticCore/Default/form.html.twig', ['form' => $formView]),
                                'custom_tag' => '<a name="end-'.$formView->vars['id'].'"></a>',
                            ]
                        );

                        $event = new PageDisplayEvent($html, $prefCenter, $eventParameters);

                        $this->dispatcher->dispatch($event, PageEvents::PAGE_ON_DISPLAY);

                        $html = $event->getContent();

                        if (!$session->has($successSessionName)) {
                            $successMessageData       = ['class="pref-successmessage"'];
                            $successMessageDataHidden = [];
                            foreach ($successMessageData as $successMessageData) {
                                $successMessageDataHidden[] = $successMessageData.' style=display:none';
                            }
                            $html = str_replace(
                                $successMessageData,
                                $successMessageDataHidden,
                                $html
                            );
                        } else {
                            $session->remove($successSessionName);
                        }
                        $html = preg_replace(
                            '/'.BuilderSubscriber::identifierToken.'/',
                            $lead->getPrimaryIdentifier(),
                            $html
                        );
                        $pageModel->hitPage($prefCenter, $request, 200, $lead);
                    } else {
                        unset($html);
                    }
                }

                if (empty($html)) {
                    $html = $this->render(
                        '@MauticEmail/Lead/preference_options.html.twig',
                        array_merge(
                            $viewParameters,
                            [
                                'form'         => $formView,
                                'currentRoute' => $this->generateUrl(
                                    'mautic_contact_action',
                                    [
                                        'objectAction' => 'contactFrequency',
                                        'objectId'     => $lead->getId(),
                                    ]
                                ),
                            ]
                        )
                    )->getContent();
                }
                $message = $html;
            }
        } else {
            $message = $this->translator->trans('mautic.email.stat_record.not_found');
        }

        $config = $theme->getConfig();

        $viewParams = [
            'email'    => $email,
            'lead'     => $lead,
            'template' => $template,
            'message'  => $message,
        ];

        if (!empty($formContent)) {
            $viewParams['content'] = $formContent;
            if (in_array('form', $config['features'])) {
                $contentTemplate = $themeHelper->checkForTwigTemplate('@themes/'.$template.'/html/form.html.twig');
            } else {
                $viewParams['content'] = '';
                $viewParams['message'] = $message.$formContent;
            }
        }

        return $this->render($contentTemplate, $viewParams);
    }

    public function unsubscribeAllAction(Request $request, string $idHash, ?string $urlEmail = null, ?string $secretHash = null): Response
    {
        $request->attributes->set('unsubscribe_all', 1);

        return $this->forward(static::class.'::unsubscribeAction', [
            'request'    => $request,
            'idHash'     => $idHash,
            'urlEmail'   => $urlEmail,
            'secretHash' => $secretHash,
        ]);
    }

    /**
     * @throws \Exception
     * @throws \Mautic\CoreBundle\Exception\FileNotFoundException
     */
    public function resubscribeAction(ContactTracker $contactTracker, EmailModel $model, MailHashHelper $mailHash, ThemeHelper $themeHelper, AssetsHelper $assetsHelper, AnalyticsHelper $analyticsHelper, $idHash): Response
    {
        $stat = $model->getEmailStatus($idHash);

        if (!empty($stat)) {
            $email = $stat->getEmail();
            $lead  = $stat->getLead();

            if ($lead) {
                // Set the lead as current lead
                $contactTracker->setTrackedContact($lead);

                if (!$this->translator instanceof LocaleAwareInterface) {
                    throw new \LogicException(sprintf('$this->translator must be an instance of "%s"', LocaleAwareInterface::class));
                }

                // Set lead lang
                if ($lead->getPreferredLocale()) {
                    $this->translator->setLocale($lead->getPreferredLocale());
                }
            }

            $model->removeDoNotContact($stat->getEmailAddress());

            $message         = $this->coreParametersHelper->get('resubscribe_message');
            $toEmail         = $stat->getEmailAddress();
            $unsubscribeHash = $mailHash->getEmailHash($toEmail);

            if (!$message) {
                $message = $this->translator->trans(
                    'mautic.email.resubscribed.success',
                    [
                        '%unsubscribeUrl%' => '|URL|',
                        '%email%'          => '|EMAIL|',
                    ]
                );
            }
            $message = str_replace(
                [
                    '|URL|',
                    '|EMAIL|',
                ],
                [
                    $this->generateUrl('mautic_email_unsubscribe', ['idHash' => $idHash, 'urlEmail' => $toEmail, 'secretHash' => $unsubscribeHash]),
                    $stat->getEmailAddress(),
                ],
                $message
            );
        } else {
            $email   = $lead   = false;
            $message = $this->translator->trans('mautic.email.stat_record.not_found');
        }

        $template = (!empty($email) && 'mautic_code_mode' !== $email->getTemplate()) ? $email->getTemplate() : $this->coreParametersHelper->get('theme');

        $theme = $themeHelper->getTheme($template);

        if ($theme->getTheme() != $template) {
            $template = $theme->getTheme();
        }

        // Ensure template still exists
        $theme = $themeHelper->getTheme($template);
        if (empty($theme) || $theme->getTheme() !== $template) {
            $template = $this->coreParametersHelper->get('theme');
        }

        $analytics = $analyticsHelper->getCode();

        if (!empty($analytics)) {
            $assetsHelper->addCustomDeclaration($analytics);
        }

        $logicalName = $themeHelper->checkForTwigTemplate('@themes/'.$template.'/html/message.html.twig');

        return $this->render(
            $logicalName,
            [
                'message'  => $message,
                'type'     => 'notice',
                'email'    => $email,
                'lead'     => $lead,
                'template' => $template,
            ]
        );
    }

    /**
     * Handles mailer transport webhook post.
     */
    public function mailerCallbackAction(Request $request): Response
    {
        $event = new TransportWebhookEvent($request);
        $this->dispatcher->dispatch($event, EmailEvents::ON_TRANSPORT_WEBHOOK);

        return $event->getResponse() ?? new Response('No email transport that could process this callback was found', Response::HTTP_NOT_FOUND);
    }

    /**
     * Preview email.
     *
     * @return Response
     */
    public function previewAction(
        AnalyticsHelper $analyticsHelper,
        ThemeHelper $themeHelper,
        AssetsHelper $assetsHelper,
        EmailConfig $emailConfig,
        EmailModel $model,
        Request $request,
        LeadModel $leadModel,
        FakeContactHelper $fakeLeadHelper,
        string $objectId,
        ?string $objectType = null,
    ) {
        $contactId   = (int) $request->query->get('contactId');
        $emailEntity = $model->getEntity($objectId);

        if (null === $emailEntity) {
            return $this->notFound();
        }

        $publicPreview = $emailEntity->isPublicPreview();
        $draftEnabled  = $emailConfig->isDraftEnabled();
        if ('draft' === $objectType && $draftEnabled && $emailEntity->hasDraft()) {
            $publicPreview = $emailEntity->getDraft()->isPublicPreview();
        }

        if (
            ($this->security->isAnonymous() && !$publicPreview)
            || (!$this->security->isAnonymous()
                && !$this->security->hasEntityAccess(
                    'email:emails:viewown',
                    'email:emails:viewother',
                    $emailEntity->getCreatedBy()
                ))
        ) {
            return $this->accessDenied();
        }

        // bogus ID
        if ($contactId && (
            !$this->security->isAdmin()
            && !$this->security->hasEntityAccess('lead:leads:viewown', 'lead:leads:viewother')
        )
        ) {
            // disallow displaying contact information
            $contactId = null;
        }

        // bogus ID
        $idHash = 'xxxxxxxxxxxxxx';

        $BCcontent = $emailEntity->getContent();
        $content   = $emailEntity->getCustomHtml();

        if ('draft' === $objectType && $draftEnabled && $emailEntity->hasDraft()) {
            $content = $emailEntity->getDraftContent();
        }

        if (empty($content) && !empty($BCcontent)) {
            $template = $emailEntity->getTemplate();

            $assetsHelper->addCustomDeclaration('<meta name="robots" content="noindex">');

            $logicalName = $themeHelper->checkForTwigTemplate('@themes/'.$template.'/html/email.html.twig');

            $response = $this->render(
                $logicalName,
                [
                    'inBrowser' => true,
                    'content'   => $emailEntity->getContent(),
                    'email'     => $emailEntity,
                    'lead'      => null,
                    'template'  => $template,
                ]
            );

            // replace tokens
            $content = $response->getContent();
        }

        // Override tracking_pixel
        $tokens = ['{tracking_pixel}' => ''];

        // Prepare contact
        if ($contactId) {
            // We have one from request parameter
            /** @var LeadModel $leadModel */
            $contact = $leadModel->getRepository()->getLead($contactId);
            $contact = $model->enrichedContactWithCompanies($contact);
        } else {
            // Make fake contact.
            /** @var FakeContactHelper $fakeLeadHelper */
            $contact = $fakeLeadHelper->prepareFakeContactWithPrimaryCompany();
        }
        // Generate and replace tokens
        $event = new EmailSendEvent(
            null,
            [
                'content'      => $content,
                'email'        => $emailEntity,
                'idHash'       => $idHash,
                'tokens'       => $tokens,
                'internalSend' => true,
                'lead'         => $contact,
            ]
        );
        $this->dispatcher->dispatch($event, EmailEvents::EMAIL_ON_DISPLAY);

        $content = $event->getContent(true);

        if ($this->security->isAnonymous()) {
            $content = $analyticsHelper->addCode($content);
        }

        return new Response($content);
    }

    /**
     * @throws \Exception
     */
    private function doTracking(Request $request, IntegrationHelper $integrationHelper, MailHelper $mailer, LoggerInterface $mauticLogger, $integration): void
    {
        $logger = $mauticLogger;

        // if additional data were sent with the tracking pixel
        $query_string = $request->server->get('QUERY_STRING');
        if (!$query_string) {
            $logger->log('error', $integration.': query string is not available');

            return;
        }

        if (str_starts_with($query_string, 'r=')) {
            $query_string = substr($query_string, strpos($query_string, '?') + 1);
        } // remove route variable

        parse_str($query_string, $query);

        // URL attr 'd' is encoded so let's decode it first.
        if (!isset($query['d'], $query['sig'])) {
            $logger->log('error', $integration.': query variables are not found');

            return;
        }

        // get secret from plugin settings
        $myIntegration = $integrationHelper->getIntegrationObject($integration);

        if (!$myIntegration) {
            $logger->log('error', $integration.': integration not found');

            return;
        }
        $keys = $myIntegration->getDecryptedApiKeys();

        // generate signature
        $salt = $keys['secret'];
        if (!str_contains($salt, '$1$')) {
            $salt = '$1$'.$salt;
        } // add MD5 prefix
        $cr    = crypt(urlencode($query['d']), $salt);
        $mySig = hash('crc32b', $cr); // this hash type is used in c#

        // compare signatures
        if (hash_equals($mySig, $query['sig'])) {
            // decode and parse query variables
            $b64 = base64_decode($query['d']);
            $gz  = gzdecode($b64);
            parse_str($gz, $query);
        } else {
            // signatures don't match: stop
            $logger->log('error', $integration.': signatures don\'t match');

            unset($query);
        }

        if (empty($query) || !isset($query['email'], $query['subject'], $query['body'])) {
            $logger->log('error', $integration.': query variables are empty');

            return;
        }

        if (MAUTIC_ENV === 'dev') {
            $logger->log('error', $integration.': '.json_encode($query, JSON_PRETTY_PRINT));
        }

        /** @var EmailModel $model */
        $model = $this->getModel('email');

        // email is a semicolon delimited list of emails
        $emails    = explode(';', $query['email']);
        $leadModel = $this->getModel('lead');
        \assert($leadModel instanceof LeadModel);
        $repo = $leadModel->getRepository();

        foreach ($emails as $email) {
            $lead = $repo->getLeadByEmail($email);
            if (null === $lead) {
                $lead = $this->createLead($email, $repo);
                if (null === $lead) {
                    continue;
                }
            }

            $idHash = hash('crc32', $email.$query['body']);
            $idHash = substr($idHash.$idHash, 0, 13); // 13 bytes length

            $stat = $model->getEmailStatus($idHash);

            // stat doesn't exist, create one
            if (null === $stat) {
                $lead['email'] = $email; // needed for stat
                $stat          = $this->addStat($mailer, $lead, $email, $query, $idHash);
            }

            $stat->setSource('email.client');

            if ($stat || 'Outlook' !== $integration) { // Outlook requests the tracking gif on send
                $model->hitEmail($idHash, $request); // add email event
            }
        }
    }

    /**
     * @return Response
     */
    public function pluginTrackingGifAction(Request $request, IntegrationHelper $integrationHelper, MailHelper $mailer, LoggerInterface $mauticLogger, $integration)
    {
        $this->doTracking($request, $integrationHelper, $mailer, $mauticLogger, $integration);

        return TrackingPixelHelper::getResponse($request); // send gif
    }

    private function addStat(MailHelper $mailer, $lead, $email, $query, $idHash): ?Stat
    {
        if (null !== $lead) {
            // To lead
            $mailer->addTo($email);

            // sanitize variables to prevent malicious content
            $from = filter_var($query['from'], FILTER_SANITIZE_EMAIL);
            $mailer->setFrom($from, '');

            // Set Content
            $body = htmlspecialchars(filter_var($query['body'], FILTER_FLAG_STRIP_HIGH));
            $mailer->setBody($body);
            $mailer->parsePlainText($body);

            // Set lead
            $mailer->setLead($lead);
            $mailer->setIdHash($idHash);

            $subject = htmlspecialchars(filter_var($query['subject'], FILTER_FLAG_STRIP_HIGH));
            $mailer->setSubject($subject);

            return $mailer->createEmailStat();
        }

        return null;
    }

    private function createLead($email, $repo): ?Lead
    {
        $model = $this->getModel('lead.lead');
        \assert($model instanceof LeadModel);
        $lead  = $model->getEntity();
        // set custom field values
        $data = ['email' => $email];
        $model->setFieldValues($lead, $data, true);
        // create lead
        $model->saveEntity($lead);

        // return entity
        return $repo->getLeadByEmail($email);
    }

    public function getUnsubscribeMessage($idHash, $model, $stat, $translator): string
    {
        $model->setDoNotContact($stat, $translator->trans('mautic.email.dnc.unsubscribed'), DoNotContact::UNSUBSCRIBED);

        return $this->getUnsubscribeText($translator, $stat->getEmailAddress(), $idHash);
    }

    public function getUnsubscribeMessageLead(string $idHash, EmailModel $model, Lead $lead, TranslatorInterface $translator, string $urlEmail): string
    {
        $model->setDoNotContactLead($lead, $translator->trans('mautic.email.dnc.unsubscribed'), DoNotContact::UNSUBSCRIBED);

        return $this->getUnsubscribeText($translator, $urlEmail, $idHash);
    }

    private function getUnsubscribeText(TranslatorInterface $translator, string $email, string $idHash): string
    {
        $message = $this->coreParametersHelper->get('unsubscribe_message');
        if (!$message) {
            $message = $translator->trans(
                'mautic.email.unsubscribed.success',
                [
                    '%resubscribeUrl%' => '|URL|',
                    '%email%'          => '|EMAIL|',
                ]
            );
        }

        return str_replace(
            [
                '|URL|',
                '|EMAIL|',
            ],
            [
                $this->generateUrl('mautic_email_resubscribe', ['idHash' => $idHash]),
                $email,
            ],
            $message
        );
    }

    /**
     * The $viewParameters here have already been used to build the $form.
     * Fields that are set to show based on the app configuration are part
     * of the form. If the field is not configured to show, but a token exists
     * for that field in the content, then we need to keep the configuration
     * value instead of letting the content determine if it should show. This
     * is because of what was stated above - fields that are not configured to
     * to show are not part of the form. Attempting to render them will result
     * in an error.
     *
     * @param mixed[] $viewParameters
     *
     * @return mixed[]
     */
    private function buildShowParametersBasedOnContent(string $content, array $viewParameters): array
    {
        /*
         * Since we're going to be merging this with the $viewParameters, filter out `true` values. We do not
         * want to change a configured value from `false` to `true` because a value of `false` in the $viewParameters
         * means that the field is not configured to show and therefore is not part of the form. Attempting to
         * render that field just because a token for it exists will result in an error.
         */
        $showParamsBasedOnContent = array_filter([
            'showContactFrequency'         => str_contains($content, BuilderSubscriber::channelfrequency),
            'showContactSegments'          => str_contains($content, BuilderSubscriber::segmentListRegex),
            'showContactCategories'        => str_contains($content, BuilderSubscriber::categoryListRegex),
            'showContactPreferredChannels' => str_contains($content, BuilderSubscriber::preferredchannel),
        ], fn (bool $value) =>!$value);

        $showParamsBasedOnConfiguration = array_filter($viewParameters, fn ($key) => str_starts_with($key, 'show'), ARRAY_FILTER_USE_KEY);

        return array_merge($showParamsBasedOnConfiguration, $showParamsBasedOnContent);
    }
}
