<?php
/**
 * Email error classification helper.
 *
 * @package SubmissionInbox
 */

namespace SUBIN\Mail;

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

/**
 * Converts safe technical mail errors into admin-friendly issues.
 */
class EmailErrorClassifier {
	/**
	 * Classify a mail error.
	 *
	 * @param string $error_message Raw or stored error message.
	 * @return array{code:string,title:string,explanation:string,recommended_action:string,action_type:string,technical_error:string}
	 */
	public function classify( string $error_message ): array {
		$technical_error = $this->sanitize_technical_error( $error_message );
		$normalized      = strtolower( $technical_error );

		if ( $this->matches( $normalized, array( 'invalid address: (from)', 'invalid address from', 'from address', 'sender address rejected', 'bad sender address' ) ) ) {
			return $this->issue(
				'invalid_from_email',
				__( 'Invalid From Email address', 'submission-inbox' ),
				__( 'WordPress could not send the email because the sender address is not valid.', 'submission-inbox' ),
				__( 'Check the From Email in Settings → Sending.', 'submission-inbox' ),
				'settings_sending',
				$technical_error
			);
		}

		if ( $this->matches( $normalized, array( 'could not instantiate mail function', 'could not instantiate mail', 'mail function', 'php mail', 'sendmail' ) ) ) {
			return $this->issue(
				'default_mail_unavailable',
				__( 'Default mail method is not available', 'submission-inbox' ),
				__( 'WordPress tried to send this email using the default PHP mail function, but the server could not complete the send operation.', 'submission-inbox' ),
				__( 'Configure SMTP sending in Settings → Sending. If you want to use the default mail method, enable PHP mail/sendmail on the server.', 'submission-inbox' ),
				'settings_sending',
				$technical_error
			);
		}

		if ( $this->matches( $normalized, array( 'could not authenticate', 'authentication failed', 'smtp error: could not authenticate', 'username and password not accepted', '535' ) ) ) {
			return $this->issue(
				'smtp_auth_failed',
				__( 'SMTP authentication failed', 'submission-inbox' ),
				__( 'The SMTP server rejected the username, password, or authentication settings.', 'submission-inbox' ),
				__( 'Check the SMTP username, password, and authentication settings in Settings → Sending.', 'submission-inbox' ),
				'settings_sending',
				$technical_error
			);
		}

		if ( $this->matches( $normalized, array( 'smtp connect() failed', 'could not connect to smtp host', 'connection refused', 'connection timed out', 'timed out', 'network is unreachable', 'failed to connect', 'connection failed' ) ) ) {
			return $this->issue(
				'smtp_connection_failed',
				__( 'SMTP connection failed', 'submission-inbox' ),
				__( 'WordPress could not connect to the configured SMTP server.', 'submission-inbox' ),
				__( 'Check the SMTP host, port, encryption, and server firewall settings in Settings → Sending.', 'submission-inbox' ),
				'settings_sending',
				$technical_error
			);
		}

		if ( $this->matches( $normalized, array( 'starttls', 'tls', 'ssl operation failed', 'certificate verify failed', 'stream_socket_enable_crypto', 'encryption' ) ) ) {
			return $this->issue(
				'smtp_encryption_failed',
				__( 'SMTP encryption failed', 'submission-inbox' ),
				__( 'WordPress could not establish a secure SMTP connection with the current encryption settings.', 'submission-inbox' ),
				__( 'Check the SMTP encryption, port, and certificate requirements in Settings → Sending.', 'submission-inbox' ),
				'settings_sending',
				$technical_error
			);
		}

		if ( $this->matches( $normalized, array( 'invalid address: (to)', 'recipient address rejected', 'mailbox unavailable', 'recipient rejected', 'user unknown', 'no such user', '550', '5.1.1' ) ) ) {
			return $this->issue(
				'recipient_rejected',
				__( 'Recipient address rejected', 'submission-inbox' ),
				__( 'The recipient email address was rejected by the mail server.', 'submission-inbox' ),
				__( 'Check the notification recipient email address.', 'submission-inbox' ),
				'form_notification_settings',
				$technical_error
			);
		}

		if ( $this->matches( $normalized, array( 'data not accepted', 'message rejected', 'transaction failed', 'blocked', 'policy rejection', 'spam', 'blacklist' ) ) ) {
			return $this->issue(
				'message_rejected',
				__( 'Email message rejected', 'submission-inbox' ),
				__( 'The mail server rejected the email message.', 'submission-inbox' ),
				__( 'Check the sender address, email content, and SMTP provider rules.', 'submission-inbox' ),
				'email_log',
				$technical_error
			);
		}

		return $this->issue(
			'unknown_email_error',
			__( 'Email sending failed', 'submission-inbox' ),
			__( 'WordPress could not send the notification email.', 'submission-inbox' ),
			__( 'Review the technical error and check Settings → Sending.', 'submission-inbox' ),
			'email_log',
			$technical_error
		);
	}

	/**
	 * Sanitize and mask a technical error.
	 *
	 * @param string $error_message Raw error message.
	 * @return string
	 */
	public function sanitize_technical_error( string $error_message ): string {
		$message = wp_strip_all_tags( $error_message );
		$message = preg_replace( '/\s+/', ' ', (string) $message );
		$message = trim( (string) $message );
		$message = $this->mask_sensitive_values( $message );
		$message = sanitize_textarea_field( $message );

		if ( function_exists( 'mb_substr' ) ) {
			return mb_substr( $message, 0, 500 );
		}

		return substr( $message, 0, 500 );
	}

	/**
	 * Mask obvious secrets in technical errors.
	 *
	 * @param string $message Message.
	 * @return string
	 */
	private function mask_sensitive_values( string $message ): string {
		$patterns = array(
			'/\b(username)\s*[:=]\s*([^\s,;]+)\s+(password|passwd|pwd)\s*[:=]\s*([^\s,;]+)/i',
			'/\b(password|passwd|pwd|secret|api[_-]?key|token|authorization)\s*[:=]\s*([^\s,;]+)/i',
			'/\b(Basic|Bearer)\s+[A-Za-z0-9._~+\/=-]+/i',
		);

		foreach ( $patterns as $pattern ) {
			$message = preg_replace_callback(
				$pattern,
				static function ( array $matches ): string {
					if ( isset( $matches[3] ) ) {
						return $matches[1] . '=[redacted] ' . $matches[3] . '=[redacted]';
					}

					if ( isset( $matches[1] ) && in_array( strtolower( $matches[1] ), array( 'basic', 'bearer' ), true ) ) {
						return $matches[1] . ' [redacted]';
					}

					return $matches[1] . '=[redacted]';
				},
				$message
			);
		}

		return (string) $message;
	}

	/**
	 * Check if normalized text contains any needle.
	 *
	 * @param string            $normalized Normalized message.
	 * @param array<int,string> $needles Needles.
	 * @return bool
	 */
	private function matches( string $normalized, array $needles ): bool {
		foreach ( $needles as $needle ) {
			if ( false !== strpos( $normalized, $needle ) ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Build issue response.
	 *
	 * @param string $code Code.
	 * @param string $title Title.
	 * @param string $explanation Explanation.
	 * @param string $recommended_action Recommended action.
	 * @param string $action_type Action type.
	 * @param string $technical_error Technical error.
	 * @return array{code:string,title:string,explanation:string,recommended_action:string,action_type:string,technical_error:string}
	 */
	private function issue( string $code, string $title, string $explanation, string $recommended_action, string $action_type, string $technical_error ): array {
		return array(
			'code'               => $code,
			'title'              => $title,
			'explanation'        => $explanation,
			'recommended_action' => $recommended_action,
			'action_type'        => $action_type,
			'technical_error'    => $technical_error,
		);
	}
}
