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

namespace Hafenstudios\Kurato\Booking;

defined( 'ABSPATH' ) || exit;

/**
 * Signierter Token fuer Storno-/Umbuchungs-Links ohne Konto (K5a, K5-KONZEPT
 * Abschnitt 5).
 *
 * Muster wie {@see \Hafenstudios\Kurato\Mail\LinkToken} und
 * {@see \Hafenstudios\Kurato\Mail\TrackingToken} (Leadlotse-DoiService-Linie):
 * `base64url( payload ) . "." . HMAC`, HMAC ueber `wp_salt('auth')`. Nutzlast ist
 * allein die Buchungs-ID.
 *
 * **Zweckbindung** (wie K4d-2): eigener HMAC-Praefix `kurato_booking`. Ein
 * Abmelde-/Preference-/Tracking-Token traegt einen anderen Praefix und wird hier
 * NICHT als Booking-Token akzeptiert (und umgekehrt).
 *
 * Bewusst KEIN Ablauf im Token: die Wirkungslosigkeit nach Storno oder nach dem
 * Termin prueft der spaetere Storno-Controller (K5c) an der Buchung selbst, nicht
 * der Token. Der Token weist nur die Buchungs-ID nach; er gewaehrt keine weiteren
 * Rechte.
 */
final class BookingToken {

	/**
	 * HMAC-Praefix (Domain-Trennung gegen alle uebrigen Kurato-Token).
	 */
	private const PREFIX = 'kurato_booking';

	/**
	 * Erzeugt einen signierten Token fuer eine Buchung.
	 *
	 * @param int $booking_id Buchungs-ID.
	 * @return string
	 */
	public function create( int $booking_id ): string {
		$payload = (string) max( 0, $booking_id );
		return rtrim( strtr( base64_encode( $payload ), '+/', '-_' ), '=' ) . '.' . $this->sign( $payload );
	}

	/**
	 * Prueft einen Token und liefert die Buchungs-ID.
	 *
	 * @param string $token Token aus dem Storno-/Umbuchungs-Link.
	 * @return int|\WP_Error Buchungs-ID oder Fehler.
	 */
	public function verify( string $token ) {
		$invalid = new \WP_Error(
			'kurato_invalid_booking_token',
			__( 'Der Link ist ungueltig. Bitte nutzen Sie den Link aus Ihrer Terminbestaetigung.', 'kurato' ),
			array( 'status' => 400 )
		);

		$parts = explode( '.', $token, 2 );
		if ( 2 !== count( $parts ) ) {
			return $invalid;
		}

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

		if ( '' === $payload || ! ctype_digit( $payload ) ) {
			return $invalid;
		}
		$booking_id = (int) $payload;
		if ( $booking_id <= 0 ) {
			return $invalid;
		}

		return $booking_id;
	}

	/**
	 * HMAC-Signatur ueber die Nutzlast, gebunden an den Booking-Zweck.
	 *
	 * @param string $payload Nutzdaten.
	 * @return string
	 */
	private function sign( string $payload ): string {
		return hash_hmac( 'sha256', self::PREFIX . '|' . $payload, (string) wp_salt( 'auth' ) );
	}
}
