<?php
/**
 * Mailer Class
 *
 * @todo        config('mail.default') auswerten und alternative Mailer verwenden.
 *              Aktuell wird nur auf SMTP gesetzt. Sendmail und log sollten auch einfach umzusetzen sein.
 *              Nachtrag, 2026-04-22: "log" wird jetzt bereits in "Channels\PHPMailerChannel" berücksichtigt.
 *
 * @version     1.0.$Revision:$
 * @version     SVN: $Id:$
 * @package     bplan-base/globals
 * @subpackage  Mail
 * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
 * @copyright   Copyright (C) 2025, 2026 bplan-solutions GmbH & Co. KG <https://www.bplan-solutions.de/>
 * /Δ\
 */

namespace BplanBase\Globals\Mail;


use PHPMailer\PHPMailer\PHPMailer as TheRealPHPMailer;
use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception;


/**
 * Mailer Class
 *
 * @version     1.4.0 / 2026-07-22
 * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
 */
class PHPMailer extends TheRealPHPMailer
{


/* +++ TRAITS +++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */


/* +++ CLASS CONSTANTS +++ ++++++++++++++++++++++++++++++++++++++++++++++++++ */


    /**
     *  Setting the PHPMailer->SMTPDebug property to these numbers or constants (defined in the
     *  PHPMailer SMTP class) results in different amounts of output:
     * - DEBUG_OFF (0): Disable debugging (you can also leave this out completely, 0 is the default).
     * - DEBUG_CLIENT (1): Output messages sent by the client.
     * - DEBUG_SERVER (2): as 1, plus responses received from the server (this is the most useful
     *   setting).
     * - DEBUG_CONNECTION (3): as 2, plus more information about the initial connection - this level
     *   can help diagnose STARTTLS failures.
     * - DEBUG_LOWLEVEL (4): as 3, plus even lower-level information, very verbose, don't use for
     *   debugging SMTP, only low-level problems.
     *
     * @var     int
     */
    const DEBUG_CLIENT = SMTP::DEBUG_CLIENT;


    /**
     * @var     int
     */
    const DEBUG_CONNECTION = SMTP::DEBUG_CONNECTION;


    /**
     * @var     int
     */
    const DEBUG_LOWLEVEL = SMTP::DEBUG_LOWLEVEL;


    /**
     * @var     int
     */
    const DEBUG_OFF = SMTP::DEBUG_OFF;


    /**
     * @var     int
     */
    const DEBUG_SERVER = SMTP::DEBUG_SERVER;


/* +++ OBJECT MEMBERS +++ +++++++++++++++++++++++++++++++++++++++++++++++++++ */


    /**
     * @var     int
     */
    private int $_debugLevel = self::DEBUG_OFF;


/* +++ CLASS MEMBERS +++ ++++++++++++++++++++++++++++++++++++++++++++++++++++ */


    /**
     * @var     string
     */
    private static string $_logPrefix = 'Mail/PHPMailer';


/* +++ OBJECT METHODS +++ +++++++++++++++++++++++++++++++++++++++++++++++++++ */


    /**
     *
     * @param       bool|null $exceptions
     *
     * @version     1.2.1 / 2026-07-22
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function __construct(bool|null $exceptions)
    {
        parent::__construct($exceptions);

        $from = config('mail.from');
        $smtp = config('mail.mailers.smtp');

        $this->CharSet = TheRealPHPMailer::CHARSET_UTF8;
        $this->isSMTP();
        $this->setFrom($from['address'], $from['name']);

        $this->Host     = $smtp['host'];
        $this->Password = $smtp['password'];
        $this->Port     = $smtp['port'];
        $this->Username = $smtp['username'];

        $this->SMTPAuth  = $smtp['smtp_auth'];
        $this->SMTPDebug = $smtp['debug'] ?? $this->_debugLevel;

        /*
        **  Mirror Laravel/Symfony Mailer's own scheme resolution (see MailManager::createSmtpTransport()):
        **  the port decides between implicit TLS (SMTPS, port 465) and opportunistic/explicit TLS, the
        **  "encryption" config value is only consulted to force explicit STARTTLS. Blindly mapping
        **  "encryption=ssl" to SMTPS regardless of port breaks unencrypted relays on port 25/587. */
        $this->SMTPSecure = match(true) {
            (int) $smtp['port'] === 465  => TheRealPHPMailer::ENCRYPTION_SMTPS,
            $smtp['encryption'] === 'tls' => TheRealPHPMailer::ENCRYPTION_STARTTLS,
            default                       => '',
        };
        $this->SMTPAutoTLS = ($this->SMTPSecure === '');

        if (($verifyPeer = $this->_getVerifyPeerOption($smtp['url'] ?? null)) === false) {
            $this->SMTPOptions = [
                'ssl' => [
                    'verify_peer'      => false,
                    'verify_peer_name' => false,
                    'allow_self_signed' => true,
                ],
            ];
        }

        $this->Debugoutput = static function ($str, $level) {
            \AppLog::debug('Debug level '.$level.': message: '.$str, prefix: self::$_logPrefix);
        };
    } // __construct()


    /**
     * Determines the "verify_peer" DSN option (as used by Symfony Mailer / Laravel's native mail
     * transport, e.g. "smtp://host:port?verify_peer=0") from the raw MAIL_URL, since PHPMailer has
     * no dedicated config key for it.
     *
     * @param       string|null $url
     *
     * @return      bool
     *
     * @version     1.0.0 / 2026-07-22
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    private function _getVerifyPeerOption(string|null $url): bool
    {
        if (empty($url) === true) {
            return true;
        }
        parse_str((string) parse_url($url, PHP_URL_QUERY), $query);

        if (array_key_exists('verify_peer', $query) === false) {
            return true;
        }
        return filter_var($query['verify_peer'], FILTER_VALIDATE_BOOL);

    } // _getVerifyPeerOption()


    /**
     *
     * @version     1.0.0 / 2026-04-22
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function send()
    {
        return parent::send();

    } // send()


    /**
     *
     * @return 	    $this
     *
     * @version     1.0.0 / 2026-04-22
     * @author      Wassilios Meletiadis <wassilios.meletiadis@bplan-solutions.de>
     */
    public function setDebugLevel(int|null $level = null): self
    {
        if ($level === null) {
            $level = self::DEBUG_OFF;
        }
        $this->SMTPDebug = $this->_debugLevel = $level;

        return $this;

    } // setDebugLevel()


/* +++ CLASS METHODS +++ ++++++++++++++++++++++++++++++++++++++++++++++++++++ */


} // class PHPMailer extends TheRealPHPMailer {}
