<?php
/**
 * wp_mail() delivery through the Brevo SMTP relay.
 *
 * @package RewindBrevoFormBridge
 */

namespace RewindBrevoFormBridge\Emails;

use RewindBrevoFormBridge\Admin\Settings;

defined( 'ABSPATH' ) || exit;

/**
 * Points PHPMailer at smtp-relay.brevo.com — the classic SMTP route, for
 * sites that would rather authenticate with an SMTP key than send over the
 * HTTP API.
 *
 * Prefer the API transport where you can: it rides HTTPS 443, which is
 * open everywhere, while many hosts block outbound 587 and 465 and the
 * failure looks like "email just stopped". This class exists because some
 * accounts and policies still require a real SMTP conversation.
 *
 * @since 0.5.0
 */
class SmtpRelay {

	/**
	 * Brevo's SMTP relay host.
	 *
	 * @var string
	 */
	const HOST = 'smtp-relay.brevo.com';

	/**
	 * Ports Brevo accepts. 587 and 2525 use STARTTLS, 465 implicit TLS.
	 *
	 * @var int[]
	 */
	const PORTS = array( 587, 465, 2525 );

	/**
	 * Settings storage service.
	 *
	 * @var Settings
	 */
	private Settings $settings;

	/**
	 * Constructor.
	 *
	 * @since 0.5.0
	 * @param Settings $settings Settings storage service.
	 */
	public function __construct( Settings $settings ) {
		$this->settings = $settings;
	}

	/**
	 * Attach the PHPMailer and sender hooks.
	 *
	 * @since 0.5.0
	 * @return void
	 */
	public function register_hooks(): void {
		add_action( 'phpmailer_init', array( $this, 'configure' ) );
		add_filter( 'wp_mail_from', array( $this, 'filter_from_email' ) );
		add_filter( 'wp_mail_from_name', array( $this, 'filter_from_name' ) );
		add_action( 'wp_mail_failed', array( $this, 'record_failure' ) );
		add_action( 'wp_mail_succeeded', array( $this, 'record_success' ) );
	}

	/**
	 * Whether SMTP delivery is configured and switched on.
	 *
	 * @since 0.5.0
	 * @return bool
	 */
	public function is_active(): bool {
		return (bool) $this->settings->get( 'email_enabled', false )
			&& 'smtp' === $this->settings->get( 'email_transport', 'api' )
			&& '' !== $this->settings->smtp_key()
			&& '' !== trim( (string) $this->settings->get( 'smtp_login', '' ) );
	}

	/**
	 * Point PHPMailer at the Brevo relay.
	 *
	 * @since 0.5.0
	 * @param \PHPMailer\PHPMailer\PHPMailer $phpmailer The mailer instance,
	 *                                                  passed by reference.
	 * @return void
	 */
	public function configure( $phpmailer ): void {
		if ( ! $this->is_active() || ! is_object( $phpmailer ) ) {
			return;
		}

		$port = (int) $this->settings->get( 'smtp_port', 587 );

		if ( ! in_array( $port, self::PORTS, true ) ) {
			$port = 587;
		}

		$phpmailer->isSMTP();
		$phpmailer->Host       = self::HOST;      // phpcs:ignore WordPress.NamingConventions.ValidVariableName.NotSnakeCaseMemberVar -- PHPMailer's public API.
		$phpmailer->Port       = $port;           // phpcs:ignore WordPress.NamingConventions.ValidVariableName.NotSnakeCaseMemberVar -- PHPMailer's public API.
		$phpmailer->SMTPAuth   = true;            // phpcs:ignore WordPress.NamingConventions.ValidVariableName.NotSnakeCaseMemberVar -- PHPMailer's public API.
		$phpmailer->Username   = trim( (string) $this->settings->get( 'smtp_login', '' ) ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.NotSnakeCaseMemberVar -- PHPMailer's public API.
		$phpmailer->Password   = $this->settings->smtp_key();                               // phpcs:ignore WordPress.NamingConventions.ValidVariableName.NotSnakeCaseMemberVar -- PHPMailer's public API.
		$phpmailer->SMTPSecure = ( 465 === $port ) ? 'ssl' : 'tls';                          // phpcs:ignore WordPress.NamingConventions.ValidVariableName.NotSnakeCaseMemberVar -- PHPMailer's public API.

		// Never downgrade to a plaintext session: the SMTP key is a credential.
		$phpmailer->SMTPAutoTLS = true; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.NotSnakeCaseMemberVar -- PHPMailer's public API.

		$reply_to = (string) $this->settings->get( 'email_reply_to', '' );

		if ( is_email( $reply_to ) && 0 === count( $phpmailer->getReplyToAddresses() ) ) {
			$phpmailer->addReplyTo( $reply_to );
		}

		/**
		 * Fires after the Brevo relay has been applied to PHPMailer.
		 *
		 * Last chance to adjust the connection — the credentials are already
		 * set, so anything read here must stay out of logs.
		 *
		 * @since 0.5.0
		 * @param \PHPMailer\PHPMailer\PHPMailer $phpmailer The configured mailer.
		 */
		do_action( 'rbfb_smtp_configured', $phpmailer );
	}

	/**
	 * Force the saved sender address.
	 *
	 * Brevo refuses a send whose From address is not a registered sender,
	 * so with "always use this sender" on, the saved address wins outright.
	 * With it off, only WordPress's own wordpress@host default is replaced —
	 * an address another plugin set deliberately is left alone.
	 *
	 * @since 0.5.0
	 * @param string $email Address wp_mail() resolved.
	 * @return string
	 */
	public function filter_from_email( $email ): string {
		$email = (string) $email;

		if ( ! $this->is_active() ) {
			return $email;
		}

		$saved = (string) $this->settings->get( 'email_from_email', '' );

		if ( ! is_email( $saved ) ) {
			return $email;
		}

		$force = (bool) $this->settings->get( 'email_force_from', true );

		return ( $force || $this->is_wp_default_from( $email ) ) ? $saved : $email;
	}

	/**
	 * Force the saved sender name, on the same terms as the address.
	 *
	 * @since 0.5.0
	 * @param string $name Name wp_mail() resolved.
	 * @return string
	 */
	public function filter_from_name( $name ): string {
		$name = (string) $name;

		if ( ! $this->is_active() ) {
			return $name;
		}

		$saved = (string) $this->settings->get( 'email_from_name', '' );

		if ( '' === $saved ) {
			return $name;
		}

		$force = (bool) $this->settings->get( 'email_force_from', true );

		return ( $force || 'WordPress' === $name ) ? $saved : $name;
	}

	/**
	 * Whether an address is the wordpress@host default wp_mail() invents
	 * when no From header was supplied.
	 *
	 * @since 0.5.0
	 * @param string $email Address to test.
	 * @return bool
	 */
	private function is_wp_default_from( string $email ): bool {
		$sitename = (string) wp_parse_url( network_home_url(), PHP_URL_HOST );

		if ( 'www.' === substr( $sitename, 0, 4 ) ) {
			$sitename = substr( $sitename, 4 );
		}

		return strtolower( $email ) === strtolower( 'wordpress@' . $sitename );
	}

	/**
	 * Remember a relay failure for the Email settings screen.
	 *
	 * @since 0.5.0
	 * @param \WP_Error $error The wp_mail() failure.
	 * @return void
	 */
	public function record_failure( $error ): void {
		if ( ! $this->is_active() || ! is_wp_error( $error ) ) {
			return;
		}

		MailStatus::record_failure( $error->get_error_message() );
	}

	/**
	 * Remember an accepted relay send.
	 *
	 * @since 0.5.0
	 * @return void
	 */
	public function record_success(): void {
		if ( ! $this->is_active() ) {
			return;
		}

		MailStatus::record_success();
	}
}
