<?php
/**
 * Bot Detector
 *
 * Maintains a list of known malicious bots and heuristic scoring rules.
 * Good bots (Googlebot, Bingbot …) are explicitly whitelisted.
 *
 * @package GuardianShield
 */

namespace GuardianShield;

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

class BotDetector {

	private Logger $logger;

	private const BAD_BOT_SIGNATURES = [
		'masscan', 'zgrab', 'nikto', 'sqlmap', 'havij', 'acunetix',
		'netsparker', 'openvas', 'nmap', 'metasploit', 'jbrofuzz',
		'paros', 'burpsuite', 'dirbuster', 'wfuzz', 'gobuster',
		'hydra', 'medusa', 'aircrack', 'w3af', 'skipfish',
		'python-requests/2', 'go-http-client', 'curl/7.', 'libwww-perl',
		'headlesschrome', 'phantomjs', 'selenium',
		'yandex', 'semrush', 'mj12bot', 'dotbot', 'blexbot', 'ahrefsbot',
	];

	// Good bots — never blocked.
	private const GOOD_BOT_SIGNATURES = [
		'googlebot', 'bingbot', 'slurp', 'duckduckbot', 'baiduspider',
		'facebookexternalhit', 'twitterbot', 'linkedinbot',
	];

	public function __construct( Logger $logger ) {
		$this->logger = $logger;
	}

	public function register_hooks(): void {
		// Passive logging only — actual blocking is in Firewall::run().
	}

	public function is_bad_bot(): bool {
		if ( ! (bool) gs_get_option( 'block_bad_bots', true ) ) {
			return false;
		}

		$ua = strtolower( $_SERVER['HTTP_USER_AGENT'] ?? '' );

		if ( empty( $ua ) ) {
			return false;
		}

		foreach ( self::GOOD_BOT_SIGNATURES as $sig ) {
			if ( str_contains( $ua, $sig ) ) {
				return false;
			}
		}

		foreach ( self::BAD_BOT_SIGNATURES as $sig ) {
			if ( str_contains( $ua, $sig ) ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Heuristic: calculate a "bot likelihood" score 0-100.
	 */
	public function bot_score(): int {
		$score = 0;
		$ua    = $_SERVER['HTTP_USER_AGENT'] ?? '';

		if ( empty( $ua ) )                                        $score += 40;
		if ( ! isset( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) )         $score += 20;
		if ( ! isset( $_SERVER['HTTP_ACCEPT'] ) )                  $score += 20;
		if ( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) )           $score += 5;
		if ( preg_match( '/bot|crawl|spider|scan/i', $ua ) )       $score += 25;
		if ( strlen( $ua ) < 15 )                                  $score += 15;

		return min( 100, $score );
	}
}
