<?php

namespace SGalinski\SgMail\Service;

/***************************************************************
 *  Copyright notice
 *
 *  (c) sgalinski Internet Services (https://www.sgalinski.de)
 *
 *  All rights reserved
 *
 *  This script is part of the TYPO3 project. The TYPO3 project is
 *  free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  The GNU General Public License can be found at
 *  http://www.gnu.org/copyleft/gpl.html.
 *
 *  This script is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  This copyright notice MUST APPEAR in all copies of the script!
 ***************************************************************/

use SGalinski\SgMail\Domain\Model\Mail;
use SGalinski\SgMail\Domain\Model\Template;
use SGalinski\SgMail\Domain\Repository\MailRepository;
use SGalinski\SgMail\Domain\Repository\TemplateRepository;
use Swift_Attachment;
use Swift_OutputByteStream;
use TYPO3\CMS\Core\Mail\MailMessage;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
use TYPO3\CMS\Fluid\View\StandaloneView;

/**
 * MailTemplateService
 */
class MailTemplateService {

	/**
	 * @var string
	 */
	const MARKER_TYPE_STRING = 'String';

	/**
	 * @var string
	 */
	const MARKER_TYPE_DATE = 'Date';

	/**
	 * @var string
	 */
	const MARKER_TYPE_INTEGER = 'Integer';

	/**
	 * @var array $registerArray
	 */
	private static $registerArray = [];

	/**
	 * @var array $toAddresses
	 */
	private $toAddresses = [];

	/**
	 * @var string $fromAddress
	 */
	private $fromAddress;

	/**
	 * @var array $ccAddresses
	 */
	private $ccAddresses = [];

	/**
	 * @var string $replyToAddress
	 */
	private $replyToAddress;

	/**
	 * @var string $language
	 */
	private $language;

	/**
	 * @var boolean $ignoreMailQueue
	 */
	private $ignoreMailQueue = FALSE;

	/**
	 * @var \TYPO3\CMS\Core\Mail\MailMessage $mailMessage
	 */
	private $mailMessage;

	/**
	 * @var string $templateName
	 */
	private $templateName;

	/**
	 * @var string $extensionKey
	 */
	private $extensionKey;

	/**
	 * @var array $markers
	 */
	private $markers = [];

	/**
	 * holds the TypoScript configuration for sg_mail
	 *
	 * @var array $tsSettings
	 */
	private $tsSettings = [];

	/**
	 * @var array $bccAddresses
	 */
	private $bccAddresses = [];

	/**
	 * @var int
	 */
	private $priority = Mail::PRIORITY_LOWEST;

	/**
	 * @var \SGalinski\SgMail\Domain\Repository\TemplateRepository
	 */
	protected $templateRepository;

	/**
	 * @var \TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager
	 */
	protected $persistenceManager;

	/**
	 * @var \TYPO3\CMS\Extbase\Object\ObjectManager
	 */
	protected $objectManager;

	/**
	 * @var array
	 */
	private $attachments = [];

	/**
	 * MailTemplateService constructor.
	 */
	public function __construct() {
		/** @var ObjectManager objectManager */
		$this->objectManager = GeneralUtility::makeInstance(ObjectManager::class);
		/** @var MailMessage mailMessage */
		$this->mailMessage = $this->objectManager->get(MailMessage::class);
		/** @var TypoScriptSettingsService $typoScriptSettingsService */
		$typoScriptSettingsService = $this->objectManager->get(TypoScriptSettingsService::class);
		$this->tsSettings = $typoScriptSettingsService->getSettings(0, 'tx_sgmail');
		$this->language = $this->tsSettings['templateDefaultLanguage'];
		/** @var TemplateRepository templateRepository */
		$this->templateRepository = $this->objectManager->get(TemplateRepository::class);
		/** @var PersistenceManager persistenceManager */
		$this->persistenceManager = $this->objectManager->get(PersistenceManager::class);

		$this->fromAddress = $this->tsSettings['mail']['default']['from'];
		$this->mailMessage->setFrom($this->fromAddress);
		$this->bccAddresses = GeneralUtility::trimExplode(',', $this->tsSettings['mail']['default']['bcc']);
		$this->ccAddresses = GeneralUtility::trimExplode(',', $this->tsSettings['mail']['default']['cc']);

		foreach ($this->bccAddresses as $index => $email) {
			if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
				unset($this->bccAddresses[$index]);
			}
		}

		foreach ($this->ccAddresses as $index => $email) {
			if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
				unset($this->ccAddresses[$index]);
			}
		}

		if (count($this->bccAddresses) > 0) {
			$this->mailMessage->setBcc($this->bccAddresses);
		}

		if (count($this->ccAddresses) > 0) {
			$this->mailMessage->setCc($this->ccAddresses);
		}
	}

	/**
	 * register a template with sg_mail
	 *
	 * description and subject can now be an array i.e. with elements such as 'en' => 'english description'
	 * or an translation string used in locallang.xml
	 *
	 * @param string $extension
	 * @param string $templateName
	 * @param string $templatePath
	 * @param mixed $description
	 * @param array $markers
	 * @param mixed $subject
	 */
	public static function registerTemplate(
		$extension, $templateName, $templatePath, $description, array $markers, $subject
	) {
		MailTemplateService::$registerArray[$extension][$templateName] = [
			'templatePath' => $templatePath,
			'description' => $description,
			'marker' => $markers,
			'extension' => $extension,
			'templateName' => $templateName,
			'subject' => $subject
		];
	}

	/**
	 * Return default markers for sg_mail
	 *
	 * @param string $translationKey
	 * @param array $marker
	 * @param string $extensionKey
	 * @return array
	 */
	public static function getDefaultTemplateMarker($translationKey, array $marker, $extensionKey = 'sg_mail') {
		$languagePath = 'LLL:EXT:' . $extensionKey . '/Resources/Private/Language/locallang.xlf:' . $translationKey;

		// Need the key for translations
		if (trim($extensionKey) === '') {
			return [];
		}

		$generatedMarker = [];
		foreach ($marker as $markerName) {
			$generatedMarker[] = [
				'marker' => $markerName,
				'value' => $languagePath . '.example.' . $markerName,
				'description' => $languagePath . '.description.' . $markerName,
				'backend_translation_key' => $translationKey . '.example.' . $markerName,
				'extension_key' => $extensionKey
			];
		}

		return $generatedMarker;
	}

	/**
	 * Get all registered templates
	 *
	 * @return array
	 */
	public static function getRegisterArray() {
		return self::$registerArray;
	}

	/**
	 * Send the Email
	 *
	 * @param boolean $isPreview
	 * @return boolean email was sent or added to mail queue successfully?
	 */
	public function sendEmail($isPreview = FALSE) {
		/** @var Template $template */
		$template = $this->templateRepository->findOneByTemplate(
			$this->extensionKey, $this->templateName, $this->language
		);

		// If there is no template for this language, use the default template
		if ($template === NULL) {
			$templatePath = self::$registerArray[$this->extensionKey][$this->templateName]['templatePath'];
			$templateFile = $templatePath . $this->language . '.template.html';
			if (file_exists($templateFile)) {
				$defaultTemplateContent = file_get_contents($templatePath . $this->language . '.template.html');
			} else {
				// no language found and no default template
				$this->setLanguage('en');
				// does an english default template exist ?
				if (file_exists($templatePath . $this->language . '.template.html')) {
					$this->sendEmail();
					return TRUE;
				}

				return FALSE;
			}
		}

		if ($isPreview) {
			$previewMarker = [];
			$markerArray = self::$registerArray[$this->extensionKey][$this->templateName]['marker'];
			foreach ($markerArray as $marker) {
				if ($marker['backend_translation_key']) {
					$previewMarker[$marker['marker']] = LocalizationUtility::translate(
						$marker['backend_translation_key'], $marker['extension_key']
					);
				} else {
					$previewMarker[$marker['marker']] = $marker['value'];
				}
			}
			$this->setIgnoreMailQueue(TRUE);
			$this->setMarkers($previewMarker);
		}

		/** @var StandaloneView $emailView */
		$emailView = $this->objectManager->get(StandaloneView::class);

		if (!isset($defaultTemplateContent)) {
			$emailView->setTemplateSource($template->getContent());
			$subject = $template->getSubject();
		} else {
			$emailView->setTemplateSource($defaultTemplateContent);

			$subject = self::$registerArray[$this->extensionKey][$this->templateName]['subject'];
			if (is_array($subject)) {
				$subject = self::$registerArray[$this->extensionKey][$this->templateName]['subject'][$this->language];
			} else {
				$subject = LocalizationUtility::translate(
					self::$registerArray[$this->extensionKey][$this->templateName]['subject'],
					$this->extensionKey
				);
			}
		}
		$this->mailMessage->setSubject($subject);

		$emailView->assignMultiple($this->markers);
		$emailBody = $emailView->render();

		// insert <br /> tags, but replace every instance of three or more successive breaks with just two.
		$emailBody = nl2br($emailBody);
		$emailBody = preg_replace('/(<br[\s]?[\/]?>[\s]*){3,}/', '<br /><br />', $emailBody);

		if ($this->ignoreMailQueue) {
			$this->addMailToMailQueue($subject, $emailBody, $this->priority, TRUE);
			$this->mailMessage->setBody($emailBody, 'text/html');
			$this->mailMessage->send();
		} else {
			$this->addMailToMailQueue($subject, $emailBody, $this->priority);
		}

		return TRUE;
	}

	/**
	 * Adds a new mail to the mail queue.
	 *
	 * @param string $subject
	 * @param string $emailBody
	 * @param int $priority
	 * @param bool $sent
	 */
	private function addMailToMailQueue($subject, $emailBody, $priority, $sent = FALSE) {
		$mail = $this->objectManager->get(Mail::class);
		$mail->setFromAddress($this->fromAddress);
		$mail->setToAddress($this->toAddresses);
		$mail->setMailSubject($subject);
		$mail->setMailBody($emailBody);
		$mail->setPriority($priority);
		$mail->setBccAddresses(implode(',', $this->bccAddresses));
		$mail->setCcAddresses(implode(',', $this->ccAddresses));
		$mail->setSent($sent);

		$mailRepository = $this->objectManager->get(MailRepository::class);
		$mailRepository->add($mail);
		$this->persistenceManager->persistAll();
	}

	/**
	 * @param array $registerArray
	 * @return void
	 */
	public static function setRegisterArray(array $registerArray) {
		self::$registerArray = $registerArray;
	}

	/**
	 * @param string $toAddresses
	 * @return MailTemplateService
	 */
	public function setToAddresses($toAddresses) {
		$this->toAddresses = $toAddresses;
		$this->mailMessage->setTo($toAddresses);
		return $this;
	}

	/**
	 * @param string $fromAddress
	 * @return MailTemplateService
	 */
	public function setFromAddress($fromAddress) {
		$this->fromAddress = $fromAddress;
		$this->mailMessage->setFrom($fromAddress);
		return $this;
	}

	/**
	 * @param array|string $ccAddresses
	 * @return MailTemplateService
	 */
	public function setCcAddresses($ccAddresses) {
		$this->ccAddresses[] = $ccAddresses;
		$this->mailMessage->setCc($this->ccAddresses);
		return $this;
	}

	/**
	 * @param string $replyToAddress
	 * @return MailTemplateService
	 */
	public function setReplyToAddress($replyToAddress) {
		$this->replyToAddress = $replyToAddress;
		$this->mailMessage->setReplyTo($replyToAddress);
		return $this;
	}

	/**
	 * @param string $language
	 * @return MailTemplateService
	 */
	public function setLanguage($language) {
		$this->language = $language;
		return $this;
	}

	/**
	 * @param boolean $ignoreMailQueue
	 * @return MailTemplateService
	 */
	public function setIgnoreMailQueue($ignoreMailQueue) {
		$this->ignoreMailQueue = $ignoreMailQueue;
		return $this;
	}

	/**
	 * @param string $templateName
	 * @return MailTemplateService
	 */
	public function setTemplateName($templateName) {
		$this->templateName = $templateName;
		return $this;
	}

	/**
	 * @param string $extensionKey
	 * @return MailTemplateService
	 */
	public function setExtensionKey($extensionKey) {
		$this->extensionKey = $extensionKey;
		return $this;
	}

	/**
	 * @param array $markers
	 * @return MailTemplateService
	 */
	public function setMarkers(array $markers) {
		$this->markers = $markers;
		return $this;
	}

	/**
	 * @param array $bccAddresses
	 * @return MailTemplateService
	 */
	public function setBccAddresses(array $bccAddresses) {
		$this->bccAddresses[] = $bccAddresses;
		$this->mailMessage->setBcc($this->bccAddresses);
		return $this;
	}

	/**
	 * @param int $priority
	 * @return MailTemplateService
	 */
	public function setPriority($priority) {
		$this->priority = $priority;
		return $this;
	}

	/**
	 * @param Swift_OutputByteStream $data
	 * @param string $path
	 * @param string $contentType
	 * @return MailTemplateService
	 */
	public function addAttachment($data, $filename, $contentType) {
		$attachment = Swift_Attachment::newInstance()
			->setFilename($filename)
			->setContentType($contentType)
			->setBody($data);
		$this->mailMessage->attach($attachment);
		return $this;
	}

	/**
	 * @return MailMessage
	 */
	public function getMailMessage() {
		return $this->mailMessage;
	}

}