<?php
/**
 * Mageplaza
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the mageplaza.com license that is
 * available through the world-wide-web at this URL:
 * https://www.mageplaza.com/LICENSE.txt
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade this extension to newer
 * version in the future.
 *
 * @category    Mageplaza
 * @package     Mageplaza_EmailAttachments
 * @copyright   Copyright (c) Mageplaza (https://www.mageplaza.com/)
 * @license     https://www.mageplaza.com/LICENSE.txt
 */

namespace Mageplaza\EmailAttachments\Model;

use Laminas\Mail\Message;
use Laminas\Mime\Mime;
use Laminas\Mime\Part;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\Filesystem;
use Magento\Framework\Mail\MailMessageInterface;
use Magento\Framework\Mail\MimeInterface;
use Magento\Framework\ObjectManagerInterface;
use Magento\Framework\Session\SessionManagerInterface;
use Magento\Sales\Model\Order;
use Magento\Sales\Model\Order\Creditmemo;
use Magento\Sales\Model\Order\Invoice;
use Magento\Sales\Model\Order\Shipment;
use Magento\Store\Model\Store;
use Mageplaza\EmailAttachments\Helper\Data;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\Part\DataPart;
use Symfony\Component\Mime\Part\Multipart\MixedPart;
use Zend_Pdf;
use Zend_Pdf_Exception;

/**
 * Class MailEvent
 * @package Mageplaza\EmailAttachments\Model
 */
class MailEvent
{
    /**
     * @var array
     */
    const MIME_TYPES = [
        'txt'  => 'text/plain',
        'pdf'  => 'application/pdf',
        'doc'  => 'application/msword',
        'docx' => 'application/msword',
    ];

    private $message;

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

    /**
     * @var Mail
     */
    private $mail;

    /**
     * @var Data
     */
    private $dataHelper;

    /**
     * @var Filesystem
     */
    private $filesystem;

    /**
     * @var ObjectManagerInterface
     */
    private $objectManager;

    /**
     * @var SessionManagerInterface
     */
    private $coreSession;

    /**
     * MailEvent constructor.
     *
     * @param Mail $mail
     * @param Data $dataHelper
     * @param Filesystem $filesystem
     * @param ObjectManagerInterface $objectManager
     * @param SessionManagerInterface $coreSession
     */
    public function __construct(
        Mail $mail,
        Data $dataHelper,
        Filesystem $filesystem,
        ObjectManagerInterface $objectManager,
        SessionManagerInterface $coreSession
    ) {
        $this->mail          = $mail;
        $this->dataHelper    = $dataHelper;
        $this->filesystem    = $filesystem;
        $this->objectManager = $objectManager;
        $this->coreSession   = $coreSession;
    }

    /**
     * @param MailMessageInterface|Message|\Symfony\Component\Mime\Email $message
     *
     * @throws Zend_Pdf_Exception
     */
    public function dispatch($message)
    {
        $templateVars = $this->mail->getTemplateVars();
        if (!$templateVars) {
            return;
        }
        /** @var Store|null $store */
        $store   = isset($templateVars['store']) ? $templateVars['store'] : null;
        $storeId = $store ? $store->getId() : null;

        if (!$this->dataHelper->isEnabled($storeId)) {
            return;
        }

        if ($emailType = $this->getEmailType($templateVars)) {
            /** @var Order|Invoice|Shipment|Creditmemo $obj */
            $obj = $templateVars[$emailType];

            $attachmentPDF = null;
            if ($this->dataHelper->checkPdfInvoiceIsEnable()) {
                $attachmentPDF = $this->coreSession->getAttachPdf();
                if (is_object($attachmentPDF)) {
                    $this->parts[] = $attachmentPDF;
                }
            }

            if ($this->dataHelper->isEnabledAttachPdf($storeId)
                && in_array($emailType, $this->dataHelper->getAttachPdf($storeId), true)) {
                $this->setPdfAttachment($emailType, $message, $obj, $attachmentPDF);
                $attachmentPDF = true;
            }

            if ($this->dataHelper->getTacFile($storeId)
                && in_array($emailType, $this->dataHelper->getAttachTac($storeId), true)) {
                $this->setTACAttachment($message, $storeId);
                $attachmentPDF = true;
            }

            if ($this->dataHelper->versionCompare('2.2.9') && $attachmentPDF) {
                $this->setBodyAttachment($message);
                $this->parts = [];
            }

            foreach ($this->dataHelper->getCcTo($storeId) as $email) {
                $message->addCc(trim($email));
            }

            foreach ($this->dataHelper->getBccTo($storeId) as $email) {
                $message->addBcc(trim($email));
            }
        }
        $this->message = $message;

        $this->mail->setTemplateVars([]);
    }

    public function getMessage()
    {
        return $this->message;
    }

    /**
     * @param $templateVars
     *
     * @return bool|string
     */
    private function getEmailType($templateVars)
    {
        $emailTypes = ['invoice', 'shipment', 'creditmemo', 'order'];

        foreach ($emailTypes as $emailType) {
            if (isset($templateVars[$emailType])) {
                return $emailType;
            }
        }

        return false;
    }

    /**
     * @param $emailType
     * @param $message
     * @param $obj
     * @param $attachmentPDF
     *
     * @return DataPart|void
     * @throws Zend_Pdf_Exception
     */
    private function setPdfAttachment($emailType, $message, $obj, $attachmentPDF = null)
    {
        if (is_object($attachmentPDF)) {
            return;
        }

        $pdfModel = 'Magento\Sales\Model\Order\Pdf\\' . ucfirst($emailType);
        /** @var Zend_Pdf $pdf */
        $pdf = $this->objectManager->create($pdfModel)->getPdf([$obj]);

        $filename       = $emailType . $obj->getIncrementId() . '.pdf';
        $type           = 'application/pdf';
        $attachmentFile = $pdf->render();

        if ($this->dataHelper->versionCompare('2.2.9') && !$this->dataHelper->isAbove248()) {
            $attachment              = new Part($attachmentFile);
            $attachment->type        = $type;
            $attachment->encoding    = Mime::ENCODING_BASE64;
            $attachment->disposition = Mime::DISPOSITION_ATTACHMENT;
            $attachment->filename    = $filename;

            $this->parts[] = $attachment;

            return;
        }

        if ($this->dataHelper->isAbove248()) {
            $attachment    = new DataPart($attachmentFile, $filename, $type, MimeInterface::ENCODING_BASE64);
            $this->parts[] = $attachment;

            return;
        }

        $message->createAttachment(
            $attachmentFile,
            $type,
            Mime::DISPOSITION_ATTACHMENT,
            Mime::ENCODING_BASE64,
            $emailType . $obj->getIncrementId() . '.pdf'
        );
    }

    /**
     * @param MailMessageInterface|Message $message
     * @param null $storeId
     */
    private function setTACAttachment($message, $storeId = null)
    {
        [$content, $ext, $mimeType] = $this->getTacFile($storeId);

        if ($this->dataHelper->versionCompare('2.2.9') && !$this->dataHelper->isAbove248()) {
            $attachment              = new Part($content);
            $attachment->type        = $mimeType;
            $attachment->encoding    = Mime::ENCODING_BASE64;
            $attachment->disposition = Mime::DISPOSITION_ATTACHMENT;
            $attachment->filename    = __('terms_and_conditions') . '.' . $ext;

            $this->parts[] = $attachment;

            return;
        }

        if ($this->dataHelper->isAbove248()) {
            $attachment    = new DataPart($content, __('terms_and_conditions') . '.' . $ext, $mimeType, MimeInterface::ENCODING_BASE64);
            $this->parts[] = $attachment;

            return;
        }

        $message->createAttachment(
            $content,
            $mimeType,
            Mime::DISPOSITION_ATTACHMENT,
            Mime::ENCODING_BASE64,
            __('terms_and_conditions') . '.' . $ext
        );
    }

    /**
     * @param $message
     * @param $attachmentFile
     *
     * @return void
     */
    private function setBodyAttachment($message)
    {
        if ($this->dataHelper->isAbove248()) {
            if ($message instanceof \Magento\Framework\Mail\EmailMessage) {
                $originalBody = $message->getSymfonyMessage()->getBody();
                $allParts     = array_merge([$originalBody], $this->parts);
                $body         = new MixedPart(...$allParts);
                $message->getSymfonyMessage()->setBody($body);
            }
        } else {
            $body = Message::fromString($message->getRawMessage())->getBody();
            if ($this->dataHelper->versionCompare('2.3.3')) {
                $body = quoted_printable_decode($body);
            }

            $part = new Part($body);
            $part->setCharset('utf-8');
            $part->setEncoding(Mime::ENCODING_BASE64);
            if ($this->dataHelper->versionCompare('2.3.3')) {
                $part->setEncoding(Mime::ENCODING_QUOTEDPRINTABLE);
                $part->setDisposition(Mime::DISPOSITION_INLINE);
            }
            $part->setType(Mime::TYPE_HTML);
            array_unshift($this->parts, $part);

            $bodyPart = new \Laminas\Mime\Message();
            $bodyPart->setParts($this->parts);
            $message->setBody($bodyPart);
        }
    }

    /**
     * @param $laminasMessage
     *
     * @return Email
     */
    protected function convertToSymfonyEmail($laminasMessage)
    {
        $email = new Email();

        $fromList = $laminasMessage->getFrom();
        if (is_array($fromList) && count($fromList)) {
            $from = $fromList[0];
            $email->from(new Address($from->getEmail(), $from->getName()));
        }

        $to = $laminasMessage->getTo();
        if (is_array($to)) {
            foreach ($to as $toAddress) {
                $email->to(new Address($toAddress->getEmail(), $toAddress->getName()));
            }
        }

        $email->subject((string) $laminasMessage->getSubject());
        $body    = $laminasMessage->getBody();
        $content = $body->getBody();
        $subtype = $body->getMediaSubtype();

        if ($subtype === 'html') {
            $email->html($content);
        } else {
            $email->text($content);
        }

        if ($laminasMessage->getCc()) {
            foreach ($laminasMessage->getCc() as $ccAddress) {
                $email->addCc(new Address($ccAddress->getEmail(), $ccAddress->getName()));
            }
        }

        if ($laminasMessage->getBcc()) {
            foreach ($laminasMessage->getBcc() as $bccAddress) {
                $email->addBcc(new Address($bccAddress->getEmail(), $bccAddress->getName()));
            }
        }

        if ($laminasMessage->getReplyTo()) {
            foreach ($laminasMessage->getReplyTo() as $replyTo) {
                $email->replyTo(new Address($replyTo->getEmail(), $replyTo->getName()));
            }
        }

        return $email;
    }

    /**
     * @param null $storeId
     *
     * @return array
     */
    private function getTacFile($storeId = null)
    {
        $mediaDirectory = $this->filesystem->getDirectoryRead(DirectoryList::MEDIA);
        $tacPath        = $this->dataHelper->getTacFile($storeId);
        $filePath       = $mediaDirectory->getAbsolutePath('mageplaza/email_attachments/' . $tacPath);
        $content        = file_get_contents($filePath);
        $ext            = (string) substr($filePath, strrpos($filePath, '.') + 1);

        return [$content, $ext, self::MIME_TYPES[$ext]];
    }
}
