<?php
declare(strict_types=1);
namespace MRBS;

use Email\Parse;
use PHPMailer\PHPMailer\Exception;
use PHPMailer\PHPMailer\PHPMailer;

/**
 * A wrapper for PHPMailer
 */
class Mailer extends PHPMailer
{

  /**
   * @throws Exception
   */
  public function __construct(array $mail_settings, array $sendmail_settings, array $smtp_settings, ?bool $exceptions = null)
  {
    parent::__construct($exceptions);

    // Set the Auto-Submitted header as this mailer will only be used for automatically
    // generated emails (e.g. booking notifications and error logging).
    $this->addCustomHeader('Auto-Submitted', 'auto-generated');

    switch ($mail_settings['admin_backend'])
    {
      case 'mail':
        $this->isMail();
        break;

      case 'qmail':
        $this->isQmail();
        if (isset($mail_settings['qmail']['qmail-inject-path']))
        {
          $this->Sendmail = $mail_settings['qmail']['qmail-inject-path'];
        }
        break;

      case 'sendmail':
        $this->isSendmail();
        $this->Sendmail = $sendmail_settings['path'];
        if (isset($sendmail_settings['args']) && ($sendmail_settings['args'] !== ''))
        {
          $this->Sendmail .= ' ' . $sendmail_settings['args'];
        }
        break;

      case 'smtp':
        $this->isSMTP();
        $this->Host = $smtp_settings['host'];
        $this->Port = $smtp_settings['port'];
        $this->SMTPAuth = $smtp_settings['auth'];
        $this->SMTPSecure = $smtp_settings['secure'];
        $this->Username = $smtp_settings['username'];
        $this->Password = $smtp_settings['password'];
        $this->Hostname = $smtp_settings['hostname'];
        $this->Helo = $smtp_settings['helo'];
        if ($smtp_settings['disable_opportunistic_tls'])
        {
          $this->SMTPAutoTLS = false;
        }
        $this->SMTPOptions = array
        (
          'ssl' => array
          (
            'verify_peer' => $smtp_settings['ssl_verify_peer'],
            'verify_peer_name' => $smtp_settings['ssl_verify_peer_name'],
            'allow_self_signed' => $smtp_settings['ssl_allow_self_signed']
          )
        );
        break;

      default:
        $this->isMail();
        trigger_error("Unknown mail backend '" . $mail_settings['admin_backend'] . "'." .
          " Defaulting to 'mail'.",
          E_USER_WARNING);
        break;
    }
  }


  /**
   * Sets a series of To addresses
   * @param string $address_string an RFC822 address string
   * @throws Exception
   */
  public function addAddressesRFC822(string $address_string) : bool
  {
    $parser = new Parse(new Logger());
    $parsed_addresses = $parser->parse($address_string);

    foreach ($parsed_addresses['email_addresses'] as $parsed_address)
    {
      if (false === ($name_and_address = $this->getNameAndAddress($parsed_address)))
      {
        return false;
      }

      if (false === $this->addAddress($name_and_address['address'], $name_and_address['name']))
      {
        return false;
      }
    }

    return true;
  }


  /**
   * Sets a From address
   * @param string $address an RFC822 address
   * @throws Exception
   */
  public function setFromRFC822(string $address, bool $auto=true) : bool
  {
    $parser = new Parse(new Logger());
    $parsed_address = $parser->parse($address, false);

    if (false === ($name_and_address = $this->getNameAndAddress($parsed_address)))
    {
      return false;
    }

    return $this->setFrom($name_and_address['address'], $name_and_address['name'], $auto);
  }


  /**
   * Form a name and address from the output of the address parser
   *
   * @param array $parsed_address the output of MRBS\Email\Parse->parse()
   * @return array|false
   * @throws Exception
   */
  private function getNameAndAddress(array $parsed_address)
  {
    if ($parsed_address['invalid'])
    {
      $error_message = "Invalid email address '" . $parsed_address['original_address'] . "': " . $parsed_address['invalid_reason'];
      $this->setError($error_message);
      $this->edebug($error_message);
      if ($this->exceptions) {
        throw new Exception($error_message);
      }
      return false;
    }

    return [
      'name' => $parsed_address['name_parsed'],
      'address' => $parsed_address['local_part_parsed'] . '@' . $parsed_address['domain_part']
    ];
  }

}
