<?php

declare(strict_types=1);

namespace SiteFort\Modules\Firewall\SearchTrust;

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

use SiteFort\Includes\IpDetector;

final class SearchTrustRuntimeResolver {
	private const CACHE_GROUP = 'sitefort_search_trust';

	/** @var array<string, mixed> */
	private array $settings;

	/** @var array<string, mixed>|null */
	private ?array $current_context = null;

	/** @var array{protected_paths: array<int, string>, protected_prefixes: array<int, string>, scope_hash: string}|null */
	private ?array $current_scope = null;

	/**
	 * @param array<string, mixed> $settings
	 */
	public function __construct( array $settings ) {
		$this->settings = $settings;
	}

	/**
	 * @param array<string, mixed> $request
	 * @return array<string, mixed>
	 */
	public function resolve_current_request(
		string $ip,
		string $user_agent,
		bool $pre_wordpress_waf_active,
		bool $country_check_active,
		bool $community_check_active,
		array $request = []
	): array {
		if ( $this->current_context !== null ) {
			return $this->current_context;
		}

		$scope = $this->get_current_scope();
		$scope_hash = (string) ( $scope['scope_hash'] ?? '' );
		$bootstrap_context = $pre_wordpress_waf_active ? SearchTrustContext::from_bootstrap_constant() : null;
		if (
			$bootstrap_context !== null
			&& SearchTrustContext::matches_scope_hash( $bootstrap_context, $scope_hash )
		) {
			$this->current_context = $bootstrap_context;
			return $this->current_context;
		}

		if (
			! SearchTrustCore::should_resolve_verified_search_exception(
				! empty( $this->settings['rate_limit_enabled'] ),
				$country_check_active,
				$community_check_active
			)
		) {
			$this->current_context = SearchTrustContext::create(
				[
					'scope_hash' => $scope_hash,
				]
			);
			return $this->current_context;
		}

		$engine = SearchTrustCore::get_verified_search_engine_claim( $user_agent ) ?? '';
		if ( $engine === '' ) {
			$this->current_context = SearchTrustContext::create(
				[
					'scope_hash' => $scope_hash,
				]
			);
			return $this->current_context;
		}

		$eligible = SearchTrustCore::is_verified_search_request_eligible(
			[
				'method' => (string) ( $request['method'] ?? IpDetector::get_server_value( 'REQUEST_METHOD' ) ),
				'request_uri' => (string) ( $request['request_uri'] ?? IpDetector::get_server_value( 'REQUEST_URI' ) ),
			],
			$scope
		);

		if ( ! $eligible ) {
			$this->current_context = SearchTrustContext::create(
				[
					'engine' => $engine,
					'eligible' => false,
					'scope_hash' => $scope_hash,
				]
			);
			return $this->current_context;
		}

		$verified = SearchTrustDnsVerifier::verify(
			$ip,
			$engine,
			function ( string $cache_key ): ?bool {
				return $this->get_cached_verified_search_result( $cache_key );
			},
			function ( string $cache_key, bool $verified_result, int $ttl ): void {
				$this->cache_verified_search_result( $cache_key, $verified_result, $ttl );
			}
		);

		$this->current_context = SearchTrustContext::create(
			[
				'checked' => true,
				'eligible' => true,
				'verified' => $verified,
				'exception' => $verified,
				'engine' => $engine,
				'scope_hash' => $scope_hash,
			]
		);

		return $this->current_context;
	}

	/**
	 * @return array{protected_paths: array<int, string>, protected_prefixes: array<int, string>, scope_hash: string}
	 */
	private function get_current_scope(): array {
		if ( $this->current_scope === null ) {
			$this->current_scope = SearchTrustScopeBuilder::build_from_wordpress();
		}

		return $this->current_scope;
	}

	private function cache_verified_search_result( string $cache_key, bool $verified, int $ttl ): void {
		$cache_value = $verified ? 'yes' : 'no';

		if ( function_exists( 'wp_cache_set' ) ) {
			wp_cache_set( $cache_key, $cache_value, self::CACHE_GROUP, $ttl );
		}

		if ( function_exists( 'set_transient' ) ) {
			set_transient( $cache_key, $cache_value, $ttl );
		}
	}

	private function get_cached_verified_search_result( string $cache_key ): ?bool {
		if ( function_exists( 'wp_cache_get' ) ) {
			$found = false;
			$cached = wp_cache_get( $cache_key, self::CACHE_GROUP, false, $found );
			if ( $found ) {
				if ( $cached === 'yes' ) {
					return true;
				}
				if ( $cached === 'no' ) {
					return false;
				}
			}
		}

		if ( function_exists( 'get_transient' ) ) {
			$cached = get_transient( $cache_key );
			if ( $cached === 'yes' ) {
				return true;
			}
			if ( $cached === 'no' ) {
				return false;
			}
		}

		return null;
	}
}
