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

namespace Hafenstudios\Wellenbrecher\Db;

defined( 'ABSPATH' ) || exit;

/**
 * Zugriff auf hs_spam_tokens (Pro/Bayes, Bauplan Kap. 5): token PK, ham_count,
 * spam_count. Die Tabelle enthaelt bewusst keine PII (E-Mail-Adressen werden vor
 * der Tokenisierung auf die Domain reduziert, siehe Tokenizer).
 *
 * Nicht final und mit ueberschreibbaren DB-Methoden (Flotten-Testkonvention K4):
 * Unit-Tests spiegeln counts()/learn() ueber ein In-Memory-Double, sodass die
 * Trainings- und Klassifikationslogik ohne echtes wpdb pruefbar bleibt.
 */
class TokenRepository {

	/**
	 * Tabellenname.
	 *
	 * @return string
	 */
	public function table(): string {
		return Schema::table( 'spam_tokens' );
	}

	/**
	 * Liest ham/spam-Zaehler fuer eine Token-Menge (EIN SELECT ... WHERE token
	 * IN (...), Bauplan Kap. 5 / Technik A6).
	 *
	 * @param array<int, string> $tokens Tokens.
	 * @return array<string, array{ham: int, spam: int}>
	 */
	public function counts( array $tokens ): array {
		global $wpdb;

		$tokens = $this->normalize( $tokens );
		if ( array() === $tokens ) {
			return array();
		}

		$table  = $this->table();
		$places = implode( ',', array_fill( 0, count( $tokens ), '%s' ) );
		// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Tabellenname aus eigener Definition, Platzhalter prepared.
		$rows = (array) $wpdb->get_results(
			$wpdb->prepare( "SELECT token, ham_count, spam_count FROM {$table} WHERE token IN ({$places})", $tokens ),
			ARRAY_A
		);

		$out = array();
		foreach ( $rows as $row ) {
			$out[ (string) ( $row['token'] ?? '' ) ] = array(
				'ham'  => (int) ( $row['ham_count'] ?? 0 ),
				'spam' => (int) ( $row['spam_count'] ?? 0 ),
			);
		}
		return $out;
	}

	/**
	 * Zaehlt die Tokens einer Nachricht in der gewaehlten Klasse hoch (je Token
	 * +1, Upsert). Ein Multi-Row-INSERT mit ON DUPLICATE KEY UPDATE haelt es bei
	 * einer Abfrage je Trainingsschritt.
	 *
	 * @param array<int, string> $tokens   Eindeutige Tokens der Nachricht.
	 * @param string             $category 'ham' oder 'spam'.
	 * @return void
	 */
	public function learn( array $tokens, string $category ): void {
		global $wpdb;

		$tokens = $this->normalize( $tokens );
		if ( array() === $tokens ) {
			return;
		}

		$is_spam = ( 'spam' === $category );
		$column  = $is_spam ? 'spam_count' : 'ham_count';
		$table   = $this->table();

		$rows   = array();
		$params = array();
		foreach ( $tokens as $token ) {
			$rows[]   = '(%s, %d, %d)';
			$params[] = $token;
			$params[] = $is_spam ? 0 : 1;
			$params[] = $is_spam ? 1 : 0;
		}

		$sql = "INSERT INTO {$table} (token, ham_count, spam_count) VALUES " . implode( ',', $rows )
			. " ON DUPLICATE KEY UPDATE {$column} = {$column} + 1";
		// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Tabellenname/Platzhalter aus eigener Definition, Werte prepared.
		$wpdb->query( $wpdb->prepare( $sql, $params ) );
	}

	/**
	 * Normalisiert eine Token-Liste: nicht-leer, gekappt auf 191 Zeichen (PK),
	 * eindeutig.
	 *
	 * @param array<int, string> $tokens Tokens.
	 * @return array<int, string>
	 */
	protected function normalize( array $tokens ): array {
		$clean = array();
		foreach ( $tokens as $token ) {
			$token = trim( (string) $token );
			if ( '' === $token ) {
				continue;
			}
			$clean[] = mb_substr( $token, 0, 191 );
		}
		return array_values( array_unique( $clean ) );
	}
}
