<?php
/**
 * Optional admin / chat alerts when outbound mail hard-fails.
 *
 * @package EmailSmtp
 */

namespace EmailSmtp\Includes;

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

final class FailureNotifier {

	private const TRANSIENT_KEY = 'wpps_ed_failure_alert_sent';

	private static bool $notified_this_request = false;

	/**
	 * @param array<string, mixed> $settings
	 */
	public static function maybe_notify( DeliveryResult $result, ProviderInterface $provider, array $settings ): void {
		if ( $result->is_success() || self::$notified_this_request ) {
			return;
		}
		if ( empty( $settings['failure_alert_enabled'] ) || (string) $settings['failure_alert_enabled'] === '0' ) {
			return;
		}

		// Rate-limit: at most one alert every 15 minutes per site.
		if ( get_transient( self::TRANSIENT_KEY ) ) {
			return;
		}
		self::$notified_this_request = true;
		set_transient( self::TRANSIENT_KEY, 1, 15 * MINUTE_IN_SECONDS );

		$site = wp_specialchars_decode( (string) get_option( 'blogname' ), ENT_QUOTES );
		$error = ErrorNormalizer::redact( $result->get_user_message() );
		$subject = sprintf(
			/* translators: %s: site name */
			__( '[%s] Email delivery failed', 'wp-powersuite' ),
			$site
		);

		$body_lines = [
			__( 'An outbound email failed after retries / fallback.', 'wp-powersuite' ),
			'',
			/* translators: 1: string, 2: string. */
			sprintf( /* translators: %s: provider name */ __( 'Provider: %s', 'wp-powersuite' ), $provider->get_name() ),
			sprintf( /* translators: %s: error */ __( 'Error: %s', 'wp-powersuite' ), $error ),
			'',
			__( 'Check Email Delivery settings and Email Logs for details.', 'wp-powersuite' ),
		];
		$body = implode( "\n", $body_lines );

		$chat_url = esc_url_raw( trim( (string) ( $settings['failure_alert_webhook'] ?? '' ) ) );
		if ( $chat_url !== '' && self::is_allowed_webhook_url( $chat_url ) ) {
			self::send_chat_alert( $chat_url, $site, $provider->get_name(), $error );
		}

		$to = sanitize_email( (string) ( $settings['failure_alert_email'] ?? '' ) );
		if ( ! is_email( $to ) ) {
			$to = sanitize_email( (string) get_option( 'admin_email' ) );
		}
		if ( ! is_email( $to ) ) {
			return;
		}

		/**
		 * Prevent recursive failure alerts if the alert itself fails.
		 */
		add_filter( 'wp_powersuite_email_delivery_skip_failure_alert', '__return_true', 99 );
		try {
			// phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.wp_mail_wp_mail
			wp_mail( $to, $subject, $body );
		} finally {
			remove_filter( 'wp_powersuite_email_delivery_skip_failure_alert', '__return_true', 99 );
		}
	}

	/**
	 * Whether a failure-alert chat webhook URL is on the Slack/Discord allowlist (or filter).
	 */
	public static function is_allowed_webhook_url( string $url ): bool {
		if ( ! preg_match( '#^https://#i', $url ) ) {
			return false;
		}
		$host = wp_parse_url( $url, PHP_URL_HOST );
		if ( ! is_string( $host ) || $host === '' ) {
			return false;
		}
		$host = strtolower( $host );
		$allowed = [
			'hooks.slack.com',
			'discord.com',
			'discordapp.com',
		];
		foreach ( $allowed as $needle ) {
			if ( $host === $needle || str_ends_with( $host, '.' . $needle ) ) {
				return true;
			}
		}
		/**
		 * Allow additional HTTPS chat webhook hosts (Teams, custom bridges, etc.).
		 *
		 * @param list<string> $allowed
		 * @param string       $host
		 */
		$extra = apply_filters( 'wp_powersuite_email_delivery_alert_webhook_hosts', [], $host );
		if ( is_array( $extra ) ) {
			foreach ( $extra as $needle ) {
				$needle = strtolower( (string) $needle );
				if ( $needle !== '' && ( $host === $needle || str_ends_with( $host, '.' . $needle ) ) ) {
					return true;
				}
			}
		}
		return false;
	}

	private static function send_chat_alert( string $url, string $site, string $provider, string $error ): void {
		$host = strtolower( (string) wp_parse_url( $url, PHP_URL_HOST ) );
		$text = sprintf(
			/* translators: 1: site name, 2: provider, 3: error */
			__( '[%1$s] Email delivery failed via %2$s: %3$s', 'wp-powersuite' ),
			$site,
			$provider,
			$error
		);

		if ( str_contains( $host, 'discord' ) ) {
			$payload = [ 'content' => $text ];
		} else {
			// Slack incoming webhook (also works for many Slack-compatible bridges).
			$payload = [ 'text' => $text ];
		}

		wp_remote_post(
			$url,
			[
				'timeout'     => 8,
				'redirection' => 0,
				'headers'     => [ 'Content-Type' => 'application/json; charset=utf-8' ],
				'body'        => wp_json_encode( $payload ),
			]
		);
	}
}

