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

namespace Hafenstudios\Wellenbrecher\Support;

use Hafenstudios\Core\Events\EventBus;
use Hafenstudios\Wellenbrecher\Db\SpamLogRepository;
use Hafenstudios\Wellenbrecher\Db\StatsRepository;
use Hafenstudios\Wellenbrecher\Engine\Verdict;

defined( 'ABSPATH' ) || exit;

/**
 * Zentrale Verdict-Verbuchung fuer ALLE Kanaele (W3): schreibt hs_spam_log,
 * zaehlt hs_spam_stats hoch und feuert EINMAL das Flotten-Event
 * `spam_detected` (Bauplan Kap. 10) bei jedem Nicht-pass-Verdict - auch
 * flagged; Konsumenten (Kompass, Kurato-Timeline) filtern selbst. So bleibt
 * die Event-Verdrahtung an einer Stelle statt je Kanal kopiert.
 *
 * Fail-open: ein Log-/Stats-Fehler stoppt nie den Ablauf; das Event wird
 * unabhaengig vom DB-Ergebnis gefeuert (die Erkennung hat stattgefunden).
 */
class SpamRecorder {

	/**
	 * Option, die den Zeitpunkt der allerersten Blockierung festhaelt
	 * (Hans-Meilenstein auf der Uebersichts-Seite).
	 */
	public const FIRST_BLOCK_OPTION = 'wellenbrecher_first_block_at';

	protected SpamLogRepository $log;

	protected StatsRepository $stats;

	/**
	 * @param SpamLogRepository $log   Spam-Log.
	 * @param StatsRepository   $stats Tageszaehler.
	 */
	public function __construct( SpamLogRepository $log, StatsRepository $stats ) {
		$this->log   = $log;
		$this->stats = $stats;
	}

	/**
	 * Verbucht ein Verdict: Log-Zeile, Tageszaehler, Event.
	 *
	 * @param string             $verdict blocked|quarantined|flagged.
	 * @param Verdict            $result  Engine-Urteil (Score + Breakdown).
	 * @param array<string, mixed> $base  Log-Basisdaten (channel Pflicht; ip_hmac,
	 *                                    email_domain, excerpt, payload, form_id,
	 *                                    comment_id optional).
	 * @return int Neue Log-ID (0 bei Fehler).
	 */
	public function record( string $verdict, Verdict $result, array $base ): int {
		$channel = (string) ( $base['channel'] ?? '' );
		$id      = 0;

		try {
			$id = $this->log->insert(
				array_merge(
					$base,
					array(
						'verdict' => $verdict,
						'score'   => $result->score,
						'reasons' => $result->reasons,
					)
				)
			);
			$this->stats->increment( $channel, $verdict );

			if ( SpamLogRepository::VERDICT_BLOCKED === $verdict && false === get_option( self::FIRST_BLOCK_OPTION, false ) ) {
				update_option( self::FIRST_BLOCK_OPTION, time() );
			}
		} catch ( \Throwable $e ) {
			error_log( 'Wellenbrecher: Log-Eintrag fehlgeschlagen (' . $e->getMessage() . ')' ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
		}

		/**
		 * Flotten-Event bei jedem Nicht-pass-Verdict (Bauplan Kap. 10). Konsumenten
		 * (Kompass-Kacheln, Kurato-Kontakt-Timeline) filtern den Verdict selbst.
		 *
		 * @param string             $channel Kanal (comment|registration|leadlotse|...).
		 * @param string             $verdict blocked|quarantined|flagged.
		 * @param int                $score   Gesamtscore.
		 * @param array<string, int> $reasons Score-Breakdown rule_id => Punkte.
		 */
		EventBus::emit( 'spam_detected', $channel, $verdict, $result->score, $result->reasons );

		return $id;
	}
}
