<?php
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

class Aegisify_Emails {

	const OPTION_KEY = 'aegisify_email_settings';

	public static function init(): void {
		add_filter( 'pre_wp_mail', array( __CLASS__, 'maybe_send_via_aegisify_smtp' ), 10, 2 );
		add_action( 'phpmailer_init', array( __CLASS__, 'configure_phpmailer' ), 10, 1 );
		add_filter( 'wp_mail_from', array( __CLASS__, 'filter_mail_from' ), 10, 1 );
		add_filter( 'wp_mail_from_name', array( __CLASS__, 'filter_mail_from_name' ), 10, 1 );
	}

	public static function get_settings(): array {
		$defaults = array(
			'smtp_enabled'     => 'no',
			'smtp_provider'    => 'custom',
			'smtp_host'        => '',
			'smtp_port'        => 587,
			'smtp_encryption'  => 'tls', // none|ssl|tls
			'smtp_auth'        => 'yes',
			'smtp_username'    => '',
			'smtp_password'    => '',
			'smtp_from_email'  => '',
			'smtp_from_name'   => '',
			'smtp_reply_to'    => '',
		);

		$saved = get_option( self::OPTION_KEY, array() );
		if ( ! is_array( $saved ) ) {
			$saved = array();
		}

		return wp_parse_args( $saved, $defaults );
	}

	public static function update_settings( array $settings ): void {
		update_option( self::OPTION_KEY, $settings );
	}

	protected static function get_smtp_setting( string $key, $default = '' ) {
		$settings = self::get_settings();

		$map = array(
			'smtp_enabled'    => 'AEGISIFY_SMTP_ENABLED',
			'smtp_provider'   => 'AEGISIFY_SMTP_PROVIDER',
			'smtp_host'       => 'AEGISIFY_SMTP_HOST',
			'smtp_port'       => 'AEGISIFY_SMTP_PORT',
			'smtp_encryption' => 'AEGISIFY_SMTP_ENCRYPTION',
			'smtp_auth'       => 'AEGISIFY_SMTP_AUTH',
			'smtp_username'   => 'AEGISIFY_SMTP_USERNAME',
			'smtp_password'   => 'AEGISIFY_SMTP_PASSWORD',
			'smtp_from_email' => 'AEGISIFY_SMTP_FROM_EMAIL',
			'smtp_from_name'  => 'AEGISIFY_SMTP_FROM_NAME',
			'smtp_reply_to'   => 'AEGISIFY_SMTP_REPLY_TO',
		);

		if ( isset( $map[ $key ] ) && defined( $map[ $key ] ) ) {
			return constant( $map[ $key ] );
		}

		return isset( $settings[ $key ] ) ? $settings[ $key ] : $default;
	}

	protected static function is_aegisify_smtp_provider_selected(): bool {
		$enabled  = (string) self::get_smtp_setting( 'smtp_enabled', 'no' );
		$provider = (string) self::get_smtp_setting( 'smtp_provider', 'custom' );

		return ( 'yes' === $enabled && 'aegisify_smtp' === $provider );
	}

	protected static function can_use_aegisify_smtp_provider(): bool {
		if ( ! self::is_aegisify_smtp_provider_selected() ) {
			return false;
		}

		if ( ! function_exists( 'aegisify_is_pro' ) || ! aegisify_is_pro() ) {
			return false;
		}

		return function_exists( 'aegisify_send_alert' );
	}

	public static function maybe_send_via_aegisify_smtp( $pre, array $atts ) {
		static $sending_via_aegisify_smtp = false;

		if ( $sending_via_aegisify_smtp ) {
			return $pre;
		}

		if ( ! self::can_use_aegisify_smtp_provider() ) {
			return $pre;
		}

		$to          = isset( $atts['to'] ) ? $atts['to'] : array();
		$subject     = isset( $atts['subject'] ) ? (string) $atts['subject'] : '';
		$message     = isset( $atts['message'] ) ? (string) $atts['message'] : '';
		$headers     = isset( $atts['headers'] ) ? $atts['headers'] : array();
		$attachments = isset( $atts['attachments'] ) ? $atts['attachments'] : array();

		$sending_via_aegisify_smtp = true;
		try {
			return (bool) aegisify_send_alert( 'wordpress', $to, $subject, $message, $headers, $attachments );
		} finally {
			$sending_via_aegisify_smtp = false;
		}
	}

	public static function configure_phpmailer( $phpmailer ): void {
		$enabled = (string) self::get_smtp_setting( 'smtp_enabled', 'no' );
		if ( 'yes' !== $enabled ) {
			return;
		}

		if ( self::is_aegisify_smtp_provider_selected() ) {
			return;
		}


		$host       = (string) self::get_smtp_setting( 'smtp_host', '' );
		$port       = (int) self::get_smtp_setting( 'smtp_port', 587 );
		$encryption = (string) self::get_smtp_setting( 'smtp_encryption', 'tls' ); // none|ssl|tls
		$auth       = (string) self::get_smtp_setting( 'smtp_auth', 'yes' );
		$username   = (string) self::get_smtp_setting( 'smtp_username', '' );
		$password   = (string) self::get_smtp_setting( 'smtp_password', '' );

		if ( '' === $host ) {
			return;
		}

		$phpmailer->isSMTP();
		$phpmailer->Host    = $host;
		$phpmailer->Port    = $port > 0 ? $port : 587;
		$phpmailer->Timeout = 10;
		$phpmailer->CharSet = 'UTF-8';

		if ( 'ssl' === $encryption ) {
			$phpmailer->SMTPSecure  = 'ssl';
			$phpmailer->SMTPAutoTLS = false;
		} elseif ( 'tls' === $encryption ) {
			$phpmailer->SMTPSecure  = 'tls';
			$phpmailer->SMTPAutoTLS = true;
		} else {
			$phpmailer->SMTPSecure  = '';
			$phpmailer->SMTPAutoTLS = false;
		}

		$phpmailer->SMTPAuth = ( 'yes' === $auth );
		if ( $phpmailer->SMTPAuth ) {
			$phpmailer->Username = $username;
			$phpmailer->Password = $password;
		}

		$from_email = (string) self::get_smtp_setting( 'smtp_from_email', '' );
		$from_name  = (string) self::get_smtp_setting( 'smtp_from_name', '' );
		if ( $from_email && is_email( $from_email ) ) {
			try {
				$phpmailer->setFrom( $from_email, $from_name ? $from_name : null, false );
			} catch ( \Exception $e ) {
				// Do not hard-fail.
			}
		}

		$reply_to = (string) self::get_smtp_setting( 'smtp_reply_to', '' );
		if ( $reply_to && is_email( $reply_to ) ) {
			try {
				$phpmailer->addReplyTo( $reply_to );
			} catch ( \Exception $e ) {
				// ignore.
			}
		}
	}

	public static function filter_mail_from( $from ): string {
		$enabled = (string) self::get_smtp_setting( 'smtp_enabled', 'no' );
		if ( 'yes' !== $enabled ) {
			return (string) $from;
		}
		if ( self::is_aegisify_smtp_provider_selected() ) {
			return (string) $from;
		}
		$from_email = (string) self::get_smtp_setting( 'smtp_from_email', '' );
		if ( $from_email && is_email( $from_email ) ) {
			return $from_email;
		}
		return (string) $from;
	}

	public static function filter_mail_from_name( $name ): string {
		$enabled = (string) self::get_smtp_setting( 'smtp_enabled', 'no' );
		if ( 'yes' !== $enabled ) {
			return (string) $name;
		}
		if ( self::is_aegisify_smtp_provider_selected() ) {
			return (string) $name;
		}
		$from_name = (string) self::get_smtp_setting( 'smtp_from_name', '' );
		if ( '' !== $from_name ) {
			return $from_name;
		}
		return (string) $name;
	}

	public static function test_smtp_connection(): array {
		$enabled = (string) self::get_smtp_setting( 'smtp_enabled', 'no' );
		if ( 'yes' !== $enabled ) {
			return array(
				'ok'      => false,
				'step'    => 'disabled',
				'details' => array( 'message' => 'SMTP is disabled.' ),
			);
		}

		if ( self::is_aegisify_smtp_provider_selected() ) {
			return array(
				'ok'      => self::can_use_aegisify_smtp_provider(),
				'step'    => 'aegisify_smtp',
				'details' => array(
					'provider_selected'   => true,
					'core_license_active' => function_exists( 'aegisify_is_pro' ) ? (bool) aegisify_is_pro() : false,
					'relay_available'     => function_exists( 'aegisify_send_alert' ),
				),
			);
		}

		$host       = (string) self::get_smtp_setting( 'smtp_host', '' );
		$port       = (int) self::get_smtp_setting( 'smtp_port', 587 );
		$encryption = (string) self::get_smtp_setting( 'smtp_encryption', 'tls' );
		$auth       = (string) self::get_smtp_setting( 'smtp_auth', 'yes' );
		$username   = (string) self::get_smtp_setting( 'smtp_username', '' );
		$password   = (string) self::get_smtp_setting( 'smtp_password', '' );

		if ( '' === $host || $port <= 0 ) {
			return array(
				'ok'      => false,
				'step'    => 'config',
				'details' => array( 'message' => 'Missing SMTP host/port.' ),
			);
		}

		$timeout = 10;
		$remote  = $host . ':' . $port;

		$ctx = stream_context_create(
			array(
				'ssl' => array(
					'verify_peer'       => true,
					'verify_peer_name'  => true,
					'allow_self_signed' => false,
				),
			)
		);

		$errno  = 0;
		$errstr = '';
		$fp     = @stream_socket_client(
			'tcp://' . $remote,
			$errno,
			$errstr,
			$timeout,
			STREAM_CLIENT_CONNECT,
			$ctx
		);

		if ( ! is_resource( $fp ) ) {
			return array(
				'ok'      => false,
				'step'    => 'connect',
				'details' => array(
					'errno'  => $errno,
					'error'  => $errstr,
					'remote' => $remote,
				),
			);
		}

		stream_set_timeout( $fp, $timeout );

		$details = array(
			'remote'     => $remote,
			'encryption' => $encryption,
			'auth'       => ( 'yes' === $auth ),
		);

		$banner = self::sock_readline( $fp );
		$details['banner'] = $banner;

		if ( ! self::is_positive_smtp( $banner ) ) {
			fclose( $fp ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- Socket resource, WP_Filesystem not applicable.
			return array( 'ok' => false, 'step' => 'banner', 'details' => $details );
		}

		$ehlo = self::smtp_cmd( $fp, 'EHLO aegisify.local' );
		$details['ehlo'] = $ehlo;

		if ( ! self::is_positive_smtp( $ehlo ) ) {
			fclose( $fp ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- Socket resource, WP_Filesystem not applicable.
			return array( 'ok' => false, 'step' => 'ehlo', 'details' => $details );
		}

		if ( 'tls' === $encryption ) {
			$starttls = self::smtp_cmd( $fp, 'STARTTLS' );
			$details['starttls'] = $starttls;

			if ( ! self::is_positive_smtp( $starttls ) ) {
				fclose( $fp ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- Socket resource, WP_Filesystem not applicable.
				return array( 'ok' => false, 'step' => 'starttls', 'details' => $details );
			}

			$crypto_ok = @stream_socket_enable_crypto( $fp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT );
			$details['tls_crypto'] = $crypto_ok;

			if ( true !== $crypto_ok && 1 !== $crypto_ok ) {
				fclose( $fp ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- Socket resource, WP_Filesystem not applicable.
				return array( 'ok' => false, 'step' => 'tls_crypto', 'details' => $details );
			}

			$ehlo2 = self::smtp_cmd( $fp, 'EHLO aegisify.local' );
			$details['ehlo2'] = $ehlo2;

			if ( ! self::is_positive_smtp( $ehlo2 ) ) {
				fclose( $fp ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- Socket resource, WP_Filesystem not applicable.
				return array( 'ok' => false, 'step' => 'ehlo2', 'details' => $details );
			}
		}

		if ( 'yes' === $auth ) {
			if ( '' === $username || '' === $password ) {
				fclose( $fp ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- Socket resource, WP_Filesystem not applicable.
				return array( 'ok' => false, 'step' => 'auth_config', 'details' => $details );
			}

			$auth1 = self::smtp_cmd( $fp, 'AUTH LOGIN' );
			$details['auth_login'] = $auth1;

			// 334 expected.
			if ( ! self::starts_with_code( $auth1, 334 ) ) {
				fclose( $fp ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- Socket resource, WP_Filesystem not applicable.
				return array( 'ok' => false, 'step' => 'auth_login', 'details' => $details );
			}

			$u = self::smtp_cmd( $fp, base64_encode( $username ) );
			$details['auth_user'] = $u;

			if ( ! self::starts_with_code( $u, 334 ) ) {
				fclose( $fp ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- Socket resource, WP_Filesystem not applicable.
				return array( 'ok' => false, 'step' => 'auth_user', 'details' => $details );
			}

			$p = self::smtp_cmd( $fp, base64_encode( $password ) );
			$details['auth_pass'] = $p;

			if ( ! self::starts_with_code( $p, 235 ) ) {
				fclose( $fp ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- Socket resource, WP_Filesystem not applicable.
				return array( 'ok' => false, 'step' => 'auth_pass', 'details' => $details );
			}
		}

		self::smtp_cmd( $fp, 'QUIT' );
		fclose( $fp ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- Socket resource, WP_Filesystem not applicable.

		return array( 'ok' => true, 'step' => 'ok', 'details' => $details );
	}

	protected static function sock_readline( $fp ): string {
		$line = '';
		while ( is_resource( $fp ) && ! feof( $fp ) ) {
			$chunk = fgets( $fp, 2048 );
			if ( false === $chunk ) {
				break;
			}
			$line .= $chunk;
			// Multiline response has dash after code: "250-...".
			if ( preg_match( '/^\d{3}\s/', $chunk ) ) {
				break;
			}
		}
		return trim( (string) $line );
	}

	protected static function smtp_cmd( $fp, string $cmd ): string {
		if ( ! is_resource( $fp ) ) {
			return '';
		}
		@fwrite( $fp, $cmd . "\r\n" ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fwrite -- Socket resource, WP_Filesystem not applicable.
		return self::sock_readline( $fp );
	}

	protected static function is_positive_smtp( string $resp ): bool {
		return (bool) preg_match( '/^(2|3)\d{2}\b/', trim( $resp ) );
	}

	protected static function starts_with_code( string $resp, int $code ): bool {
		return (bool) preg_match( '/^' . preg_quote( (string) $code, '/' ) . '\b/', trim( $resp ) );
	}
}
