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

namespace Hafenstudios\Wellenbrecher\Signals;

defined( 'ABSPATH' ) || exit;

/**
 * HMAC-Formular-Token aus wp_salt + Datumsfenster (Bauplan Kap. 4.4).
 *
 * Der Token ist fuer alle Besucher gleich (kein State, page-cache-fest) und
 * an ein UTC-Tagesfenster gebunden. Gueltig sind zwei Generationen (heute
 * und gestern), damit Cache-TTLs bis 48 Stunden nicht brechen; je nach
 * Ausspielzeitpunkt ergibt das eine Restgueltigkeit von 24 bis 48 Stunden.
 * Aeltere, aber echte Token (bis GRACE_DAYS zurueck) gelten als "abgelaufen"
 * (schwaches Signal +10), alles andere als "fehlend" (+30). Fehlende Token
 * sind immer nur Score, nie alleiniger Block (No-JS, REST-Clients).
 */
class TokenService {

	public const STATUS_VALID   = 'valid';
	public const STATUS_EXPIRED = 'expired';
	public const STATUS_MISSING = 'missing';

	/**
	 * Anzahl gueltiger Generationen (heute + gestern).
	 */
	public const VALID_GENERATIONS = 2;

	/**
	 * Wie viele Tage zurueck ein echter Token noch als "abgelaufen" statt
	 * "fehlend" erkannt wird.
	 */
	public const GRACE_DAYS = 7;

	/**
	 * Token der aktuellen Generation.
	 *
	 * @param int|null $time Unix-Zeit (Default: jetzt).
	 * @return string
	 */
	public function current( ?int $time = null ): string {
		return $this->for_day( 0, $time );
	}

	/**
	 * Prueft einen eingereichten Token.
	 *
	 * @param string   $token Eingereichter Token.
	 * @param int|null $time  Unix-Zeit (Default: jetzt).
	 * @return string STATUS_VALID | STATUS_EXPIRED | STATUS_MISSING.
	 */
	public function status( string $token, ?int $time = null ): string {
		if ( '' === $token ) {
			return self::STATUS_MISSING;
		}
		for ( $days_ago = 0; $days_ago < self::VALID_GENERATIONS; $days_ago++ ) {
			if ( hash_equals( $this->for_day( $days_ago, $time ), $token ) ) {
				return self::STATUS_VALID;
			}
		}
		for ( $days_ago = self::VALID_GENERATIONS; $days_ago <= self::GRACE_DAYS; $days_ago++ ) {
			if ( hash_equals( $this->for_day( $days_ago, $time ), $token ) ) {
				return self::STATUS_EXPIRED;
			}
		}
		return self::STATUS_MISSING;
	}

	/**
	 * Ist der Token aktuell gueltig (heute oder gestern)?
	 *
	 * @param string   $token Eingereichter Token.
	 * @param int|null $time  Unix-Zeit (Default: jetzt).
	 * @return bool
	 */
	public function is_valid( string $token, ?int $time = null ): bool {
		return self::STATUS_VALID === $this->status( $token, $time );
	}

	/**
	 * Token fuer ein bestimmtes UTC-Tagesfenster.
	 *
	 * @param int      $days_ago Tage zurueck (0 = heute).
	 * @param int|null $time     Bezugszeit (Default: jetzt).
	 * @return string
	 */
	protected function for_day( int $days_ago, ?int $time = null ): string {
		$time = ( $time ?? time() ) - ( $days_ago * DAY_IN_SECONDS );
		$day  = gmdate( 'Y-m-d', $time );
		return hash_hmac( 'sha256', 'wellenbrecher_token|' . $day, (string) wp_salt( 'nonce' ) );
	}
}
