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

namespace Hafenstudios\Leadlotse\Submit;

use Hafenstudios\Leadlotse\Fields\Types\HoneypotField;

defined( 'ABSPATH' ) || exit;

/**
 * Spam-Kette laut D7, in dieser Reihenfolge:
 *
 * 1. Honeypot: unsichtbares Feld befuellt -> still verwerfen (200 OK vortaeuschen).
 * 2. Zeitfalle: Formular in unter X Sekunden abgesendet -> Fehler.
 * 3. Rate Limiting pro IP+Formular (Transient-basiert).
 *
 * Kein externes Captcha in 1.0; Turnstile/Friendly Captcha koennen sich ueber
 * den Filter `hafenstudios/leadlotse_spam_check` einklinken (Pro-Erweiterungspunkt).
 */
final class SpamGuard {

	/**
	 * Name des Zeitstempel-Felds im Formular.
	 */
	public const TIMESTAMP_FIELD = 'll_ts';

	/**
	 * Erzeugt den signierten Zeitstempel-Token fuer das Formular-Rendering.
	 *
	 * @param int|null $time Unix-Zeit (Default: jetzt).
	 * @return string
	 */
	public function create_timestamp_token( ?int $time = null ): string {
		$time = $time ?? time();
		$sig  = hash_hmac( 'sha256', 'll_ts|' . $time, (string) wp_salt( 'nonce' ) );
		return $time . '.' . $sig;
	}

	/**
	 * Schritt 1: Ist der Honeypot befuellt? Dann still verwerfen.
	 *
	 * @param array<string, mixed> $input Eingereichte Rohdaten.
	 * @return bool True = Spam (still verwerfen).
	 */
	public function is_honeypot_filled( array $input ): bool {
		$value = $input[ HoneypotField::FIELD_ID ] ?? '';
		if ( is_array( $value ) ) {
			return true;
		}
		return '' !== trim( (string) $value );
	}

	/**
	 * Schritt 2: Zeitfalle. Prueft Signatur und Mindest-Ausfuellzeit.
	 *
	 * @param string $token       Token aus dem Feld ll_ts.
	 * @param int    $min_seconds Mindestzeit in Sekunden.
	 * @return string|null Fehlermeldung oder null bei Erfolg.
	 */
	public function check_time_trap( string $token, int $min_seconds ): ?string {
		$error = __( 'Das Formular wurde zu schnell abgesendet. Bitte warten Sie einen Moment und senden Sie es erneut.', 'leadlotse' );

		$parts = explode( '.', $token, 2 );
		if ( 2 !== count( $parts ) ) {
			return $error;
		}

		list( $time, $sig ) = $parts;
		$time               = (int) $time;
		$expected           = hash_hmac( 'sha256', 'll_ts|' . $time, (string) wp_salt( 'nonce' ) );

		if ( ! hash_equals( $expected, $sig ) ) {
			return $error;
		}
		if ( $min_seconds > 0 && ( time() - $time ) < $min_seconds ) {
			return $error;
		}
		// Token aelter als 24 Stunden: Formular-Seite war zu lange offen.
		if ( ( time() - $time ) > DAY_IN_SECONDS ) {
			return __( 'Die Formular-Seite war zu lange geoeffnet. Bitte laden Sie die Seite neu und senden Sie das Formular erneut.', 'leadlotse' );
		}

		return null;
	}

	/**
	 * Schritt 3: Rate Limiting pro IP und Formular (Transient-basiert).
	 *
	 * @param int $form_id        Formular-ID.
	 * @param int $max_requests   Maximale Submits im Fenster.
	 * @param int $window_seconds Fensterlaenge in Sekunden.
	 * @return string|null Fehlermeldung oder null bei Erfolg.
	 */
	public function check_rate_limit( int $form_id, int $max_requests, int $window_seconds ): ?string {
		$ip  = isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : '';
		$key = 'll_rate_' . md5( $ip . '|' . $form_id );

		$count = (int) get_transient( $key );
		if ( $count >= $max_requests ) {
			return __( 'Es wurden zu viele Anfragen in kurzer Zeit gesendet. Bitte versuchen Sie es in einigen Minuten erneut.', 'leadlotse' );
		}

		set_transient( $key, $count + 1, $window_seconds );

		return null;
	}
}
