<?php
/**
 * @package Hafenstudios\Wellenbrecher
 */

namespace Hafenstudios\Wellenbrecher\Rules;

use Hafenstudios\Wellenbrecher\Engine\Context;
use Hafenstudios\Wellenbrecher\Engine\RuleInterface;
use Hafenstudios\Wellenbrecher\Engine\RuleResult;
use Hafenstudios\Wellenbrecher\Pro\GeoIp\GeoDatabase;

defined( 'ABSPATH' ) || exit;

/**
 * Rechenzentrums-/VPS-ASN-Regel (Pro, Stufe 3, W5b Technik A8). Gibt +15, wenn
 * die aufgeloeste Besucher-IP zu einer bekannten Hosting-/Cloud-ASN gehoert
 * (Data\datacenter-asns.php). Das ASN-Signal ist staerker als das Land: ein
 * normaler Besucher sitzt hinter seinem Zugangsanbieter, nicht bei AWS.
 *
 * Fehlt/veraltet die ASN-Datenbank, ist die Regel stumm (fail-open). Blockt nie
 * allein (+15 unter der Block-Schwelle).
 */
class AsnDatacenterRule implements RuleInterface {

	public const ID = 'geo_asn_datacenter';

	public const DEFAULT_POINTS = 15;

	protected GeoDatabase $geo;

	/**
	 * @var array<int, bool> Bekannte Datacenter-/Hosting-ASNs (asn => true).
	 */
	protected array $asns;

	protected int $points;

	/**
	 * @param GeoDatabase     $geo    Geo-Datenbank-Huelle.
	 * @param array<int,bool> $asns   Hash-Set bekannter Datacenter-ASNs.
	 * @param int             $points Punkte bei Treffer.
	 */
	public function __construct( GeoDatabase $geo, array $asns, int $points = self::DEFAULT_POINTS ) {
		$this->geo    = $geo;
		$this->asns   = $asns;
		$this->points = $points;
	}

	public function id(): string {
		return self::ID;
	}

	public function stage(): int {
		return 3;
	}

	/**
	 * Nur aktiv mit vorhandener ASN-Datenbank und aufgeloester IP.
	 *
	 * @param Context $context Bewertungs-Kontext.
	 * @return bool
	 */
	public function applies( Context $context ): bool {
		return array() !== $this->asns
			&& $this->geo->has_asn()
			&& '' !== $context->server( 'remote_addr' );
	}

	public function evaluate( Context $context ): RuleResult {
		$asn = $this->geo->asn( $context->server( 'remote_addr' ) );
		if ( $asn <= 0 || ! isset( $this->asns[ $asn ] ) ) {
			return RuleResult::none( self::ID );
		}
		return new RuleResult( $this->points, self::ID );
	}
}
