<?php
/**
 * AJAX handlers for Mail Doctor admin.
 *
 * @package MailDoctor
 */

namespace MailDoctor\Admin\Modules;

use MailDoctor\Helpers\Dependencies;
use MailDoctor\Helpers\Settings;
use MailDoctor\Logger;
use MailDoctor\Mailer;
use MailDoctor\Transports\Generic_Http;
use MailDoctor\Transports\Postmark;
use MailDoctor\Transports\SendGrid;
use MailDoctor\Transports\Ses;
use MailDoctor\Transports\SparkPost;
use MailDoctor\Woo;

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

/**
 * Provides AJAX responders and transport diagnostics.
 */
trait Ajax_Trait {

	/**
	 * Write debug output when debug logging is enabled.
	 *
	 * @param string              $message Message to log.
	 * @param array<string,mixed> $context Additional context.
	 * @return void
	 */
	protected static function debug_log( string $message, array $context = array() ): void {
		if ( ! Settings::debug_logging_enabled() ) {
			return;
		}

		$line = '[MailDoctor AJAX] ' . $message;

		if ( ! empty( $context ) ) {
			$encoded = wp_json_encode( $context );
			if ( false !== $encoded ) {
				$line .= ' :: ' . $encoded;
			}
		}

		error_log( $line ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
	}

	/**
	 * AJAX handler: send a real test email and surface diagnostics.
	 *
	 * @return void
	 */
	public static function ajax_send_test_email(): void {
check_ajax_referer( 'mail_doctor_test_email_nonce' );
		self::assert_capability();

		$email = isset( $_POST['email'] ) ? sanitize_email( wp_unslash( $_POST['email'] ) ) : '';

		if ( ! is_email( $email ) ) {
			wp_send_json_error(
				array(
					'message' => esc_html__( 'Please provide a valid recipient email address.', 'mail-doctor' ),
				),
				400
			);
		}

		$profile = isset( $_POST['provider'] ) ? sanitize_text_field( wp_unslash( $_POST['provider'] ) ) : 'default';

		$overrides = array(
			'host'                   => isset( $_POST['host'] ) ? sanitize_text_field( wp_unslash( $_POST['host'] ) ) : null,
			'port'                   => isset( $_POST['port'] ) ? sanitize_text_field( wp_unslash( $_POST['port'] ) ) : null,
			'encryption'             => isset( $_POST['encryption'] ) ? sanitize_key( wp_unslash( $_POST['encryption'] ) ) : null,
			'username'               => isset( $_POST['username'] ) ? sanitize_text_field( wp_unslash( $_POST['username'] ) ) : null,
			'password'               => isset( $_POST['password'] ) ? trim( (string) wp_unslash( $_POST['password'] ) ) : null, // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Passwords may contain any character; sanitize_text_field() would corrupt valid values.
			'from_email'             => isset( $_POST['from_email'] ) ? sanitize_email( wp_unslash( $_POST['from_email'] ) ) : null,
			'from_name'              => isset( $_POST['from_name'] ) ? sanitize_text_field( wp_unslash( $_POST['from_name'] ) ) : null,
			'transport'              => isset( $_POST['transport'] ) ? sanitize_key( wp_unslash( $_POST['transport'] ) ) : null,
			'mailgun_api_key'        => isset( $_POST['mailgun_api_key'] ) ? sanitize_text_field( wp_unslash( $_POST['mailgun_api_key'] ) ) : null,
			'mailgun_api_domain'     => isset( $_POST['mailgun_api_domain'] ) ? sanitize_text_field( wp_unslash( $_POST['mailgun_api_domain'] ) ) : null,
			'mailgun_api_endpoint'   => isset( $_POST['mailgun_api_endpoint'] ) ? sanitize_text_field( wp_unslash( $_POST['mailgun_api_endpoint'] ) ) : null,
			'sendgrid_api_key'       => isset( $_POST['sendgrid_api_key'] ) ? sanitize_text_field( wp_unslash( $_POST['sendgrid_api_key'] ) ) : null,
			'postmark_api_token'     => isset( $_POST['postmark_api_token'] ) ? sanitize_text_field( wp_unslash( $_POST['postmark_api_token'] ) ) : null,
			'sparkpost_api_key'      => isset( $_POST['sparkpost_api_key'] ) ? sanitize_text_field( wp_unslash( $_POST['sparkpost_api_key'] ) ) : null,
			'sparkpost_api_endpoint' => isset( $_POST['sparkpost_api_endpoint'] ) ? sanitize_text_field( wp_unslash( $_POST['sparkpost_api_endpoint'] ) ) : null,
			'ses_api_key'            => isset( $_POST['ses_api_key'] ) ? sanitize_text_field( wp_unslash( $_POST['ses_api_key'] ) ) : null,
			'ses_api_secret'         => isset( $_POST['ses_api_secret'] ) ? sanitize_text_field( wp_unslash( $_POST['ses_api_secret'] ) ) : null,
			'ses_api_session_token'  => isset( $_POST['ses_api_session_token'] ) ? sanitize_text_field( wp_unslash( $_POST['ses_api_session_token'] ) ) : null,
			'ses_api_region'         => isset( $_POST['ses_api_region'] ) ? sanitize_text_field( wp_unslash( $_POST['ses_api_region'] ) ) : null,
			'generic_api_endpoint'   => isset( $_POST['generic_api_endpoint'] ) ? esc_url_raw( wp_unslash( $_POST['generic_api_endpoint'] ) ) : null,
			'generic_api_method'     => isset( $_POST['generic_api_method'] ) ? sanitize_text_field( wp_unslash( $_POST['generic_api_method'] ) ) : null,
			'generic_api_auth_token' => isset( $_POST['generic_api_auth_token'] ) ? sanitize_text_field( wp_unslash( $_POST['generic_api_auth_token'] ) ) : null,
		);

		self::debug_log(
			'ajax_send_test_email invoked',
			array(
				'email'     => $email,
				'profile'   => $profile,
				'transport' => Settings::get( 'transport', 'smtp' ),
			)
		);

		$connection = self::perform_connection_test( $profile, $overrides );
		self::debug_log(
			'connection payload debug',
			array(
				'profile'   => $profile,
				'overrides' => array_keys(
					array_filter(
						$overrides,
						static function ( $value ) {
							return ! empty( $value );
						}
					)
				),
			)
		);

		self::debug_log(
			'test connection result',
			array(
				'success' => ! empty( $connection['success'] ),
				'message' => $connection['message'] ?? '',
			)
		);

		if ( empty( $connection['success'] ) ) {
			$host     = $connection['data']['host'] ?? Settings::get( 'smtp_host', '' );
			$port     = $connection['data']['port'] ?? Settings::get( 'smtp_port', 0 );
			$username = Settings::get( 'smtp_username', '' );
			$debug    = array();

			if ( ! empty( $connection['data']['debug'] ) && is_array( $connection['data']['debug'] ) ) {
				$debug = $connection['data']['debug'];
			}

			$details = array_filter(
				array(
					/* translators: %s: Hostname */
					$host ? sprintf( __( 'Host: %s', 'mail-doctor' ), $host ) : '',
					/* translators: %d: Port number */
					$port ? sprintf( __( 'Port: %d', 'mail-doctor' ), (int) $port ) : '',
					/* translators: %s: Username */
					$username ? sprintf( __( 'Username: %s', 'mail-doctor' ), $username ) : '',
				)
			);

			$data = array(
				'badges'   => array(
					'smtp' => false,
					'tls'  => self::badge_from_encryption( $connection['data']['encryption'] ?? '' ),
					'auth' => (bool) ( $connection['data']['auth'] ?? false ),
					'sent' => false,
				),
				'latency'  => 0,
				'message'  => esc_html( $connection['message'] ?? __( 'Unable to connect to the SMTP server. Check host, port, encryption, and firewall settings.', 'mail-doctor' ) ),
				'provider' => '',
				'details'  => $details,
				'debug'    => $debug,
			);

			wp_send_json_success( $data );
		}

		$mapped_overrides  = self::map_connection_overrides( $overrides );
		$mock_option       = null;
		$option_hook       = null;

		if ( ! empty( $mapped_overrides ) ) {
			$original_settings = Settings::all();
			$applied_settings  = array_merge( $original_settings, $mapped_overrides );
			$mock_option       = $applied_settings;
			
			$option_hook = static function() use ( &$mock_option ) {
				return $mock_option;
			};
			add_filter( 'pre_option_' . Settings::OPTION_KEY, $option_hook );
		}

		$blogname = wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES );
		$subject  = sprintf(
			/* translators: %s: Site name. */
			__( 'Mail Doctor Test Email – %s', 'mail-doctor' ),
			$blogname ? $blogname : wp_parse_url( home_url(), PHP_URL_HOST )
		);

		$intro = __( 'This test message was sent by Mail Doctor to confirm your SMTP configuration.', 'mail-doctor' );
		$time  = sprintf(
			/* translators: %s: formatted datetime. */
			__( 'Sent at %s.', 'mail-doctor' ),
			wp_date( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ) )
		);

		$message = sprintf(
			'<p>%1$s</p><p>%2$s</p><hr /><p>%3$s</p>',
			esc_html( $intro ),
			esc_html( $time ),
			esc_html__( 'If you did not request this test, you can safely ignore this email.', 'mail-doctor' )
		);

		$latency  = 0;
		$sent     = false;
		$log      = null;
		$status   = 'failed';
		$error    = '';
		$provider = '';

		try {
			Mailer::set_event_context( 'maildoctor-test' );
			Mailer::force_next_override();

			Mailer::set_retry_context(
				array(
					'smtp_profile' => $profile && 'default' !== $profile ? $profile : Settings::get_active_profile(),
				)
			);
			self::debug_log(
				'Mailer context configured',
				array(
					'profile'        => $profile,
					'active_profile' => Settings::get_active_profile(),
				)
			);

			$previous_log = Logger::latest_by_event( 'maildoctor-test' );
			$previous_id  = $previous_log['id'] ?? 0;

			$start   = microtime( true );
			$sent    = wp_mail( $email, $subject, $message, array( 'Content-Type: text/html; charset=UTF-8' ) );
			$latency = (int) round( ( microtime( true ) - $start ) * 1000 );
			$log     = Logger::latest_by_event( 'maildoctor-test' );
			$log_id  = $log['id'] ?? 0;

			if ( $log_id && $log_id <= $previous_id ) {
				$log = null;
			}

			$status   = $log['status'] ?? ( $sent ? 'sent' : 'failed' );
			$error    = $log['error'] ?? '';
			$provider = $log['provider_code'] ?? '';

			if ( isset( $log['duration_ms'] ) && null !== $log['duration_ms'] ) {
				$latency = (int) $log['duration_ms'];
			}

			self::debug_log(
				'wp_mail result',
				array(
					'sent'     => $sent,
					'status'   => $status,
					'error'    => $error,
					'provider' => $provider,
					'log_id'   => $log_id ?? 0,
				)
			);
		} catch ( \Throwable $throwable ) {
			$sent   = false;
			$status = 'failed';
			$error  = $throwable->getMessage();
			self::debug_log(
				'ajax_send_test_email exception',
				array(
					'error' => $error,
					'class' => get_class( $throwable ),
				)
			);
		} finally {
			Mailer::clear_event_context();
			Mailer::cleanup_context();

			if ( ! empty( $mapped_overrides ) && null !== $option_hook ) {
				remove_filter( 'pre_option_' . Settings::OPTION_KEY, $option_hook );
			}
		}

		$queued    = ( 'queued' === $status );
		$delivered = $sent;

		if ( $queued && $sent ) {
			$status    = 'queued';
			$delivered = true;
		} elseif ( 'sent' !== $status ) {
			$sent      = false;
			$delivered = false;
		}

		$badges = array(
			'smtp' => (bool) $connection['success'],
			'tls'  => self::badge_from_encryption( $connection['data']['encryption'] ?? '' ),
			'auth' => (bool) ( $connection['data']['auth'] ?? false ),
			'sent' => $delivered,
		);

		if ( $queued && $delivered ) {
			$message_text = esc_html__( 'Test email queued for delivery. Check the email log shortly for the final status.', 'mail-doctor' );
		} elseif ( $sent ) {
			$message_text = esc_html__( 'Test email sent successfully.', 'mail-doctor' );
		} else {
			$message_text = $error ? esc_html( $error ) : esc_html__( 'Delivery failed. Check the email log for details.', 'mail-doctor' );
		}

		$data = array(
			'badges'   => $badges,
			'latency'  => $latency,
			'message'  => $message_text,
			'provider' => $provider,
			'status'   => $status,
		);

		self::debug_log( 'ajax_send_test_email response', $data );

		wp_send_json_success( $data );
	}

	/**
	 * AJAX handler: perform SMTP connection test without sending mail.
	 *
	 * @return void
	 */
	public static function ajax_test_connection(): void {
check_ajax_referer( 'mail_doctor_connection_nonce' );
		self::assert_capability();

		$profile   = isset( $_POST['provider'] ) ? sanitize_text_field( wp_unslash( $_POST['provider'] ) ) : 'default';
		$overrides = array(
			'host'                   => isset( $_POST['host'] ) ? sanitize_text_field( wp_unslash( $_POST['host'] ) ) : null,
			'port'                   => isset( $_POST['port'] ) ? sanitize_text_field( wp_unslash( $_POST['port'] ) ) : null,
			'encryption'             => isset( $_POST['encryption'] ) ? sanitize_key( wp_unslash( $_POST['encryption'] ) ) : null,
			'username'               => isset( $_POST['username'] ) ? sanitize_text_field( wp_unslash( $_POST['username'] ) ) : null,
			'password'               => isset( $_POST['password'] ) ? trim( (string) wp_unslash( $_POST['password'] ) ) : null, // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Passwords may contain any character; sanitize_text_field() would corrupt valid values.
			'from_email'             => isset( $_POST['from_email'] ) ? sanitize_email( wp_unslash( $_POST['from_email'] ) ) : null,
			'from_name'              => isset( $_POST['from_name'] ) ? sanitize_text_field( wp_unslash( $_POST['from_name'] ) ) : null,
			'transport'              => isset( $_POST['transport'] ) ? sanitize_key( wp_unslash( $_POST['transport'] ) ) : null,
			'mailgun_api_key'        => isset( $_POST['mailgun_api_key'] ) ? sanitize_text_field( wp_unslash( $_POST['mailgun_api_key'] ) ) : null,
			'mailgun_api_domain'     => isset( $_POST['mailgun_api_domain'] ) ? sanitize_text_field( wp_unslash( $_POST['mailgun_api_domain'] ) ) : null,
			'mailgun_api_endpoint'   => isset( $_POST['mailgun_api_endpoint'] ) ? sanitize_text_field( wp_unslash( $_POST['mailgun_api_endpoint'] ) ) : null,
			'sendgrid_api_key'       => isset( $_POST['sendgrid_api_key'] ) ? sanitize_text_field( wp_unslash( $_POST['sendgrid_api_key'] ) ) : null,
			'postmark_api_token'     => isset( $_POST['postmark_api_token'] ) ? sanitize_text_field( wp_unslash( $_POST['postmark_api_token'] ) ) : null,
			'sparkpost_api_key'      => isset( $_POST['sparkpost_api_key'] ) ? sanitize_text_field( wp_unslash( $_POST['sparkpost_api_key'] ) ) : null,
			'sparkpost_api_endpoint' => isset( $_POST['sparkpost_api_endpoint'] ) ? sanitize_text_field( wp_unslash( $_POST['sparkpost_api_endpoint'] ) ) : null,
			'ses_api_key'            => isset( $_POST['ses_api_key'] ) ? sanitize_text_field( wp_unslash( $_POST['ses_api_key'] ) ) : null,
			'ses_api_secret'         => isset( $_POST['ses_api_secret'] ) ? sanitize_text_field( wp_unslash( $_POST['ses_api_secret'] ) ) : null,
			'ses_api_session_token'  => isset( $_POST['ses_api_session_token'] ) ? sanitize_text_field( wp_unslash( $_POST['ses_api_session_token'] ) ) : null,
			'ses_api_region'         => isset( $_POST['ses_api_region'] ) ? sanitize_text_field( wp_unslash( $_POST['ses_api_region'] ) ) : null,
			'generic_api_endpoint'   => isset( $_POST['generic_api_endpoint'] ) ? esc_url_raw( wp_unslash( $_POST['generic_api_endpoint'] ) ) : null,
			'generic_api_method'     => isset( $_POST['generic_api_method'] ) ? sanitize_text_field( wp_unslash( $_POST['generic_api_method'] ) ) : null,
			'generic_api_auth_token' => isset( $_POST['generic_api_auth_token'] ) ? sanitize_text_field( wp_unslash( $_POST['generic_api_auth_token'] ) ) : null,
		);

		$connection = self::perform_connection_test( $profile, $overrides );

		$success    = ! empty( $connection['success'] );
		set_transient( 'mail_doctor_connection_status', $success ? 'connected' : 'disconnected', DAY_IN_SECONDS );

		$encryption = $connection['data']['encryption'] ?? '';
		$auth       = $connection['data']['auth'] ?? null;
		$sent       = isset( $connection['data']['sent'] ) ? (bool) $connection['data']['sent'] : $success;

		$badges = array(
			'smtp' => $success,
			'tls'  => $encryption ? self::badge_from_encryption( $encryption ) : false,
			'auth' => null !== $auth ? (bool) $auth : false,
			'sent' => $sent,
		);

		$effective_settings = array_merge( Settings::all(), self::map_connection_overrides( $overrides ) );
		$transport          = $effective_settings['transport'] ?? 'smtp';
		$host               = $connection['data']['host'] ?? ( $effective_settings['smtp_host'] ?? '' );
		$port               = $connection['data']['port'] ?? ( $effective_settings['smtp_port'] ?? 587 );
		$encryption         = $connection['data']['encryption'] ?? ( $effective_settings['smtp_encryption'] ?? 'none' );
		$username           = $effective_settings['smtp_username'] ?? '';
		$is_auth            = ! empty( $username );

		$data = array(
			'badges'            => $badges,
			'latency'           => 0,
			'message'           => esc_html( $connection['message'] ?? __( 'Connection attempt finished.', 'mail-doctor' ) ),
			'provider'          => '',
			'details'           => array_filter(
				array(
					/* translators: %s: Hostname */
					$host ? sprintf( __( 'Host: %s', 'mail-doctor' ), $host ) : '',
					/* translators: %d: Port number */
					$port ? sprintf( __( 'Port: %d', 'mail-doctor' ), (int) $port ) : '',
					/* translators: %s: Username */
					$username ? sprintf( __( 'Username: %s', 'mail-doctor' ), sanitize_text_field( $username ) ) : '',
				)
			),
			'debug'             => isset( $connection['data']['debug'] ) && is_array( $connection['data']['debug'] ) ? $connection['data']['debug'] : array(),
			'connection_params' => array(
				'transport'  => $transport,
				'host'       => $host,
				'port'       => (int) $port,
				'encryption' => $encryption,
				'username'   => $username,
				'auth'       => $is_auth,
			),
		);

		wp_send_json_success( $data );
	}

	/**
	 * AJAX handler: render WooCommerce email preview HTML.
	 *
	 * @return void
	 */
	public static function ajax_preview_woo_email(): void {
check_ajax_referer( 'mail_doctor_preview_email_nonce' );
		self::assert_capability();

		if ( ! Dependencies::is_woocommerce_available() ) {
			wp_send_json_error( array( 'message' => esc_html__( 'WooCommerce is not available.', 'mail-doctor' ) ), 400 );
		}

		$email_id = isset( $_POST['email'] ) ? sanitize_text_field( wp_unslash( $_POST['email'] ) ) : '';
		$order_id = isset( $_POST['order_id'] ) ? absint( $_POST['order_id'] ) : 0;

		if ( '' === $email_id ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Select a WooCommerce email to preview.', 'mail-doctor' ) ), 400 );
		}

		$result = Woo::get_preview( $email_id, $order_id ?: null );

		if ( is_wp_error( $result ) ) {
			wp_send_json_error( array( 'message' => $result->get_error_message() ), 400 );
		}

		wp_send_json_success(
			array(
				'html'    => isset( $result['html'] ) ? (string) $result['html'] : '',
				'orderId' => isset( $result['order_id'] ) ? (int) $result['order_id'] : null,
				'subject' => isset( $result['subject'] ) ? (string) $result['subject'] : '',
				'title'   => isset( $result['title'] ) ? (string) $result['title'] : '',
				'heading' => isset( $result['heading'] ) ? (string) $result['heading'] : '',
			)
		);
	}

	/**
	 * AJAX handler: send WooCommerce email preview to current user.
	 *
	 * @return void
	 */
	public static function ajax_send_preview_woo_email(): void {
check_ajax_referer( 'mail_doctor_preview_send_nonce' );
		self::assert_capability();

		if ( ! Dependencies::is_woocommerce_available() ) {
			wp_send_json_error( array( 'message' => esc_html__( 'WooCommerce is not available.', 'mail-doctor' ) ), 400 );
		}

		$email_id = isset( $_POST['email'] ) ? sanitize_text_field( wp_unslash( $_POST['email'] ) ) : '';
		$order_id = isset( $_POST['order_id'] ) ? absint( $_POST['order_id'] ) : 0;

		if ( '' === $email_id ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Select a WooCommerce email before sending a preview.', 'mail-doctor' ) ), 400 );
		}

		$result = Woo::send_preview( $email_id, $order_id ?: null );

		if ( is_wp_error( $result ) ) {
			wp_send_json_error( array( 'message' => $result->get_error_message() ), 400 );
		}

		wp_send_json_success(
			array(
				'sentTo'  => isset( $result['sent_to'] ) ? (string) $result['sent_to'] : '',
				'orderId' => isset( $result['order_id'] ) ? (int) $result['order_id'] : null,
				'html'    => isset( $result['html'] ) ? (string) $result['html'] : '',
			)
		);
	}

	/**

	/**
	 * Perform SMTP connection test for the selected profile.
	 *
	 * @param string               $profile   Profile identifier (default when empty).
	 * @param array<string, mixed> $overrides Override credentials.
	 * @return array<string, mixed>
	 */
	protected static function perform_connection_test( string $profile, array $overrides = array() ): array {
		$profile_key = trim( $profile ) !== '' ? sanitize_text_field( $profile ) : 'default';

		$applied_settings = array();
		$has_overrides    = ! empty( $overrides );
		$mapped           = array();
		$mock_option      = null;
		$option_hook      = null;

		if ( $has_overrides ) {
			$mapped = self::map_connection_overrides( $overrides );

			if ( ! empty( $mapped ) ) {
				$original_settings = Settings::all();
				$applied_settings  = array_merge( $original_settings, $mapped );
				$mock_option       = $applied_settings;

				// Use an in-memory filter so the DB is never touched during a test.
				$option_hook = static function () use ( &$mock_option ) {
					return $mock_option;
				};
				add_filter( 'pre_option_' . Settings::OPTION_KEY, $option_hook );
			}
		}

		Mailer::cleanup_context();

		$effective_settings = $has_overrides ? $applied_settings : Settings::all();
		// Respect transport override if provided, otherwise use current engine setting.
		$effective_transport             = isset( $mapped['transport'] ) && $mapped['transport']
			? $mapped['transport']
			: ( isset( $effective_settings['transport'] ) ? $effective_settings['transport'] : Settings::get( 'transport', 'smtp' ) );
		$effective_settings['transport'] = $effective_transport;

		try {
			switch ( $effective_transport ) {
				case 'mailgun_api':
					return self::perform_mailgun_api_check( $effective_settings );
				case 'sendgrid_api':
					return self::perform_sendgrid_api_check( $effective_settings );
				case 'postmark_api':
					return self::perform_postmark_api_check( $effective_settings );
				case 'sparkpost_api':
					return self::perform_sparkpost_api_check( $effective_settings );
				case 'ses_api':
					return self::perform_ses_api_check( $effective_settings );
				case 'generic_http':
					return self::perform_generic_api_check( $effective_settings );
			}

			Mailer::set_retry_context(
				array(
					'smtp_profile' => $profile_key,
				)
			);

			$connection = Mailer::test_connection();
		} catch ( \Throwable $throwable ) {
			$connection = array(
				'success' => false,
				'message' => $throwable->getMessage(),
				'data'    => array(),
			);
		} finally {
			Mailer::cleanup_context();
			// Remove the in-memory override filter — DB was never modified.
			if ( null !== $option_hook ) {
				remove_filter( 'pre_option_' . Settings::OPTION_KEY, $option_hook );
			}
		}

		if ( empty( $connection['data'] ) ) {
			if ( ! empty( $applied_settings ) ) {
				$credentials = $applied_settings;
			} else {
				$credentials = Settings::smtp_credentials( $profile_key );
			}
			$connection['data'] = array(
				'host'       => $credentials['smtp_host'] ?? ( $credentials['host'] ?? '' ),
				'port'       => isset( $credentials['smtp_port'] ) ? (int) $credentials['smtp_port'] : ( isset( $credentials['port'] ) ? (int) $credentials['port'] : 0 ),
				'encryption' => $credentials['smtp_encryption'] ?? ( $credentials['encryption'] ?? '' ),
				'auth'       => ! empty( $credentials['smtp_username'] ?? $credentials['username'] ?? '' ),
			);
		}

		return $connection;
	}

	/**
	 * Perform Mailgun API credential check.
	 *
	 * @param array<string, mixed> $settings Effective settings.
	 * @return array<string, mixed>
	 */
	protected static function perform_mailgun_api_check( array $settings ): array {
		$api_key  = $settings['mailgun_api_key'] ?? '';
		$domain   = $settings['mailgun_api_domain'] ?? '';
		$endpoint = trim( $settings['mailgun_api_endpoint'] ?? 'api.mailgun.net' );

		if ( '' === $api_key || '' === $domain ) {
			return array(
				'success' => false,
				'message' => __( 'Mailgun API key or domain is missing.', 'mail-doctor' ),
				'data'    => array(
					'host'  => $endpoint,
					'auth'  => false,
					'debug' => array(),
				),
			);
		}

		$url     = sprintf( 'https://%s/v3/domains/%s', $endpoint, $domain );
		$headers = array(
			'Authorization' => 'Basic ' . base64_encode( 'api:' . $api_key ),
		);

		$response = wp_remote_get(
			$url,
			array(
				'timeout' => 15,
				'headers' => $headers,
			)
		);

		$debug = array(
			sprintf( 'GET %s', $url ),
		);

		if ( is_wp_error( $response ) ) {
			$debug[] = $response->get_error_message();
			return array(
				'success' => false,
				'message' => $response->get_error_message(),
				'data'    => array(
					'host'  => $endpoint,
					'auth'  => false,
					'debug' => $debug,
				),
			);
		}

		$code    = wp_remote_retrieve_response_code( $response );
		$body    = wp_remote_retrieve_body( $response );
		$debug[] = sprintf( 'Response: %d %s', $code, wp_remote_retrieve_response_message( $response ) );

		if ( $body ) {
			$debug[] = $body;
		}

		if ( 200 === $code ) {
			return array(
				'success' => true,
				'message' => __( 'Mailgun API credentials verified successfully.', 'mail-doctor' ),
				'data'    => array(
					'host'  => $endpoint,
					'auth'  => true,
					'debug' => $debug,
				),
			);
		}

		$reason = __( 'Mailgun API request failed.', 'mail-doctor' );
		if ( $body ) {
			$json = json_decode( $body, true );
			if ( isset( $json['message'] ) ) {
				$reason = (string) $json['message'];
			}
		}

		return array(
			'success' => false,
			'message' => $reason,
			'data'    => array(
				'host'  => $endpoint,
				'auth'  => false,
				'debug' => $debug,
			),
		);
	}

	/**
	 * Perform SendGrid API credential check.
	 *
	 * @param array<string,mixed> $settings Effective settings.
	 * @return array<string,mixed>
	 */
	protected static function perform_sendgrid_api_check( array $settings ): array {
		return SendGrid::check_connection( $settings );
	}

	/**
	 * Perform Postmark API credential check.
	 *
	 * @param array<string,mixed> $settings Effective settings.
	 * @return array<string,mixed>
	 */
	protected static function perform_postmark_api_check( array $settings ): array {
		return Postmark::check_connection( $settings );
	}

	/**
	 * Perform SparkPost API credential check.
	 *
	 * @param array<string,mixed> $settings Effective settings.
	 * @return array<string,mixed>
	 */
	protected static function perform_sparkpost_api_check( array $settings ): array {
		return SparkPost::check_connection( $settings );
	}

	/**
	 * Perform SES API credential check.
	 *
	 * @param array<string,mixed> $settings Effective settings.
	 * @return array<string,mixed>
	 */
	protected static function perform_ses_api_check( array $settings ): array {
		return Ses::check_connection( $settings );
	}

	/**
	 * Perform generic HTTP endpoint check.
	 *
	 * @param array<string,mixed> $settings Effective settings.
	 * @return array<string,mixed>
	 */
	protected static function perform_generic_api_check( array $settings ): array {
		return Generic_Http::check_connection( $settings );
	}

	/**
	 * Map raw override fields to settings keys.
	 *
	 * @param array<string, mixed> $overrides Overrides.
	 * @return array<string, mixed>
	 */
	protected static function map_connection_overrides( array $overrides ): array {
		$mapped = array();

		$normalize = static function ( $value ) {
			if ( null === $value ) {
				return null;
			}
			$value = (string) $value;
			$value = trim( $value );
			if ( $value === '' || strtolower( $value ) === 'undefined' ) {
				return null;
			}
			return $value;
		};

		// Basic connection fields.
		if ( array_key_exists( 'host', $overrides ) ) {
			$val = $normalize( $overrides['host'] );
			if ( null !== $val ) {
				$mapped['smtp_host'] = sanitize_text_field( $val );
			}
		}

		if ( array_key_exists( 'port', $overrides ) ) {
			$val = $normalize( $overrides['port'] );
			if ( null !== $val && is_numeric( $val ) ) {
				$mapped['smtp_port'] = (int) $val;
			}
		}

		if ( array_key_exists( 'encryption', $overrides ) ) {
			$val = $normalize( $overrides['encryption'] );
			if ( null !== $val ) {
				$mapped['smtp_encryption'] = sanitize_key( $val );
			}
		}

		if ( array_key_exists( 'username', $overrides ) ) {
			$val = $normalize( $overrides['username'] );
			if ( null !== $val ) {
				$mapped['smtp_username'] = sanitize_text_field( $val );
			}
		}

		if ( array_key_exists( 'password', $overrides ) ) {
			$raw = trim( (string) $overrides['password'] );
			if ( '' !== $raw && false === strpos( $raw, '•' ) && strtolower( $raw ) !== 'undefined' ) {
				$mapped['smtp_password'] = $raw;
			}
		}

		if ( array_key_exists( 'from_email', $overrides ) ) {
			$val = $normalize( $overrides['from_email'] );
			if ( null !== $val ) {
				$mapped['from_email'] = sanitize_email( $val );
			}
		}

		if ( array_key_exists( 'from_name', $overrides ) ) {
			$val = $normalize( $overrides['from_name'] );
			if ( null !== $val ) {
				$mapped['from_name'] = sanitize_text_field( $val );
			}
		}

		// Provider secrets and options — only include when non-empty.
		$secret_if_present = static function ( $key ) use ( &$mapped, $overrides, $normalize ) {
			if ( ! array_key_exists( $key, $overrides ) ) {
				return;
			}
			$val = $normalize( $overrides[ $key ] );
			if ( null !== $val && false === strpos( $val, '•' ) ) {
				$mapped[ $key ] = $val;
			}
		};

		$plain_if_present = static function ( $key, $sanitizer ) use ( &$mapped, $overrides, $normalize ) {
			if ( ! array_key_exists( $key, $overrides ) ) {
				return;
			}
			$val = $normalize( $overrides[ $key ] );
			if ( null !== $val ) {
				$mapped[ $key ] = $sanitizer( $val );
			}
		};

		$secret_if_present( 'mailgun_api_key' );
		$plain_if_present( 'mailgun_api_domain', 'sanitize_text_field' );
		$plain_if_present( 'mailgun_api_endpoint', 'sanitize_text_field' );

		$secret_if_present( 'sendgrid_api_key' );
		$secret_if_present( 'postmark_api_token' );

		$secret_if_present( 'sparkpost_api_key' );
		$plain_if_present( 'sparkpost_api_endpoint', 'sanitize_text_field' );

		$secret_if_present( 'ses_api_key' );
		if ( array_key_exists( 'ses_api_secret', $overrides ) ) {
			$raw = (string) $overrides['ses_api_secret'];
			if ( '' !== $raw && false === strpos( $raw, '•' ) && strtolower( $raw ) !== 'undefined' ) {
				$mapped['ses_api_secret'] = $raw;
			}
		}
		$secret_if_present( 'ses_api_session_token' );
		$plain_if_present( 'ses_api_region', 'sanitize_text_field' );

		$plain_if_present( 'generic_api_endpoint', 'esc_url_raw' );
		$plain_if_present(
			'generic_api_method',
			static function ( $val ) {
				return strtoupper( sanitize_text_field( $val ) );
			}
		);
		$secret_if_present( 'generic_api_auth_token' );

		// Transport: only honor a concrete value.
		if ( array_key_exists( 'transport', $overrides ) ) {
			$val = $normalize( $overrides['transport'] );
			if ( null !== $val ) {
				$mapped['transport'] = sanitize_key( $val );
			}
		}

		// Drop nulls and return final map.
		return array_filter(
			$mapped,
			static function ( $value ) {
				return null !== $value;
			}
		);
	}

	/**
	 * Determine TLS badge state based on encryption setting.
	 *
	 * @param string $encryption Encryption descriptor.
	 * @return bool
	 */
	protected static function badge_from_encryption( string $encryption ): bool {
		$value = strtolower( trim( $encryption ) );

		return in_array( $value, array( 'ssl', 'tls', 'starttls', 'smtps', 'https' ), true );
	}

	/**
	 * AJAX handler: retrieve single log entry details HTML.
	 *
	 * @return void
	 */
	public static function ajax_get_log_details(): void {
		check_ajax_referer( 'mail_doctor_get_log_nonce' );
		self::assert_capability();

		$log_id = isset( $_POST['log_id'] ) ? absint( $_POST['log_id'] ) : 0;

		if ( ! $log_id ) {
			wp_send_json_error(
				array(
					'message' => esc_html__( 'Invalid log ID.', 'mail-doctor' ),
				),
				400
			);
		}

		$row = Logger::get_entry( $log_id );

		if ( ! $row ) {
			wp_send_json_error(
				array(
					'message' => esc_html__( 'Log entry not found.', 'mail-doctor' ),
				),
				404
			);
		}

		$mail_doctor_log_item = self::prepare_log_item( (array) $row );
		$mail_doctor_is_drawer = true;

		ob_start();
		include MAIL_DOCTOR_PATH . 'templates/admin/log-view.php';
		$html = ob_get_clean();

		wp_send_json_success(
			array(
				'html' => $html,
			)
		);
	}
}

