<?php

declare(strict_types=1);

namespace Drupal\Tests\Core\Mail\Plugin\Mail;

use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Mail\MailFormatHelper;
use Drupal\Core\Mail\Plugin\Mail\SymfonyMailer;
use Drupal\Tests\UnitTestCase;
use Psr\Log\LoggerInterface;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Email;

/**
 * @coversDefaultClass \Drupal\Core\Mail\Plugin\Mail\SymfonyMailer
 * @group Mail
 */
class SymfonyMailerTest extends UnitTestCase {

  /**
   * Tests that mixed plain text and html body is converted correctly.
   *
   * @covers ::format
   */
  public function testFormatResemblesHtml() {
    // Populate global $base_path to avoid notices generated by
    // MailFormatHelper::htmlToMailUrls()
    global $base_path;
    $original_base_path = $base_path;
    $base_path = '/';

    $variables = [
      '@form-url' => 'https://www.example.com/contact',
      '@sender-url' => 'https://www.example.com/user/123',
      '@sender-name' => $this->randomString(),
    ];

    $plain = "In HTML, ampersand must be written as &amp;.\nI saw your house and <wow> it is great. There is too much to say about that beautiful building, it will never fit on one line of text.\nIf a<b and b<c then a<c.";
    $template = "@sender-name (@sender-url) sent a message using the contact form at @form-url.";
    $markup = new FormattableMarkup($template, $variables);

    $message = [
      'body' => [
        $plain,
        $markup,
      ],
    ];

    /** @var \Symfony\Component\Mailer\MailerInterface|\PHPUnit\Framework\MockObject\MockObject */
    $mailer = $this->getMockBuilder(MailerInterface::class)->getMock();

    /** @var \Psr\Log\LoggerInterface|\PHPUnit\Framework\MockObject\MockObject */
    $logger = $this->getMockBuilder(LoggerInterface::class)->getMock();

    $plugin = new SymfonyMailer($logger, $mailer);
    $message = $plugin->format($message);

    $expect = MailFormatHelper::wrapMail($plain . "\n\n" . strtr($template, $variables) . "\n");
    $this->assertEquals($expect, $message['body']);

    $base_path = $original_base_path;
  }

  /**
   * Tests sending a mail using a From address with a comma in it.
   *
   * @covers ::mail
   */
  public function testMail() {
    // Setup a mail message.
    $message = [
      'id' => 'example_key',
      'module' => 'example',
      'key' => 'key',
      'to' => 'to@example.org',
      'from' => 'from@example.org',
      'reply-to' => 'from@example.org',
      'langcode' => 'en',
      'params' => [],
      'send' => TRUE,
      'subject' => "test\r\nsubject",
      'body' => '',
      'headers' => [
        'MIME-Version' => '1.0',
        'Content-Type' => 'text/plain; charset=UTF-8; format=flowed; delsp=yes',
        'Content-Transfer-Encoding' => '8Bit',
        'X-Mailer' => 'Drupal',
        'From' => '"Foo, Bar, and Baz" <from@example.org>',
        'Reply-to' => 'from@example.org',
        'Return-Path' => 'from@example.org',
      ],
    ];

    // Verify we use line endings consistent with the PHP mail() function, which
    // changed with PHP 8. See:
    // - https://www.drupal.org/node/3270647
    // - https://bugs.php.net/bug.php?id=81158
    $line_end = "\r\n";

    /** @var \Symfony\Component\Mailer\MailerInterface|\PHPUnit\Framework\MockObject\MockObject */
    $mailer = $this->getMockBuilder(MailerInterface::class)->getMock();
    $mailer->expects($this->once())->method('send')
      ->with(
        $this->logicalAnd(
          $this->callback(fn (Email $email) =>
            $email->getHeaders()->get('mime-version')->getBodyAsString() === '1.0'
          ),
          $this->callback(fn (Email $email) =>
            $email->getHeaders()->has('content-type') === FALSE
          ),
          $this->callback(fn (Email $email) =>
            $email->getHeaders()->has('content-transfer-encoding') === FALSE
          ),
          $this->callback(fn (Email $email) =>
            $email->getHeaders()->get('x-mailer')->getBodyAsString() === 'Drupal'
          ),
          $this->callback(fn (Email $email) =>
            $email->getHeaders()->get('from')->getBodyAsString() === '"Foo, Bar, and Baz" <from@example.org>'
          ),
          $this->callback(fn (Email $email) =>
            $email->getHeaders()->get('reply-to')->getBodyAsString() === 'from@example.org'
          ),
          $this->callback(fn (Email $email) =>
            $email->getHeaders()->get('to')->getBodyAsString() === 'to@example.org'
          ),
          $this->callback(fn (Email $email) =>
            $email->getHeaders()->get('subject')->getBodyAsString() === "=?utf-8?Q?test?=$line_end =?utf-8?Q?subject?="
          ),
          $this->callback(fn (Email $email) =>
            $email->getTextBody() === ''
          )
        )
      );

    /** @var \Psr\Log\LoggerInterface|\PHPUnit\Framework\MockObject\MockObject */
    $logger = $this->getMockBuilder(LoggerInterface::class)->getMock();

    $plugin = new SymfonyMailer($logger, $mailer);
    $this->assertTrue($plugin->mail($message));
  }

}
