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

namespace Hafenstudios\Leadlotse\Events;

use Hafenstudios\Leadlotse\Db\EntryRepository;
use Hafenstudios\Leadlotse\Forms\FormPostType;

defined( 'ABSPATH' ) || exit;

/**
 * Oeffentliche Ticket- und Check-in-Seiten (L-R1).
 *
 * Muster wie das Kurato-Preference-Center: eigenstaendige, escapte HTML-Seite
 * ueber template_redirect (kein Theme, kein Login), erreichbar allein ueber den
 * zweckgebundenen HMAC-Token (`?ll_ticket=` bzw. `?ll_checkin=`), noindex.
 *
 * - Ticket-Seite: druckfaehiges Ticket mit Veranstaltungsdaten, Name,
 *   Personenzahl und QR-Code. Der QR-Inhalt ist die Check-in-URL mit eigenem,
 *   zweckgebundenem Token.
 * - Check-in-Seite: zeigt jedem die Gueltigkeit (gueltig / ungueltig / bereits
 *   eingecheckt). Eingeloggte Nutzerinnen mit hs_view_entries sehen zusaetzlich
 *   den "Einchecken"-Knopf; der POST laeuft nonce- und cap-gesichert ueber
 *   admin-post.php und setzt den Zeitstempel.
 */
final class TicketPage {

	public const QUERY_TICKET  = 'll_ticket';
	public const QUERY_CHECKIN = 'll_checkin';
	public const CHECKIN_ACTION = 'leadlotse_checkin';

	/**
	 * Capability fuer das Einchecken (deckt die Inbox-Rolle hs_lead_agent ab).
	 */
	public const CHECKIN_CAP = 'hs_view_entries';

	private EntryRepository $entries;

	private TicketToken $tokens;

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

	/**
	 * Verdrahtet Seiten-Handler und Check-in-Aktion.
	 *
	 * @return void
	 */
	public function register(): void {
		add_action( 'template_redirect', array( $this, 'maybe_render' ) );
		add_action( 'admin_post_' . self::CHECKIN_ACTION, array( $this, 'handle_checkin' ) );
	}

	/**
	 * URL der Ticket-Seite.
	 *
	 * @param string $token Ticket-Token (PURPOSE_TICKET).
	 * @return string
	 */
	public static function ticket_url( string $token ): string {
		return add_query_arg( array( self::QUERY_TICKET => $token ), home_url( '/' ) );
	}

	/**
	 * Check-in-URL (Inhalt des QR-Codes).
	 *
	 * @param string $token Check-in-Token (PURPOSE_CHECKIN).
	 * @return string
	 */
	public static function checkin_url( string $token ): string {
		return add_query_arg( array( self::QUERY_CHECKIN => $token ), home_url( '/' ) );
	}

	/**
	 * Veranstaltungsdatum lesbar formatieren (d.m.Y H:i, Flotten-Linie K-M1).
	 *
	 * @param string $raw Gespeicherter Wert (datetime-local oder Freitext).
	 * @return string
	 */
	public static function format_event_date( string $raw ): string {
		if ( '' === $raw ) {
			return '';
		}
		$timestamp = strtotime( $raw );
		if ( false === $timestamp ) {
			return $raw; // Freitext unveraendert anzeigen.
		}
		return gmdate( 'd.m.Y', $timestamp ) . ', ' . gmdate( 'H:i', $timestamp ) . ' ' . __( 'Uhr', 'leadlotse' );
	}

	/**
	 * Rendert Ticket- oder Check-in-Seite, wenn die Query-Parameter passen.
	 *
	 * @return void
	 */
	public function maybe_render(): void {
		// phpcs:disable WordPress.Security.NonceVerification.Recommended -- Oeffentliche, rein lesende Token-Seiten; die Absicherung ist der zweckgebundene HMAC-Token.
		if ( isset( $_GET[ self::QUERY_TICKET ] ) ) {
			$this->render_ticket( sanitize_text_field( wp_unslash( $_GET[ self::QUERY_TICKET ] ) ) );
		}
		if ( isset( $_GET[ self::QUERY_CHECKIN ] ) ) {
			$this->render_checkin( sanitize_text_field( wp_unslash( $_GET[ self::QUERY_CHECKIN ] ) ) );
		}
		// phpcs:enable
	}

	/**
	 * Check-in-POST (admin-post.php): Cap + Nonce + Token, dann Zeitstempel.
	 *
	 * @return void
	 */
	public function handle_checkin(): void {
		if ( ! current_user_can( self::CHECKIN_CAP ) ) {
			wp_die( esc_html__( 'Fuer das Einchecken fehlt Ihnen die Berechtigung.', 'leadlotse' ) );
		}
		check_admin_referer( self::CHECKIN_ACTION );

		$token    = isset( $_POST['token'] ) ? sanitize_text_field( wp_unslash( $_POST['token'] ) ) : '';
		$entry_id = $this->tokens->verify( $token, TicketToken::PURPOSE_CHECKIN );
		if ( $entry_id > 0 ) {
			$entry = $this->entries->get( $entry_id );
			if ( null !== $entry && empty( $entry['checked_in_at'] ) ) {
				$this->entries->mark_checked_in( $entry_id );
			}
		}

		wp_safe_redirect( self::checkin_url( $token ) );
		exit;
	}

	/**
	 * Druckfaehige Ticket-Seite.
	 *
	 * @param string $token Ticket-Token.
	 * @return void
	 */
	private function render_ticket( string $token ): void {
		$context = $this->resolve( $token, TicketToken::PURPOSE_TICKET );
		if ( null === $context ) {
			$this->page( __( 'Ticket', 'leadlotse' ), '<p>' . esc_html__( 'Dieses Ticket ist ungueltig oder wurde entfernt. Bitte pruefen Sie den Link aus Ihrer E-Mail.', 'leadlotse' ) . '</p>' );
			return;
		}

		list( $entry, $definition ) = $context;

		$event    = (array) ( $definition['settings']['event'] ?? array() );
		$values   = (array) ( $entry['field_values'] ?? array() );
		$name     = (string) ( $values['first_name'] ?? ( $values['name'] ?? '' ) );
		$persons  = EventCapacity::persons( $values, $definition );
		$title    = (string) ( $event['title'] ?? '' );
		$date     = self::format_event_date( (string) ( $event['date'] ?? '' ) );
		$location = (string) ( $event['location'] ?? '' );

		$qr = QrCode::svg( self::checkin_url( $this->tokens->create( (int) $entry['id'], TicketToken::PURPOSE_CHECKIN ) ), 4 );

		$body  = '<div class="ticket">';
		$body .= '<p class="brand">' . esc_html( (string) get_bloginfo( 'name' ) ) . '</p>';
		$body .= '<h1>' . esc_html( '' !== $title ? $title : __( 'Ihr Ticket', 'leadlotse' ) ) . '</h1>';
		$body .= '<dl>';
		if ( '' !== $date ) {
			$body .= '<dt>' . esc_html__( 'Datum', 'leadlotse' ) . '</dt><dd>' . esc_html( $date ) . '</dd>';
		}
		if ( '' !== $location ) {
			$body .= '<dt>' . esc_html__( 'Ort', 'leadlotse' ) . '</dt><dd>' . esc_html( $location ) . '</dd>';
		}
		if ( '' !== $name ) {
			$body .= '<dt>' . esc_html__( 'Name', 'leadlotse' ) . '</dt><dd>' . esc_html( $name ) . '</dd>';
		}
		$body .= '<dt>' . esc_html__( 'Personen', 'leadlotse' ) . '</dt><dd>' . (int) $persons . '</dd>';
		$body .= '</dl>';
		$body .= '<div class="qr">' . $qr . '</div>'; // QrCode::svg liefert kontrolliertes Markup.
		$body .= '<p class="hint">' . esc_html__( 'Zeigen Sie diesen QR-Code am Einlass vor.', 'leadlotse' ) . '</p>';

		$event_url = (string) ( $event['url'] ?? '' );
		if ( '' !== $event_url ) {
			$body .= '<p class="hint"><a href="' . esc_url( $event_url ) . '">' . esc_html__( 'Weitere Informationen zur Veranstaltung', 'leadlotse' ) . '</a></p>';
		}
		$body .= '<p class="noprint"><button type="button" onclick="window.print()">' . esc_html__( 'Ticket drucken', 'leadlotse' ) . '</button></p>';
		$body .= '</div>';

		$this->page( '' !== $title ? $title : __( 'Ticket', 'leadlotse' ), $body );
	}

	/**
	 * Check-in-Seite: Gueltigkeits-Anzeige, mit Berechtigung ein Einchecken-Knopf.
	 *
	 * @param string $token Check-in-Token.
	 * @return void
	 */
	private function render_checkin( string $token ): void {
		$context = $this->resolve( $token, TicketToken::PURPOSE_CHECKIN );
		if ( null === $context ) {
			$this->page(
				__( 'Check-in', 'leadlotse' ),
				'<p class="status status--invalid">' . esc_html__( 'Ticket ungueltig.', 'leadlotse' ) . '</p>'
			);
			return;
		}

		list( $entry, $definition ) = $context;

		$doi_enabled = 'newsletter' === (string) ( $definition['purpose'] ?? '' )
			|| ! empty( $definition['settings']['doi']['enabled'] );
		$status = ParticipantList::status( $entry, $doi_enabled );

		$event   = (array) ( $definition['settings']['event'] ?? array() );
		$values  = (array) ( $entry['field_values'] ?? array() );
		$name    = (string) ( $values['first_name'] ?? ( $values['name'] ?? '' ) );
		$persons = EventCapacity::persons( $values, $definition );

		$body = '<h1>' . esc_html( (string) ( $event['title'] ?? __( 'Check-in', 'leadlotse' ) ) ) . '</h1>';

		if ( ParticipantList::STATUS_CHECKED_IN === $status ) {
			$timestamp = strtotime( (string) $entry['checked_in_at'] );
			$body     .= '<p class="status status--used">' . esc_html(
				sprintf(
					/* translators: %s: Zeitpunkt */
					__( 'Bereits eingecheckt (%s).', 'leadlotse' ),
					false !== $timestamp ? gmdate( 'd.m.Y H:i', $timestamp ) : (string) $entry['checked_in_at']
				)
			) . '</p>';
		} elseif ( ParticipantList::STATUS_PENDING === $status ) {
			$body .= '<p class="status status--invalid">' . esc_html__( 'Ticket ungueltig: Die Anmeldung wurde noch nicht bestaetigt.', 'leadlotse' ) . '</p>';
		} else {
			$body .= '<p class="status status--valid">' . esc_html__( 'Ticket gueltig.', 'leadlotse' ) . '</p>';
		}

		$body .= '<dl>';
		if ( '' !== $name ) {
			$body .= '<dt>' . esc_html__( 'Name', 'leadlotse' ) . '</dt><dd>' . esc_html( $name ) . '</dd>';
		}
		$body .= '<dt>' . esc_html__( 'Personen', 'leadlotse' ) . '</dt><dd>' . (int) $persons . '</dd>';
		$body .= '</dl>';

		if ( ParticipantList::STATUS_CONFIRMED === $status && current_user_can( self::CHECKIN_CAP ) ) {
			$body .= '<form method="post" action="' . esc_url( admin_url( 'admin-post.php' ) ) . '">';
			$body .= '<input type="hidden" name="action" value="' . esc_attr( self::CHECKIN_ACTION ) . '" />';
			$body .= '<input type="hidden" name="token" value="' . esc_attr( $token ) . '" />';
			$body .= wp_nonce_field( self::CHECKIN_ACTION, '_wpnonce', true, false );
			$body .= '<p><button type="submit" class="checkin">' . esc_html__( 'Einchecken', 'leadlotse' ) . '</button></p>';
			$body .= '</form>';
		}

		$this->page( __( 'Check-in', 'leadlotse' ), $body );
	}

	/**
	 * Token pruefen und Entry + Definition laden.
	 *
	 * @param string $token   Token.
	 * @param string $purpose Erwarteter Zweck.
	 * @return array{0:array<string,mixed>,1:array<string,mixed>}|null
	 */
	private function resolve( string $token, string $purpose ): ?array {
		$entry_id = $this->tokens->verify( $token, $purpose );
		if ( $entry_id <= 0 ) {
			return null;
		}
		$entry = $this->entries->get( $entry_id );
		if ( null === $entry ) {
			return null;
		}
		$definition = FormPostType::get_definition( (int) $entry['form_id'] );
		if ( null === $definition || ! EventCapacity::is_event( $definition ) ) {
			return null;
		}
		return array( $entry, $definition );
	}

	/**
	 * Gibt die eigenstaendige HTML-Seite aus und beendet die Anfrage.
	 *
	 * @param string $title Seitentitel.
	 * @param string $body  Bereits escapter Seiteninhalt.
	 * @return void
	 */
	private function page( string $title, string $body ): void {
		nocache_headers();
		status_header( 200 );
		header( 'Content-Type: text/html; charset=utf-8' );
		header( 'X-Robots-Tag: noindex, nofollow' );

		// phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped -- $body ist oben feldweise escaped aufgebaut.
		echo '<!DOCTYPE html><html lang="de"><head><meta charset="utf-8" />'
			. '<meta name="viewport" content="width=device-width, initial-scale=1" />'
			. '<meta name="robots" content="noindex, nofollow" />'
			. '<title>' . esc_html( $title ) . '</title>'
			. '<style>'
			. 'body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;background:#f0f3f7;color:#1d2327;margin:0;padding:24px;}'
			. '.ticket,main{max-width:420px;margin:0 auto;background:#fff;border:1px solid #c3cbd4;border-radius:12px;padding:32px;text-align:center;}'
			. 'h1{font-size:22px;margin:8px 0 16px;}'
			. '.brand{color:#50575e;font-size:13px;letter-spacing:.06em;text-transform:uppercase;margin:0;}'
			. 'dl{display:grid;grid-template-columns:auto auto;gap:4px 16px;justify-content:center;text-align:left;margin:16px 0;}'
			. 'dt{color:#50575e;}dd{margin:0;font-weight:600;}'
			. '.qr{margin:16px auto;}.qr svg{width:200px;height:200px;}'
			. '.hint{color:#50575e;font-size:13px;}'
			. '.status{font-size:18px;font-weight:600;padding:12px;border-radius:8px;}'
			. '.status--valid{background:#e6f4e6;color:#1e6b2e;}'
			. '.status--invalid{background:#fbeaea;color:#8a1f1f;}'
			. '.status--used{background:#fdf4e2;color:#7a5200;}'
			. 'button{font-size:15px;padding:10px 20px;border-radius:8px;border:1px solid #1d2327;background:#1d2327;color:#fff;cursor:pointer;}'
			. '@media print{body{background:#fff;padding:0;}.noprint{display:none;}.ticket{border:none;}}'
			. '</style></head><body><main>' . $body . '</main></body></html>';
		// phpcs:enable

		$this->terminate();
	}

	/**
	 * Beendet die Anfrage (Seam fuer Tests).
	 *
	 * @return void
	 */
	protected function terminate(): void {
		exit;
	}
}
