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

namespace Hafenstudios\Wellenbrecher\Channels;

use Hafenstudios\Wellenbrecher\Rest\TokenController;
use Hafenstudios\Wellenbrecher\Signals\FieldRotation;

defined( 'ABSPATH' ) || exit;

/**
 * Frontend-Seite des Kommentar-Kanals: Feld-Tausch + Honeypot ueber den
 * Filter `comment_form_field_comment` (KEIN Output-Buffering, die
 * ASB-Fragilitaetslehre), dazu die Signal-Hidden-Felder und signals.js
 * (nur auf Seiten mit Kommentarformular, Bauplan Kap. 4.4).
 *
 * Kompatibilitaets-Fallback: rendert ein Theme das Kommentarfeld selbst
 * (Filter greift nicht oder Markup ohne name="comment"), gibt es keinen
 * Feld-Tausch und keinen Honeypot; der Kanal setzt dann kein
 * Honeypot-Signal und alle uebrigen Regeln laufen normal.
 */
class CommentFormFields {

	/**
	 * Zeitstempel des letzten Feld-Tausch-Renderings. Der Kanal wertet die
	 * Feld-Tausch-Umgehung nur, wenn dieser Stempel frisch ist (2 Tage,
	 * wie das Generationen-Fenster der Feldnamen): so entstehen bei einem
	 * Theme ohne Filter keine False Positives.
	 */
	public const SWAP_RENDER_OPTION = 'wellenbrecher_swap_last_render';

	public const FIELD_TIMESTAMP = 'wb_js';
	public const FIELD_EVENTS    = 'wb_events';
	public const FIELD_TOKEN     = 'wb_token';
	public const FIELD_POW       = 'wb_pow';

	protected FieldRotation $rotation;

	/**
	 * Rendert das Proof-of-Work-Feld (nur bei aktivem Pro-Gate + PoW-Setting;
	 * mit inaktivem Gate bleibt das Formular byte-identisch zu 1.0.0).
	 */
	protected bool $pow_enabled;

	/**
	 * @param FieldRotation $rotation    Rotierende Feldnamen.
	 * @param bool          $pow_enabled Proof-of-Work-Feld rendern (Pro + Setting an).
	 */
	public function __construct( FieldRotation $rotation, bool $pow_enabled = false ) {
		$this->rotation    = $rotation;
		$this->pow_enabled = $pow_enabled;
	}

	/**
	 * Registriert die Frontend-Hooks (alle feuern nur, wenn ein
	 * Kommentarformular gerendert wird).
	 *
	 * @return void
	 */
	public function register(): void {
		add_filter( 'comment_form_field_comment', array( $this, 'swap_comment_field' ) );
		add_action( 'comment_form', array( $this, 'render_signal_fields' ) );
		add_action( 'comment_form_before', array( $this, 'enqueue_signals' ) );
	}

	/**
	 * Tauscht das echte Kommentarfeld auf den rotierten Tarnnamen und haengt
	 * das versteckte Honeypot-Feld unter dem Originalnamen 'comment' an.
	 *
	 * @param mixed $field Vom Theme/Kern gerendertes Kommentarfeld-HTML.
	 * @return mixed
	 */
	public function swap_comment_field( $field ) {
		if ( ! is_string( $field ) || '' === $field ) {
			return $field;
		}

		$rotated = $this->rotation->name( FieldRotation::PURPOSE_FIELD );
		$swapped = preg_replace( '/name=(["\'])comment\1/', 'name="' . $rotated . '"', $field, 1, $count );

		if ( null === $swapped || 0 === $count ) {
			// Markup ohne name="comment": unveraendert lassen (Fallback, s.o.).
			return $field;
		}

		$this->mark_swap_rendered();

		return $swapped . $this->honeypot_field();
	}

	/**
	 * Signal-Hidden-Felder: Timestamp nach Akismet-Muster (Server rendert
	 * Zufallswert 0-250, JS ersetzt durch Date.now()), Event-Feld, Token-Feld.
	 *
	 * @return void
	 */
	public function render_signal_fields(): void {
		echo '<input type="hidden" name="' . esc_attr( self::FIELD_TIMESTAMP ) . '" value="' . esc_attr( (string) wp_rand( 0, 250 ) ) . '" />';
		echo '<input type="hidden" name="' . esc_attr( self::FIELD_EVENTS ) . '" value="" />';
		echo '<input type="hidden" name="' . esc_attr( self::FIELD_TOKEN ) . '" value="" />';
		if ( $this->pow_enabled ) {
			echo '<input type="hidden" name="' . esc_attr( self::FIELD_POW ) . '" value="" />';
		}
	}

	/**
	 * signals.js einreihen (Vanilla, No-Build, progressive enhancement:
	 * ohne JS bleibt alles funktionsfaehig, fehlende Signale sind nur Score).
	 *
	 * @return void
	 */
	public function enqueue_signals(): void {
		wp_enqueue_script(
			'wellenbrecher-signals',
			WELLENBRECHER_URL . 'assets/js/signals.js',
			array(),
			WELLENBRECHER_VERSION,
			true
		);
		wp_localize_script(
			'wellenbrecher-signals',
			'wellenbrecherSignals',
			array(
				'tokenUrl' => esc_url_raw( rest_url( TokenController::REST_NAMESPACE . '/token' ) ),
			)
		);
	}

	/**
	 * Verstecktes Honeypot-Feld unter dem Originalnamen 'comment':
	 * clip-rect + position absolute, tabindex="-1", aria-hidden="true",
	 * autocomplete="new-password", Screenreader-Label (Bauplan Kap. 2 Nr. 2).
	 *
	 * @return string
	 */
	protected function honeypot_field(): string {
		return '<p class="wb-comment-check" style="position:absolute!important;clip:rect(1px,1px,1px,1px);width:1px;height:1px;overflow:hidden;">'
			. '<label for="wb-comment-check">' . esc_html__( 'Dieses Feld bitte leer lassen', 'wellenbrecher' ) . '</label>'
			. '<textarea id="wb-comment-check" name="comment" rows="1" cols="1" tabindex="-1" aria-hidden="true" autocomplete="new-password"></textarea>'
			. '</p>';
	}

	/**
	 * Merkt sich (hoechstens alle 12 Stunden), dass der Feld-Tausch gerendert
	 * wurde. Veraltet der Stempel (Theme-Wechsel auf eigenes Markup), faellt
	 * nur die Feld-Tausch-Erkennung weg, es entstehen keine False Positives.
	 *
	 * @return void
	 */
	protected function mark_swap_rendered(): void {
		$last = (int) get_option( self::SWAP_RENDER_OPTION, 0 );
		if ( time() - $last > 12 * HOUR_IN_SECONDS ) {
			update_option( self::SWAP_RENDER_OPTION, time() );
		}
	}
}
