<?php

namespace MailPoet\Subscribers;

if (!defined('ABSPATH')) exit;


use Html2Text\Html2Text;
use MailPoet\Entities\SegmentEntity;
use MailPoet\Entities\SubscriberEntity;
use MailPoet\Mailer\MailerFactory;
use MailPoet\Mailer\MetaInfo;
use MailPoet\Services\AuthorizedEmailsController;
use MailPoet\Services\Bridge;
use MailPoet\Settings\SettingsController;
use MailPoet\Subscription\SubscriptionUrlFactory;
use MailPoet\Util\Helpers;
use MailPoet\WP\Functions as WPFunctions;

class ConfirmationEmailMailer {

  const MAX_CONFIRMATION_EMAILS = 3;

  /** @var MailerFactory */
  private $mailerFactory;

  /** @var WPFunctions */
  private $wp;

  /** @var SettingsController */
  private $settings;

  /** @var MetaInfo */
  private $mailerMetaInfo;

  /** @var SubscribersRepository */
  private $subscribersRepository;

  /** @var SubscriptionUrlFactory */
  private $subscriptionUrlFactory;

  /** @var array Cache for confirmation emails sent within a request */
  private $sentEmails = [];

  public function __construct(
    MailerFactory $mailerFactory,
    WPFunctions $wp,
    SettingsController $settings,
    SubscribersRepository $subscribersRepository,
    SubscriptionUrlFactory $subscriptionUrlFactory
  ) {
    $this->mailerFactory = $mailerFactory;
    $this->wp = $wp;
    $this->settings = $settings;
    $this->mailerMetaInfo = new MetaInfo;
    $this->subscriptionUrlFactory = $subscriptionUrlFactory;
    $this->subscribersRepository = $subscribersRepository;
  }

  /**
   * Use this method if you want to make sure the confirmation email
   * is not sent multiple times within a single request
   * e.g. if sending confirmation emails from hooks
   * @throws \Exception if unable to send the email.
   */
  public function sendConfirmationEmailOnce(SubscriberEntity $subscriber): bool {
    if (isset($this->sentEmails[$subscriber->getId()])) {
      return true;
    }
    return $this->sendConfirmationEmail($subscriber);
  }

  /**
   * @throws \Exception if unable to send the email.
   */
  public function sendConfirmationEmail(SubscriberEntity $subscriber) {
    $signupConfirmation = $this->settings->get('signup_confirmation');
    if ((bool)$signupConfirmation['enabled'] === false) {
      return false;
    }
    if (!$this->wp->isUserLoggedIn() && $subscriber->getConfirmationsCount() >= self::MAX_CONFIRMATION_EMAILS) {
      return false;
    }

    $authorizationEmailsValidation = $this->settings->get(AuthorizedEmailsController::AUTHORIZED_EMAIL_ADDRESSES_ERROR_SETTING);
    $unauthorizedSenderEmail = isset($authorizationEmailsValidation['invalid_sender_address']);
    if (Bridge::isMPSendingServiceEnabled() && $unauthorizedSenderEmail) {
      return false;
    }

    $segments = $subscriber->getSegments()->toArray();
    $segmentNames = array_map(function(SegmentEntity $segment) {
      return $segment->getName();
    }, $segments);

    $body = nl2br($signupConfirmation['body']);

    // replace list of segments shortcode
    $body = str_replace(
      '[lists_to_confirm]',
      '<strong>' . join(', ', $segmentNames) . '</strong>',
      $body
    );

    // replace activation link
    $body = Helpers::replaceLinkTags(
      $body,
      $this->subscriptionUrlFactory->getConfirmationUrl($subscriber),
      ['target' => '_blank'],
      'activation_link'
    );

    //create a text version. @ is important here, Html2Text throws warnings
    $text = @Html2Text::convert(
      (mb_detect_encoding($body, 'UTF-8', true)) ? $body : utf8_encode($body),
      true
    );

    // build email data
    $email = [
      'subject' => $signupConfirmation['subject'],
      'body' => [
        'html' => $body,
        'text' => $text,
      ],
    ];

    // send email
    $extraParams = [
      'meta' => $this->mailerMetaInfo->getConfirmationMetaInfo($subscriber),
    ];
    try {
      $result = $this->mailerFactory->getDefaultMailer()->send($email, $subscriber, $extraParams);
    } catch (\Exception $e) {
      throw new \Exception(__('Something went wrong with your subscription. Please contact the website owner.', 'mailpoet'));
    }

    if ($result['response'] === false) {
      throw new \Exception(__('Something went wrong with your subscription. Please contact the website owner.', 'mailpoet'));
    };

    if (!$this->wp->isUserLoggedIn()) {
      $subscriber->setConfirmationsCount($subscriber->getConfirmationsCount() + 1);
      $this->subscribersRepository->persist($subscriber);
      $this->subscribersRepository->flush();
    }
    $this->sentEmails[$subscriber->getId()] = true;

    return true;
  }
}
