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

namespace Hafenstudios\Wellenbrecher\Rules;

use Hafenstudios\Wellenbrecher\Db\TokenRepository;
use Hafenstudios\Wellenbrecher\Engine\Context;
use Hafenstudios\Wellenbrecher\Engine\RuleInterface;
use Hafenstudios\Wellenbrecher\Engine\RuleResult;
use Hafenstudios\Wellenbrecher\Pro\Bayes\Classifier;
use Hafenstudios\Wellenbrecher\Pro\Bayes\Tokenizer;

defined( 'ABSPATH' ) || exit;

/**
 * Bayes-Regel (Pro, Stufe 3, Bauplan Kap. 6 / Technik A6). Klassifiziert den
 * Inhalt und staffelt die Punkte nach Wahrscheinlichkeit:
 * p > 0,95 -> +30, p > 0,8 -> +15, p < 0,2 -> -15, sonst kein Treffer.
 *
 * Aktiv erst ab 50 Ham + 50 Spam Korpus (applies() prueft den Bestand); vorher
 * trainiert der Trainer still weiter, die Regel bleibt stumm. Die Punkte sind
 * bewusst nicht das einfache rules.{id}.points-Modell (gestaffelt), darum
 * implementiert die Regel RuleInterface direkt und steht nicht im Free-
 * Regelkatalog.
 */
class BayesRule implements RuleInterface {

	public const ID = 'bayes';

	public const MIN_CORPUS = 50;

	public const POINTS_HIGH = 30;
	public const POINTS_MID  = 15;
	public const POINTS_HAM  = -15;

	protected Tokenizer $tokenizer;

	protected Classifier $classifier;

	protected TokenRepository $tokens;

	protected int $ham_total;

	protected int $spam_total;

	protected int $min_corpus;

	/**
	 * @param Tokenizer       $tokenizer  Tokenisierung.
	 * @param Classifier      $classifier Klassifikator.
	 * @param TokenRepository $tokens     Token-Zaehler.
	 * @param int             $ham_total  Trainierte Ham-Nachrichten (aus Settings).
	 * @param int             $spam_total Trainierte Spam-Nachrichten (aus Settings).
	 * @param int             $min_corpus Mindestbestand je Klasse.
	 */
	public function __construct( Tokenizer $tokenizer, Classifier $classifier, TokenRepository $tokens, int $ham_total, int $spam_total, int $min_corpus = self::MIN_CORPUS ) {
		$this->tokenizer  = $tokenizer;
		$this->classifier = $classifier;
		$this->tokens     = $tokens;
		$this->ham_total  = $ham_total;
		$this->spam_total = $spam_total;
		$this->min_corpus = $min_corpus;
	}

	public function id(): string {
		return self::ID;
	}

	public function stage(): int {
		return 3;
	}

	/**
	 * Nur aktiv, wenn genug Korpus trainiert ist (50 Ham + 50 Spam).
	 *
	 * @param Context $context Bewertungs-Kontext.
	 * @return bool
	 */
	public function applies( Context $context ): bool {
		return $this->ham_total >= $this->min_corpus && $this->spam_total >= $this->min_corpus;
	}

	public function evaluate( Context $context ): RuleResult {
		$text = trim(
			$context->field( 'content' ) . ' '
			. $context->field( 'author_name' ) . ' '
			. $context->field( 'author_url' ) . ' '
			. $context->field( 'author_email' )
		);

		$tokens = $this->tokenizer->tokenize( $text );
		if ( array() === $tokens ) {
			return RuleResult::none( self::ID );
		}

		$p = $this->classifier->classify( $this->tokens->counts( $tokens ), $this->ham_total, $this->spam_total );

		if ( $p > 0.95 ) {
			return new RuleResult( self::POINTS_HIGH, self::ID );
		}
		if ( $p > 0.8 ) {
			return new RuleResult( self::POINTS_MID, self::ID );
		}
		if ( $p < 0.2 ) {
			return new RuleResult( self::POINTS_HAM, self::ID );
		}
		return RuleResult::none( self::ID );
	}
}
