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

namespace Hafenstudios\Wellenbrecher\Admin;

use Hafenstudios\Core\Settings\Settings;
use Hafenstudios\Wellenbrecher\Db\StatsRepository;
use Hafenstudios\Wellenbrecher\Rules\RuleRegistry;
use Hafenstudios\Wellenbrecher\Support\ObservationMode;

defined( 'ABSPATH' ) || exit;

/**
 * Einstellungs-Seite v1 (Bauplan Kap. 8): serverseitig ueber admin-post
 * (Muster Kurato ListsPage), Cap hs_manage_spam, Sie-Form, keine Em-Dashes.
 *
 * Bloecke: Beobachtungsmodus (Banner + Scharfschalten), Zonen mit "Zurueck
 * auf Standard", Aufbewahrung, Trusted-Proxy-Header, XML-RPC-Option, eigene
 * Keyword-Liste, Regel-Tabelle (Toggle + Punkte je Regel).
 */
class SettingsPage {

	public const SLUG        = 'wellenbrecher-settings';
	public const ACTION_SAVE = 'wellenbrecher_save_settings';
	public const ACTION_ARM  = 'wellenbrecher_arm';

	public const QUARANTINE_CHOICES = array( 7, 15, 30, 60 );

	protected Settings $settings;

	protected StatsRepository $stats;

	protected ObservationMode $observation;

	/**
	 * Pro-Block (Bayes/PoW) nur bei aktivem Pro-Gate zeigen und speichern.
	 */
	protected bool $pro;

	/**
	 * @param Settings        $settings    Wellenbrecher-Settings.
	 * @param StatsRepository $stats       Tageszaehler.
	 * @param ObservationMode $observation Beobachtungsmodus.
	 * @param bool            $pro         Pro-Gate aktiv (blendet den Pro-Block ein).
	 */
	public function __construct( Settings $settings, StatsRepository $stats, ObservationMode $observation, bool $pro = false ) {
		$this->settings    = $settings;
		$this->stats       = $stats;
		$this->observation = $observation;
		$this->pro         = $pro;
	}

	/**
	 * Registriert die admin-post-Handler.
	 *
	 * @return void
	 */
	public function register(): void {
		add_action( 'admin_post_' . self::ACTION_SAVE, array( $this, 'handle_save' ) );
		add_action( 'admin_post_' . self::ACTION_ARM, array( $this, 'handle_arm' ) );
	}

	/**
	 * Rendert die Seite.
	 *
	 * @return void
	 */
	public function render(): void {
		if ( ! current_user_can( 'hs_manage_spam' ) ) {
			wp_die( esc_html__( 'Sie haben keine Berechtigung fuer die Wellenbrecher-Einstellungen.', 'wellenbrecher' ) );
		}

		$notice = isset( $_GET['msg'] ) ? sanitize_key( wp_unslash( $_GET['msg'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nur Anzeige einer Statusmeldung nach Redirect.

		echo '<div class="wrap">';
		echo '<h1 class="wp-heading-inline">' . esc_html__( 'Wellenbrecher: Einstellungen', 'wellenbrecher' ) . '</h1>';
		echo '<hr class="wp-header-end" />';

		if ( 'saved' === $notice ) {
			echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__( 'Einstellungen gespeichert.', 'wellenbrecher' ) . '</p></div>';
		} elseif ( 'armed' === $notice ) {
			echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__( 'Wellenbrecher ist jetzt scharf geschaltet. Auffaellige Kommentare werden ab sofort blockiert oder in den Spam-Ordner gestellt.', 'wellenbrecher' ) . '</p></div>';
		} elseif ( 'invalid_zones' === $notice ) {
			echo '<div class="notice notice-error"><p>' . esc_html__( 'Die Zonen-Schwellen wurden nicht gespeichert: Die Quarantaene-Schwelle muss groesser als 0 und kleiner als die Block-Schwelle sein. Bitte korrigieren Sie die Werte.', 'wellenbrecher' ) . '</p></div>';
		}

		$this->render_observation_block();
		$this->render_form();

		echo '</div>';
	}

	/**
	 * Beobachtungsmodus-Block: Ueberschrift plus das gemeinsame Banner
	 * (ObservationNotice, auch von der Uebersicht genutzt) bzw. der Hinweis,
	 * dass der Modus beendet ist.
	 *
	 * @return void
	 */
	protected function render_observation_block(): void {
		echo '<h2>' . esc_html__( 'Beobachtungsmodus', 'wellenbrecher' ) . '</h2>';

		if ( ! $this->observation->is_active() ) {
			echo '<p>' . esc_html__( 'Der Beobachtungsmodus ist beendet. Wellenbrecher ist scharf geschaltet: Auffaellige Vorgaenge werden blockiert oder in Quarantaene gestellt.', 'wellenbrecher' ) . '</p>';
			return;
		}

		ObservationNotice::render( $this->settings, $this->stats, $this->observation );
	}

	/**
	 * Das Einstellungs-Formular.
	 *
	 * @return void
	 */
	protected function render_form(): void {
		$quarantine = (int) $this->settings->get( 'zones.quarantine', 40 );
		$block      = (int) $this->settings->get( 'zones.block', 80 );
		$retention  = (int) $this->settings->get( 'retention.quarantine_days', 30 );
		$proxy      = (string) $this->settings->get( 'proxy.trusted_header', '' );
		$xmlrpc     = ! empty( $this->settings->get( 'xmlrpc.disable_comments', false ) );
		$keywords   = $this->settings->get( 'lists.custom_keywords', array() );
		$keywords   = is_array( $keywords ) ? implode( "\n", array_map( 'strval', $keywords ) ) : '';

		echo '<form method="post" action="' . esc_url( admin_url( 'admin-post.php' ) ) . '">';
		echo '<input type="hidden" name="action" value="' . esc_attr( self::ACTION_SAVE ) . '" />';
		wp_nonce_field( self::ACTION_SAVE );

		echo '<h2>' . esc_html__( 'Zonen', 'wellenbrecher' ) . '</h2>';
		echo '<p>' . esc_html__( 'Kommentare unterhalb der Quarantaene-Schwelle werden durchgelassen, ab der Quarantaene-Schwelle in den Spam-Ordner gestellt und ab der Block-Schwelle mit Status 403 abgewiesen, bevor sie die Datenbank erreichen.', 'wellenbrecher' ) . '</p>';
		echo '<table class="form-table" role="presentation"><tbody>';
		echo '<tr><th scope="row"><label for="wb-zone-quarantine">' . esc_html__( 'Quarantaene ab Punktzahl', 'wellenbrecher' ) . '</label></th>';
		echo '<td><input type="number" id="wb-zone-quarantine" name="zones[quarantine]" value="' . esc_attr( (string) $quarantine ) . '" min="1" max="500" class="small-text" /></td></tr>';
		echo '<tr><th scope="row"><label for="wb-zone-block">' . esc_html__( 'Block ab Punktzahl', 'wellenbrecher' ) . '</label></th>';
		echo '<td><input type="number" id="wb-zone-block" name="zones[block]" value="' . esc_attr( (string) $block ) . '" min="2" max="500" class="small-text" /> ';
		echo '<button type="submit" name="reset_zones" value="1" class="button">' . esc_html__( 'Zurueck auf Standard (40/80)', 'wellenbrecher' ) . '</button></td></tr>';
		echo '</tbody></table>';

		echo '<h2>' . esc_html__( 'Aufbewahrung', 'wellenbrecher' ) . '</h2>';
		echo '<table class="form-table" role="presentation"><tbody>';
		echo '<tr><th scope="row"><label for="wb-retention">' . esc_html__( 'Quarantaene-Eintraege im Log aufbewahren', 'wellenbrecher' ) . '</label></th><td>';
		echo '<select id="wb-retention" name="retention[quarantine_days]">';
		foreach ( self::QUARANTINE_CHOICES as $choice ) {
			echo '<option value="' . esc_attr( (string) $choice ) . '"' . selected( $retention, $choice, false ) . '>';
			/* translators: %d: Anzahl Tage */
			echo esc_html( sprintf( __( '%d Tage', 'wellenbrecher' ), $choice ) );
			echo '</option>';
		}
		echo '</select>';
		echo '<p class="description">' . esc_html__( 'Block-Eintraege werden nach 7 Tagen geloescht, die aggregierte Statistik enthaelt keinen Personenbezug und bleibt erhalten.', 'wellenbrecher' ) . '</p>';
		echo '</td></tr>';
		echo '</tbody></table>';

		echo '<h2>' . esc_html__( 'Netzwerk', 'wellenbrecher' ) . '</h2>';
		echo '<table class="form-table" role="presentation"><tbody>';
		echo '<tr><th scope="row"><label for="wb-proxy">' . esc_html__( 'Trusted-Proxy-Header', 'wellenbrecher' ) . '</label></th><td>';
		echo '<input type="text" id="wb-proxy" name="proxy[trusted_header]" value="' . esc_attr( $proxy ) . '" class="regular-text" placeholder="X-Forwarded-For" />';
		echo '<p class="description">' . esc_html__( 'Nur setzen, wenn Ihre Website hinter einem vertrauenswuerdigen Proxy oder CDN laeuft und dieser Header dort gesetzt wird. Ein falsch gesetzter Header macht die IP-Erkennung faelschbar, weil Absender ihn frei erfinden koennen. Im Zweifel leer lassen.', 'wellenbrecher' ) . '</p>';
		echo '</td></tr>';
		echo '<tr><th scope="row">' . esc_html__( 'XML-RPC', 'wellenbrecher' ) . '</th><td>';
		echo '<label><input type="checkbox" name="xmlrpc[disable_comments]" value="1"' . checked( $xmlrpc, true, false ) . ' /> ' . esc_html__( 'Kommentare und Pingbacks ueber XML-RPC deaktivieren', 'wellenbrecher' ) . '</label>';
		echo '<p class="description">' . esc_html__( 'Entfernt nur die Methoden pingback.ping und wp.newComment. XML-RPC selbst bleibt aktiv, damit Apps und Dienste wie Jetpack weiter funktionieren.', 'wellenbrecher' ) . '</p>';
		echo '</td></tr>';
		echo '</tbody></table>';

		echo '<h2>' . esc_html__( 'Eigene Keyword-Liste', 'wellenbrecher' ) . '</h2>';
		echo '<p>' . esc_html__( 'Ein Begriff pro Zeile. Trifft ein Begriff auf Inhalt, Autor-Name oder Autor-URL zu, gibt es die in der Regel-Tabelle eingestellten Punkte. Einfache Textsuche ohne Gross-/Kleinschreibung, keine regulaeren Ausdruecke.', 'wellenbrecher' ) . '</p>';
		echo '<textarea name="custom_keywords" rows="5" class="large-text code">' . esc_textarea( $keywords ) . '</textarea>';

		if ( $this->pro ) {
			$this->render_pro_block();
		}

		$this->render_rules_table();

		echo '<p><button type="submit" class="button button-primary">' . esc_html__( 'Einstellungen speichern', 'wellenbrecher' ) . '</button></p>';
		echo '</form>';
	}

	/**
	 * Pro-Block (W5a): Bayes-Filter (mit Korpus-Status) und eskalierender
	 * Proof-of-Work. Nur bei aktivem Pro-Gate gerendert.
	 *
	 * @return void
	 */
	protected function render_pro_block(): void {
		$bayes_on = ! empty( $this->settings->get( 'pro.bayes.enabled', true ) );
		$ham      = (int) $this->settings->get( 'pro.bayes.ham_messages', 0 );
		$spam     = (int) $this->settings->get( 'pro.bayes.spam_messages', 0 );
		$active   = $ham >= 50 && $spam >= 50;

		$pow_on = ! empty( $this->settings->get( 'pro.pow.enabled', false ) );
		$diff   = (int) $this->settings->get( 'pro.pow.difficulty', 4 );

		echo '<h2>' . esc_html__( 'Pro-Funktionen', 'wellenbrecher' ) . '</h2>';
		echo '<table class="form-table" role="presentation"><tbody>';

		echo '<tr><th scope="row">' . esc_html__( 'Bayes-Filter', 'wellenbrecher' ) . '</th><td>';
		echo '<label><input type="checkbox" name="pro[bayes][enabled]" value="1"' . checked( $bayes_on, true, false ) . ' /> ' . esc_html__( 'Aus Ihrer Moderation lernen (lokal, kein externer Dienst)', 'wellenbrecher' ) . '</label>';
		echo '<p class="description">';
		if ( $active ) {
			/* translators: 1: Anzahl Ham, 2: Anzahl Spam */
			echo esc_html( sprintf( __( 'Aktiv. Trainiert aus %1$d Ham- und %2$d Spam-Nachrichten.', 'wellenbrecher' ), $ham, $spam ) );
		} else {
			/* translators: 1: Anzahl Ham, 2: Anzahl Spam */
			echo esc_html( sprintf( __( 'Lernt noch (aktiv ab 50 Ham + 50 Spam). Bisher %1$d Ham, %2$d Spam. Markieren Sie Kommentare als Spam bzw. holen Sie sie zurueck, um den Filter zu trainieren.', 'wellenbrecher' ), $ham, $spam ) );
		}
		echo '</p></td></tr>';

		echo '<tr><th scope="row">' . esc_html__( 'Proof-of-Work', 'wellenbrecher' ) . '</th><td>';
		echo '<label><input type="checkbox" name="pro[pow][enabled]" value="1"' . checked( $pow_on, true, false ) . ' /> ' . esc_html__( 'Rechen-Nachweis fuer auffaellige Besucher verlangen', 'wellenbrecher' ) . '</label>';
		echo ' <label style="margin-left:12px;">' . esc_html__( 'Schwierigkeit', 'wellenbrecher' ) . ' <input type="number" name="pro[pow][difficulty]" value="' . esc_attr( (string) $diff ) . '" min="3" max="6" class="small-text" /></label>';
		echo '<p class="description">' . esc_html__( 'Eskalierend: nur Besuchern, die zuletzt auffaellig waren, wird im Hintergrund eine kleine Rechenaufgabe gestellt (unsichtbar, per JavaScript geloest). Unauffaellige Besucher merken nichts. Ohne JavaScript oder ueber http bleibt es beim Score.', 'wellenbrecher' ) . '</p>';
		echo '</td></tr>';

		echo '</tbody></table>';
	}

	/**
	 * Regel-Tabelle: Toggle + Punkte je Regel, Defaults aus dem Katalog.
	 *
	 * @return void
	 */
	protected function render_rules_table(): void {
		$defaults = RuleRegistry::default_rule_settings();
		$rules    = $this->settings->get( 'rules', array() );
		$rules    = is_array( $rules ) ? $rules : array();

		echo '<h2>' . esc_html__( 'Regeln', 'wellenbrecher' ) . '</h2>';
		echo '<p>' . esc_html__( 'Jede Regel laesst sich einzeln abschalten und gewichten. Negative Punkte sind Vertrauens-Abzuege. Die Vorgabe-Spalte zeigt den Katalogwert.', 'wellenbrecher' ) . '</p>';
		echo '<table class="wp-list-table widefat fixed striped"><thead><tr>';
		echo '<th scope="col">' . esc_html__( 'Regel', 'wellenbrecher' ) . '</th>';
		echo '<th scope="col">' . esc_html__( 'Aktiv', 'wellenbrecher' ) . '</th>';
		echo '<th scope="col">' . esc_html__( 'Punkte', 'wellenbrecher' ) . '</th>';
		echo '<th scope="col">' . esc_html__( 'Vorgabe', 'wellenbrecher' ) . '</th>';
		echo '</tr></thead><tbody>';

		foreach ( $defaults as $id => $default ) {
			$enabled = (bool) ( $rules[ $id ]['enabled'] ?? true );
			$points  = (int) ( $rules[ $id ]['points'] ?? $default['points'] );

			echo '<tr>';
			echo '<td>' . esc_html( RuleLabels::label( (string) $id ) ) . '</td>';
			echo '<td><input type="checkbox" name="rules[' . esc_attr( (string) $id ) . '][enabled]" value="1"' . checked( $enabled, true, false ) . ' aria-label="' . esc_attr( RuleLabels::label( (string) $id ) ) . '" /></td>';
			echo '<td><input type="number" name="rules[' . esc_attr( (string) $id ) . '][points]" value="' . esc_attr( (string) $points ) . '" min="-100" max="100" class="small-text" /></td>';
			echo '<td>' . esc_html( (string) (int) $default['points'] ) . '</td>';
			echo '</tr>';
		}

		echo '</tbody></table>';
	}

	/**
	 * Speichert die Einstellungen (admin-post).
	 *
	 * @return void
	 */
	public function handle_save(): void {
		if ( ! current_user_can( 'hs_manage_spam' ) ) {
			wp_die( esc_html__( 'Keine Berechtigung.', 'wellenbrecher' ) );
		}
		check_admin_referer( self::ACTION_SAVE );

		// Zonen (mit "Zurueck auf Standard").
		if ( ! empty( $_POST['reset_zones'] ) ) {
			$quarantine = 40;
			$block      = 80;
		} else {
			$zones      = isset( $_POST['zones'] ) && is_array( $_POST['zones'] ) ? wp_unslash( $_POST['zones'] ) : array(); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Werte werden unten zu int gecastet.
			$quarantine = (int) ( $zones['quarantine'] ?? 40 );
			$block      = (int) ( $zones['block'] ?? 80 );
			if ( $quarantine < 1 || $block <= $quarantine ) {
				$this->redirect( 'invalid_zones' );
			}
		}
		$this->settings->set( 'zones', array( 'quarantine' => $quarantine, 'block' => $block ) );

		// Aufbewahrung (nur die vier erlaubten Werte).
		$retention = isset( $_POST['retention']['quarantine_days'] ) ? (int) $_POST['retention']['quarantine_days'] : 30;
		if ( ! in_array( $retention, self::QUARANTINE_CHOICES, true ) ) {
			$retention = 30;
		}
		$this->settings->set( 'retention.quarantine_days', $retention );

		// Trusted-Proxy-Header.
		$proxy = isset( $_POST['proxy']['trusted_header'] ) ? sanitize_text_field( wp_unslash( $_POST['proxy']['trusted_header'] ) ) : '';
		$this->settings->set( 'proxy.trusted_header', $proxy );

		// XML-RPC-Option (Checkbox: fehlend = aus).
		$this->settings->set( 'xmlrpc.disable_comments', ! empty( $_POST['xmlrpc']['disable_comments'] ) );

		// Eigene Keyword-Liste (eine pro Zeile, leere Zeilen entfernen).
		$raw      = isset( $_POST['custom_keywords'] ) ? sanitize_textarea_field( wp_unslash( $_POST['custom_keywords'] ) ) : '';
		$keywords = array_values( array_filter( array_map( 'trim', explode( "\n", $raw ) ) ) );
		$this->settings->set( 'lists.custom_keywords', $keywords );

		// Regel-Tabelle: enabled/points je bekannter Regel; weitere
		// Regel-Optionen (z.B. Rate-Limit-Fenster) bleiben unangetastet.
		$posted  = isset( $_POST['rules'] ) && is_array( $_POST['rules'] ) ? wp_unslash( $_POST['rules'] ) : array(); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Werte werden unten gecastet.
		$current = $this->settings->get( 'rules', array() );
		$current = is_array( $current ) ? $current : array();

		foreach ( RuleRegistry::default_rule_settings() as $id => $default ) {
			$row                = is_array( $current[ $id ] ?? null ) ? $current[ $id ] : array();
			$row['enabled']     = ! empty( $posted[ $id ]['enabled'] );
			$row['points']      = isset( $posted[ $id ]['points'] ) ? (int) $posted[ $id ]['points'] : (int) $default['points'];
			$current[ $id ]     = $row;
		}
		$this->settings->set( 'rules', $current );

		// Pro-Block (nur bei aktivem Gate speichern; die Korpus-Zaehler bleiben
		// unangetastet - sie pflegt der Trainer).
		if ( $this->pro ) {
			$this->settings->set( 'pro.bayes.enabled', ! empty( $_POST['pro']['bayes']['enabled'] ) );
			$this->settings->set( 'pro.pow.enabled', ! empty( $_POST['pro']['pow']['enabled'] ) );
			$diff = isset( $_POST['pro']['pow']['difficulty'] ) ? (int) $_POST['pro']['pow']['difficulty'] : 4;
			$this->settings->set( 'pro.pow.difficulty', max( 3, min( 6, $diff ) ) );
		}

		$this->redirect( 'saved' );
	}

	/**
	 * Scharfschalten: beendet den Beobachtungsmodus (admin-post + Nonce).
	 *
	 * @return void
	 */
	public function handle_arm(): void {
		if ( ! current_user_can( 'hs_manage_spam' ) ) {
			wp_die( esc_html__( 'Keine Berechtigung.', 'wellenbrecher' ) );
		}
		check_admin_referer( self::ACTION_ARM );

		$this->observation->disable();

		$this->redirect( 'armed' );
	}

	/**
	 * Zurueck zur Einstellungs-Seite mit Statusmeldung.
	 *
	 * @param string $msg Statuscode.
	 * @return void
	 */
	protected function redirect( string $msg ): void {
		wp_safe_redirect( add_query_arg( array( 'page' => self::SLUG, 'msg' => $msg ), admin_url( 'admin.php' ) ) );
		exit;
	}
}
