<?php declare(strict_types=1);

// Load Composer's autoloader
require_once __DIR__ . '/../../vendor/autoload.php';

// Import PHPMailer classes into the global namespace
// These must be at the top of your script, not inside a function
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;

require_once __DIR__ . '/../data/QrbProfileConfiguration.php';
require_once __DIR__ . '/../data/QrbMailData.php';
require_once __DIR__ . '/../data/QrbRecipient.php';
require_once __DIR__ . '/../common/QrbException.php';

// Sends mails.
final class QrbMailProcessor
{
    // public for unit test
    public  const PLACE_HOLDER_GREETING = "{{greeting}}";
    public  const PLACE_HOLDER_AMOUNT   = "{{amount}}";
    
    private QrbStorage              $qrbStorage;
    private QrbProfileConfiguration $qrbCfg;
    private string                  $runName;
    private bool                    $dryRun;

    private ?QrbMailData            $mailData;
    
    // Creates an instance.
    // 
    // qrbStorage: storage
    // qrbCfg:     holds language
    // runName:    used to build the attachment name
    // dryRun:     if dryRun then data is only set up but mail is not sent
    public function __construct(QrbStorage              $qrbStorage,
                                QrbProfileConfiguration $qrbCfg,
                                string                  $runName,
                                bool                    $dryRun)
    {
        $this->qrbStorage = $qrbStorage;
        $this->qrbCfg     = $qrbCfg;
        $this->runName    = $runName;
        $this->dryRun     = $dryRun;
    }

    // Reads required data from storage, must be called once after construction.
    public function init() : void
    {
        $this->mailData = $this->readMailData();
    }
    
    // returns mail data, NULL if flag sendMail is false
    private function readMailData() : ?QrbMailData
    {
        if (! $this->qrbCfg->getSendMail())
        {
            $this->qrbStorage->writeLog("Mail data is not used as flag 'sendMail' is 'false'.");
            return NULL;
        }
        
        $mailData = $this->qrbStorage->readMailData($this->qrbCfg->getAdditionalLanguages(), 
                                                    $this->qrbCfg->getCreatePdf());
        $this->qrbStorage->writeLog("Using mail data: {$mailData}");
        return $mailData;
    }
    
    // Sends a mail with the given mail data to the given recipient with the given PDF 
    // as attachment (attachment only if pdfOutput is not NULL)).
    //
    // Does nothing (returns false) if dryRun (but dryRun will set the data into the PHPMailer).
    //
    // recipient:         recipient
    // language:          language
    // pdfOutput:         PDF to attach (ignored if NULL)
    //
    // return:            true if mail has been sent
    public function send(QrbRecipient $recipient, 
                         string          $language,
                         ?string         $pdfOutput
        ) : bool
    {
        $index = $recipient->getIndex();
        $logPrefix = " - #{$index}.b) ";
        
        if (! $this->mailData)
            throw new QrbException(static::class . "{$logPrefix}mailData not provided",
                                      "exception.mail.mailDataNotProvided");
    
        $mailBody = self::replacePlaceHolders($this->mailData->getHtmlBody($language), 
                                              $recipient, 
                                              $this->qrbCfg->getCurrency());
        
        $phpMailer = new PHPMailer(true);
        $phpMailer->CharSet = PHPMailer::CHARSET_UTF8;
        $phpMailer->SetFrom($this->mailData->getFromAddress(), $this->mailData->getFromName()); // Name is optional
        $phpMailer->Subject = $this->mailData->getSubject($language);
        $phpMailer->Body    = $mailBody;
        $phpMailer->isHTML(true);
        $phpMailer->AddAddress($recipient->getMailAddress());
        
        if ($pdfOutput)
        {
            $attachmentName     = $this->mailData->getAttachmentName($language);
            $additionalNamePart = "_{$this->runName}-{$index}.pdf";
            $attachmentFullName = $attachmentName . $additionalNamePart;
            
            $result = $phpMailer->addStringAttachment($pdfOutput, $attachmentFullName);   // Name is optional
            if (!$result)
                throw new QrbException(static::class . "{$logPrefix}Unable to attach PDF to mail to '{$recipient->getMailAddress()}'",
                                          "exception.mail.unableToAttachPdf",
                                          "{$index}, {$recipient->getMailAddress()}");
        }

        if ($this->dryRun)
            return false;

        if (! $phpMailer->Send())
            throw new QrbException(static::class . "{$logPrefix}Unable to send mail to '{$recipient->getMailAddress()}'",
                                      "exception.mail.unableToSendMail",
                                      "{$index}, {$recipient->getMailAddress()}");
        
        return true;
    }
    
    // Replaces the some known placeholders by some values in the recipient.
    // 
    // Placeholder              replaced by
    // ------------------------------------
    // PLACE_HOLDER_GREETING    recipient->getGreeting()
    // PLACE_HOLDER_AMOUNT      currency + " " + recipient->getAmount()
    // 
    // The replacement of the amount is only processed if an amount exists in the recipient!
    // 
    // mailBody:  as uploaded to input files
    // recipient: current recipient to send the mail to
    // currency:  as configured in the environment (prefixed following a blank to amount)
    // 
    // public for unit test
    public static function replacePlaceHolders(string          $mailBody, 
                                               QrbRecipient $recipient,
                                               String          $currency
        ) : string
    {
        $result = $mailBody;
        
        $result = str_replace(self::PLACE_HOLDER_GREETING, $recipient->getGreeting(), $result);
        
        if ( $recipient->getAmount() != NULL )    // amount exists
        {
            $amount = $currency . " " . $recipient->getAmount();
            $result = str_replace(self::PLACE_HOLDER_AMOUNT, $amount, $result);
        }
        
        return $result;
    }
}

?>
