<?php

declare(strict_types=1);

namespace SiteFort\Includes;

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

/**
 * Central validation for remote URLs consumed by sensitive network sinks.
 */
final class RemoteUrlValidator {
	public const STORAGE_HOST_SUFFIXES = [
		'.amazonaws.com',
		'.r2.cloudflarestorage.com',
	];

	public const SITEFORT_EXACT_HOSTS = [
		'securewp.net',
	];

	public const SITEFORT_HOST_SUFFIXES = [
		'.securewp.net',
	];

	public const MAXMIND_EXACT_HOSTS = [
		'download.maxmind.com',
	];

	/**
	 * Bucket MaxMind serves GeoLite archives from. S3 bucket names are globally
	 * unique, so pinning the label keeps arbitrary third-party buckets out of
	 * the redirect allowlist.
	 */
	public const MAXMIND_STORAGE_BUCKET = 'mm-prod-geoip-databases';

	/**
	 * Validate a generic externally configured HTTPS URL.
	 */
	public static function is_safe_https_url( string $url ): bool {
		if ( ! self::has_valid_https_authority( $url ) ) {
			return false;
		}

		return function_exists( 'wp_http_validate_url' )
			? (bool) wp_http_validate_url( $url )
			: (bool) filter_var( $url, FILTER_VALIDATE_URL );
	}

	/**
	 * Validate an HTTPS URL and require the host to match an allowlist.
	 *
	 * @param string[] $exact_hosts Lowercase exact hosts without ports.
	 * @param string[] $host_suffixes Lowercase suffixes, usually prefixed with a dot.
	 */
	public static function is_trusted_https_url( string $url, array $exact_hosts = [], array $host_suffixes = [] ): bool {
		if ( ! self::has_valid_https_authority( $url ) ) {
			return false;
		}

		$host = self::normalized_host( $url );
		if ( '' === $host ) {
			return false;
		}

		foreach ( $exact_hosts as $exact_host ) {
			if ( $host === strtolower( trim( $exact_host ) ) ) {
				return true;
			}
		}

		foreach ( $host_suffixes as $suffix ) {
			$suffix = strtolower( trim( $suffix ) );
			if ( '' === $suffix ) {
				continue;
			}
			if ( $suffix[0] !== '.' ) {
				$suffix = '.' . $suffix;
			}
			if ( str_ends_with( $host, $suffix ) ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Validate SiteFort Vault URLs used for clean repair sources.
	 */
	public static function is_trusted_vault_url( string $url ): bool {
		return self::is_trusted_https_url(
			$url,
			self::SITEFORT_EXACT_HOSTS,
			array_merge( self::SITEFORT_HOST_SUFFIXES, self::STORAGE_HOST_SUFFIXES )
		);
	}

	/**
	 * Validate MaxMind redirect targets before streaming archive data.
	 *
	 * Storage hosts must be the known MaxMind bucket, not just any S3/R2 host.
	 */
	public static function is_trusted_maxmind_redirect_url( string $url ): bool {
		if ( self::is_trusted_https_url( $url, self::MAXMIND_EXACT_HOSTS, [] ) ) {
			return true;
		}

		if ( ! self::is_trusted_https_url( $url, [], self::STORAGE_HOST_SUFFIXES ) ) {
			return false;
		}

		return str_starts_with( self::normalized_host( $url ), self::MAXMIND_STORAGE_BUCKET . '.' );
	}

	private static function has_valid_https_authority( string $url ): bool {
		$url = trim( $url );
		if ( '' === $url ) {
			return false;
		}

		$scheme = strtolower( (string) wp_parse_url( $url, PHP_URL_SCHEME ) );
		if ( 'https' !== $scheme ) {
			return false;
		}

		if ( '' !== (string) wp_parse_url( $url, PHP_URL_USER ) || '' !== (string) wp_parse_url( $url, PHP_URL_PASS ) ) {
			return false;
		}

		$port = wp_parse_url( $url, PHP_URL_PORT );
		if ( null !== $port && 443 !== (int) $port ) {
			return false;
		}

		return '' !== self::normalized_host( $url );
	}

	private static function normalized_host( string $url ): string {
		$host = strtolower( (string) wp_parse_url( $url, PHP_URL_HOST ) );
		$host = trim( $host, ". \t\n\r\0\x0B" );

		if ( '' === $host || preg_match( '/[\s\/:@]/', $host ) ) {
			return '';
		}

		return $host;
	}

	public static function is_tls_error( \WP_Error $error ): bool {
		$message = strtolower( $error->get_error_message() );
		if ( '' === $message ) {
			return false;
		}

		foreach ( [ 'ssl', 'tls', 'certificate', 'cert ' ] as $needle ) {
			if ( str_contains( $message, $needle ) ) {
				return true;
			}
		}

		return false;
	}
}
