<?php
/**
 * WordPress mail interception for Email Delivery.
 *
 * @package EmailSmtp
 * @since 1.1.0
 */

namespace EmailSmtp\Includes;

use EmailSmtp\Module;
use WP_PowerSuite\Core\OptionStoreModuleSettingsRepository;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

final class MailInterceptor {

	public static function register(): void {
		// Keep a high-but-not-absurd priority so we win over typical SMTP plugins when we are responsible,
		// without using an extreme sentinel like 999999.
		$priority = (int) apply_filters( 'wp_powersuite_smtp_phpmailer_priority', 100 );
		if ( ! has_action( 'phpmailer_init', [ self::class, 'configure_phpmailer' ] ) ) {
			add_action( 'phpmailer_init', [ self::class, 'configure_phpmailer' ], $priority );
		}
		if ( ! has_filter( 'wp_mail', [ self::class, 'filter_wp_mail' ] ) ) {
			add_filter( 'wp_mail', [ self::class, 'filter_wp_mail' ], 5, 1 );
		}
		if ( ! has_filter( 'pre_wp_mail', [ EmailRouter::class, 'maybe_send_via_api' ] ) ) {
			add_filter( 'pre_wp_mail', [ EmailRouter::class, 'maybe_send_via_api' ], 10, 2 );
		}
		// Priority 6: after Email Logs PHPMailer capture (5), before mark_sent/mark_failed (10).
		if ( ! has_action( 'wp_mail_succeeded', [ self::class, 'on_mail_finished' ] ) ) {
			add_action( 'wp_mail_succeeded', [ self::class, 'on_mail_finished' ], 6 );
		}
		if ( ! has_action( 'wp_mail_failed', [ self::class, 'on_mail_finished' ] ) ) {
			add_action( 'wp_mail_failed', [ self::class, 'on_mail_finished' ], 6 );
		}
	}

	/**
	 * @param array<string, mixed> $args
	 * @return array<string, mixed>
	 */
	public static function filter_wp_mail( array $args ): array {
		if ( apply_filters( 'wp_powersuite_skip_smtp_mail_args', false, $args ) ) {
			return $args;
		}
		if ( ! OptionStoreModuleSettingsRepository::isModuleEnabled( 'email-smtp' ) ) {
			return $args;
		}

		self::register();

		$base     = EmailRouter::get_settings();
		$draft    = EmailMessage::from_wp_mail_args( $args );
		$settings = ConnectionStore::settings_for_from( $base, $draft->get_from_email() );
		$provider = ProviderRegistry::get( (string) ( $settings['provider'] ?? 'wordpress' ) );

		// Apply shared sender identity headers for the connection matched by From email.
		$headers = \WP_PowerSuite\Email\MailHeaders::normalizeLines( $args['headers'] ?? [] );
		$headers = \WP_PowerSuite\Email\MailHeaders::dropInvalidAddressHeaders( $headers );
		$args['headers'] = self::append_sender_headers( $headers, $settings );

		// Orchestrated SMTP/API sends short-circuit in pre_wp_mail. PHPMailer hook remains
		// registered as a safety net in case another plugin clears pre_wp_mail short-circuiting.
		self::register();

		return $args;
	}

	/**
	 * @param \PHPMailer\PHPMailer\PHPMailer $phpmailer
	 */
	public static function configure_phpmailer( $phpmailer ): void {
		if ( ! OptionStoreModuleSettingsRepository::isModuleEnabled( 'email-smtp' ) ) {
			return;
		}
		if ( ! apply_filters( 'wp_powersuite_smtp_should_configure', true, $phpmailer ) ) {
			return;
		}

		$base     = EmailRouter::get_settings();
		$from     = is_object( $phpmailer ) && isset( $phpmailer->From ) ? (string) $phpmailer->From : '';
		$settings = ConnectionStore::settings_for_from( $base, $from );
		$provider = ProviderRegistry::get( (string) ( $settings['provider'] ?? 'wordpress' ) );
		$uses_smtp = SmtpRelayProfiles::connection_uses_smtp( $settings, $provider );

		// PHP Mail connection: optionally set envelope sender (-f) without changing transport.
		if ( ! $uses_smtp ) {
			if ( $provider && $provider->get_id() === 'wordpress' ) {
				self::maybe_set_php_mail_envelope( $phpmailer, $settings, $from );
				DeliveryAttemptContext::merge(
					[
						'transport'        => 'php_mail',
						'provider'         => 'wordpress',
						'phpmailer_mailer' => is_object( $phpmailer ) ? (string) ( $phpmailer->Mailer ?? 'mail' ) : 'mail',
						'envelope_sender'  => is_object( $phpmailer ) ? sanitize_email( (string) ( $phpmailer->Sender ?? '' ) ) : '',
					]
				);
			}
			return;
		}

		$provider_id = sanitize_key( (string) ( $settings['provider'] ?? 'smtp' ) );
		$settings    = SmtpRelayProfiles::merge_smtp_settings( $settings, $provider_id );
		$bag         = SettingsSchema::provider_bag( $settings, 'smtp' );
		$merged      = array_merge( $settings, $bag );

		// Safety net: if an SMTP connection is selected, never leave PHPMailer on mail/sendmail.
		// Empty host still forces SMTP so the send fails instead of silently using Postfix.
		try {
			if ( empty( $bag['smtp_host'] ) ) {
				$phpmailer->isSMTP();
				$phpmailer->Host = '';
				DeliveryAttemptContext::merge(
					[
						'transport'        => 'smtp',
						'provider'         => $provider_id,
						'phpmailer_mailer' => 'smtp',
						'orchestrated'     => (int) DeliveryAttemptContext::get( 'orchestrated', 0 ),
					]
				);
				return;
			}

			self::ensure_module_loaded();
			Module::configurePhpMailerFromSettings( $phpmailer, $merged );
			$envelope = is_object( $phpmailer ) ? sanitize_email( (string) ( $phpmailer->Sender ?? '' ) ) : '';
			if ( $envelope === '' && is_email( $from ) ) {
				$envelope = sanitize_email( $from );
			}
			DeliveryAttemptContext::merge(
				[
					'transport'        => 'smtp',
					'provider'         => $provider_id,
					'phpmailer_mailer' => 'smtp',
					'envelope_sender'  => $envelope,
				]
			);
		} catch ( \Exception $e ) {
			// Still force SMTP so WordPress cannot fall back to local mail after a config exception.
			$phpmailer->isSMTP();
			if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
				$brand = function_exists( 'wp_powersuite_get_plugin_name' ) ? wp_powersuite_get_plugin_name() : 'WP PowerSuite';
				error_log( $brand . ' Email Delivery: ' . ErrorNormalizer::redact( $e->getMessage() ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
			}
		}
	}

	/**
	 * Clear request context after WordPress finishes (PHP Mail path).
	 *
	 * @param mixed $unused
	 */
	public static function on_mail_finished( $unused = null ): void {
		unset( $unused );
		if ( (int) DeliveryAttemptContext::get( 'orchestrated', 0 ) === 1 ) {
			DeliveryAttemptContext::clear();
			return;
		}
		// Publish late enrichment for the PHP Mail path (pre_wp_mail returned null).
		$log_data = DeliveryAttemptContext::for_log();
		if ( $log_data !== [] ) {
			$log_data = apply_filters( 'wp_powersuite_email_delivery_log_data', $log_data, null, null );
			do_action( 'wp_powersuite_email_delivery_log_enrich', $log_data, null, null );
		}
		DeliveryAttemptContext::clear();
	}

	/**
	 * Lazy-load Module when SMTP password decrypt / PHPMailer config is required.
	 */
	private static function ensure_module_loaded(): void {
		if ( class_exists( Module::class, false ) ) {
			return;
		}
		$module_file = dirname( __DIR__ ) . DIRECTORY_SEPARATOR . 'module.php';
		if ( is_readable( $module_file ) ) {
			require_once $module_file;
		}
	}

	/**
	 * @param \PHPMailer\PHPMailer\PHPMailer $phpmailer
	 * @param array<string, mixed>           $settings
	 */
	private static function maybe_set_php_mail_envelope( $phpmailer, array $settings, string $from ): void {
		$set_return_path = ! isset( $settings['set_return_path'] )
			|| ( ! empty( $settings['set_return_path'] ) && (string) $settings['set_return_path'] !== '0' );
		if ( ! $set_return_path ) {
			return;
		}

		$return_path = sanitize_email( (string) ( $settings['return_path'] ?? '' ) );
		if ( ! is_email( $return_path ) ) {
			$return_path = sanitize_email( (string) ( $settings['from_email'] ?? '' ) );
		}
		if ( ! is_email( $return_path ) ) {
			$return_path = sanitize_email( $from );
		}
		if ( is_email( $return_path ) && empty( $phpmailer->Sender ) ) {
			$phpmailer->Sender = $return_path;
		}
	}

	/**
	 * @param list<string>         $headers
	 * @param array<string, mixed> $settings
	 * @return list<string>
	 */
	private static function append_sender_headers( array $headers, array $settings ): array {
		$override = ! empty( $settings['override_from'] ) && (string) $settings['override_from'] !== '0';
		$override = $override
			|| ( ! empty( $settings['force_from_email'] ) && (string) $settings['force_from_email'] !== '0' );

		$has_from = false;
		foreach ( $headers as $header ) {
			if ( stripos( (string) $header, 'from:' ) === 0 ) {
				$has_from = true;
				break;
			}
		}

		// Match legacy behaviour: only inject From when override is enabled and none is present.
		// PHPMailer configuration applies force rules with plugin-custom-From awareness.
		if ( $override && ! $has_from ) {
			$from_email = sanitize_email( (string) ( $settings['from_email'] ?? '' ) );
			if ( ! is_email( $from_email ) ) {
				$from_email = sanitize_email( (string) get_option( 'admin_email' ) );
			}
			if ( is_email( $from_email ) ) {
				$from_name = SmtpSettingsHelper::sanitizeFromName( (string) ( $settings['from_name'] ?? '' ) );
				$headers[] = sprintf( 'From: %s <%s>', $from_name, $from_email );
			}
		}

		$reply_email = sanitize_email( (string) ( $settings['reply_to_email'] ?? '' ) );
		$has_reply   = false;
		foreach ( $headers as $header ) {
			if ( stripos( (string) $header, 'reply-to:' ) === 0 ) {
				$has_reply = true;
				break;
			}
		}
		if ( is_email( $reply_email ) && ! $has_reply ) {
			$reply_name = SmtpSettingsHelper::sanitizeFromName( (string) ( $settings['reply_to_name'] ?? '' ) );
			$headers[]  = sprintf( 'Reply-To: %s <%s>', $reply_name, $reply_email );
		}

		return $headers;
	}
}
