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

namespace Hafenstudios\Leadlotse\Doi;

use Hafenstudios\Leadlotse\Db\EntryRepository;

defined( 'ABSPATH' ) || exit;

/**
 * Double-Opt-in-Flow fuer den Zweck Newsletter (D9).
 *
 * Token: base64url( entry_id . "|" . expiry ) . "." . HMAC. Zusaetzlich wird der
 * Token-Hash am Entry gespeichert, damit ein Token nur einmal verwendbar ist
 * (Bestaetigung leert den Hash). Bestaetigung laeuft ueber den Public-Endpoint
 * `leadlotse/v1/confirm`.
 */
final class DoiService {

	private EntryRepository $entries;

	/**
	 * @param EntryRepository $entries Entry-Repository.
	 */
	public function __construct( EntryRepository $entries ) {
		$this->entries = $entries;
	}

	/**
	 * Erzeugt einen signierten Bestaetigungs-Token.
	 *
	 * @param int $entry_id     Entry-ID.
	 * @param int $expiry_hours Gueltigkeit in Stunden.
	 * @return string
	 */
	public function create_token( int $entry_id, int $expiry_hours = 48 ): string {
		$expires = time() + ( max( 1, $expiry_hours ) * HOUR_IN_SECONDS );
		$payload = $entry_id . '|' . $expires;
		$sig     = $this->sign( $payload );
		return rtrim( strtr( base64_encode( $payload ), '+/', '-_' ), '=' ) . '.' . $sig;
	}

	/**
	 * Hash eines Tokens (Speicherung am Entry, Einmal-Verwendung).
	 *
	 * @param string $token Token.
	 * @return string
	 */
	public function token_hash( string $token ): string {
		return hash( 'sha256', $token );
	}

	/**
	 * Prueft einen Token und liefert die Entry-ID.
	 *
	 * @param string $token Token aus dem Bestaetigungslink.
	 * @return int|\WP_Error Entry-ID oder Fehler.
	 */
	public function verify_token( string $token ) {
		$invalid = new \WP_Error(
			'leadlotse_invalid_token',
			__( 'Der Bestaetigungslink ist ungueltig oder abgelaufen. Bitte melden Sie sich erneut an, dann erhalten Sie einen neuen Link.', 'leadlotse' ),
			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;
		}

		$segments = explode( '|', $payload );
		if ( 2 !== count( $segments ) ) {
			return $invalid;
		}

		list( $entry_id, $expires ) = array_map( 'intval', $segments );
		if ( $entry_id <= 0 || $expires < time() ) {
			return $invalid;
		}

		return $entry_id;
	}

	/**
	 * Fuehrt die Bestaetigung durch: Token pruefen, Einmal-Verwendung sichern,
	 * Entry als bestaetigt markieren.
	 *
	 * @param string $token Token.
	 * @return array<string, mixed>|\WP_Error Der bestaetigte Entry (mit Flag
	 *         `_newly_confirmed`) oder Fehler. Das Flag ist true, wenn die
	 *         Bestaetigung JETZT erfolgt ist, sonst false (idempotenter
	 *         Wiederholungsklick) - so feuert der Aufrufer das Event nur einmal.
	 */
	public function confirm( string $token ) {
		$entry_id = $this->verify_token( $token );
		if ( is_wp_error( $entry_id ) ) {
			return $entry_id;
		}

		$entry = $this->entries->get( $entry_id );
		if ( null === $entry ) {
			return new \WP_Error(
				'leadlotse_entry_not_found',
				__( 'Zu diesem Bestaetigungslink wurde keine Anmeldung gefunden. Bitte melden Sie sich erneut an.', 'leadlotse' ),
				array( 'status' => 404 )
			);
		}

		if ( ! empty( $entry['doi_confirmed_at'] ) ) {
			// Bereits bestaetigt: idempotent, kein Fehler fuer den Nutzer.
			$entry['_newly_confirmed'] = false;
			return $entry;
		}

		if ( empty( $entry['doi_token_hash'] ) || ! hash_equals( $entry['doi_token_hash'], $this->token_hash( $token ) ) ) {
			return new \WP_Error(
				'leadlotse_token_used',
				__( 'Dieser Bestaetigungslink wurde bereits verwendet oder ersetzt. Bitte melden Sie sich erneut an, falls Ihre Anmeldung nicht bestaetigt ist.', 'leadlotse' ),
				array( 'status' => 400 )
			);
		}

		$this->entries->mark_doi_confirmed( $entry_id );

		$confirmed = $this->entries->get( $entry_id );
		if ( is_array( $confirmed ) ) {
			$confirmed['_newly_confirmed'] = true;
		}
		return $confirmed;
	}

	/**
	 * HMAC-Signatur.
	 *
	 * @param string $payload Nutzdaten.
	 * @return string
	 */
	private function sign( string $payload ): string {
		return hash_hmac( 'sha256', 'leadlotse_doi|' . $payload, (string) wp_salt( 'auth' ) );
	}
}
