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

namespace Hafenstudios\Leadlotse\Events;

defined( 'ABSPATH' ) || exit;

/**
 * Signierter Token fuer Ticket- und Check-in-Links (L-R1).
 *
 * Muster wie DoiService/Kurato-LinkToken: `base64url( payload ) . "." . HMAC`
 * mit wp_salt-signiertem HMAC. Nutzlast ist die Entry-ID.
 *
 * Zweckbindung wie Kurato K4d-2: der HMAC-Praefix haengt vom Zweck ab. Ein
 * Ticket-Token (Anzeige der Ticket-Seite) und ein Check-in-Token (im QR-Code)
 * tragen dieselbe Nutzlast, aber unterschiedliche Signaturen; keiner gilt am
 * jeweils anderen Ort. Bewusst KEIN Ablauf: der Ticket-Link aus der Mail muss
 * bis zur Veranstaltung gueltig bleiben. Der Token traegt keine Rechte ausser
 * dem Anzeigen des einen Tickets bzw. seiner Gueltigkeitspruefung; das
 * eigentliche Einchecken verlangt zusaetzlich eine Admin-Capability.
 */
final class TicketToken {

	/**
	 * Zweck: Ticket-Seite anzeigen (Link in der Ticket-Mail).
	 */
	public const PURPOSE_TICKET = 'ticket';

	/**
	 * Zweck: Check-in-Ansicht (Inhalt des QR-Codes).
	 */
	public const PURPOSE_CHECKIN = 'checkin';

	/**
	 * HMAC-Praefix je Zweck (Domain-Trennung).
	 *
	 * @var array<string, string>
	 */
	private const PREFIXES = array(
		self::PURPOSE_TICKET  => 'leadlotse_ticket',
		self::PURPOSE_CHECKIN => 'leadlotse_checkin',
	);

	/**
	 * Erzeugt einen signierten Token fuer einen Entry.
	 *
	 * @param int    $entry_id Entry-ID.
	 * @param string $purpose  Zweck (PURPOSE_TICKET | PURPOSE_CHECKIN).
	 * @return string
	 */
	public function create( int $entry_id, string $purpose = self::PURPOSE_TICKET ): string {
		$payload = (string) max( 0, $entry_id );
		return rtrim( strtr( base64_encode( $payload ), '+/', '-_' ), '=' ) . '.' . $this->sign( $payload, $purpose );
	}

	/**
	 * Prueft einen Token gegen den erwarteten Zweck und liefert die Entry-ID.
	 *
	 * @param string $token   Token aus der URL.
	 * @param string $purpose Erwarteter Zweck.
	 * @return int Entry-ID, oder 0 bei ungueltigem Token.
	 */
	public function verify( string $token, string $purpose = self::PURPOSE_TICKET ): int {
		$parts = explode( '.', $token, 2 );
		if ( 2 !== count( $parts ) ) {
			return 0;
		}

		list( $encoded, $sig ) = $parts;
		$payload               = base64_decode( strtr( $encoded, '-_', '+/' ), true );
		if ( false === $payload || ! hash_equals( $this->sign( $payload, $purpose ), $sig ) ) {
			return 0;
		}

		$entry_id = (int) $payload;
		return $entry_id > 0 ? $entry_id : 0;
	}

	/**
	 * HMAC-Signatur ueber die Nutzlast, gebunden an den Zweck.
	 *
	 * @param string $payload Nutzdaten.
	 * @param string $purpose Zweck (unbekannt faellt auf Ticket zurueck).
	 * @return string
	 */
	private function sign( string $payload, string $purpose ): string {
		$prefix = self::PREFIXES[ $purpose ] ?? self::PREFIXES[ self::PURPOSE_TICKET ];
		return hash_hmac( 'sha256', $prefix . '|' . $payload, (string) wp_salt( 'auth' ) );
	}
}
