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

namespace Hafenstudios\Core\Hans;

use Hafenstudios\Core\Events\EventBus;

defined( 'ABSPATH' ) || exit;

/**
 * Hans-Framework: Karten, Meilenstein-Toasts und Wizard-Grundlage.
 *
 * Regeln aus UX-KONZEPT Abschnitt 6:
 * - maximal eine Hans-Karte pro Screen, immer schliessbar, nie blockierend;
 * - geschlossene Karten kommen nicht wieder (pro Nutzer persistiert);
 * - global abschaltbar (Einstellung "Hans anzeigen: an/aus");
 * - Wizard und Fehlermeldungen funktionieren auch ohne Hans-Bild.
 *
 * Bildzuordnung (Motiv-Nummern) siehe UX-KONZEPT Abschnitt 6 und BAUPLAN Abschnitt 17.
 */
final class Hans {

	private const USER_META_DISMISSED = 'hs_hans_dismissed';

	private const OPTION_ENABLED = 'hs_core_hans_enabled';

	/**
	 * Ob in diesem Request bereits eine Karte gerendert wurde (Ein-Karte-Regel).
	 *
	 * @var bool
	 */
	private bool $card_rendered = false;

	/**
	 * Registriert REST-Route und Admin-Assets.
	 *
	 * @return void
	 */
	public function boot(): void {
		add_action( 'rest_api_init', array( $this, 'register_rest' ) );
		if ( is_admin() ) {
			add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ) );
		}
	}

	/**
	 * Ist Hans global aktiviert?
	 *
	 * @return bool
	 */
	public function is_enabled(): bool {
		return (bool) get_option( self::OPTION_ENABLED, true );
	}

	/**
	 * Schaltet Hans global an/aus.
	 *
	 * @param bool $enabled Neuer Zustand.
	 * @return void
	 */
	public function set_enabled( bool $enabled ): void {
		update_option( self::OPTION_ENABLED, $enabled );
	}

	/**
	 * Aufloesung der Bild-URL fuer ein Motiv. Das nutzende Plugin liefert die
	 * Basis-URL per Filter; ohne Bild rendert die Karte trotzdem (UX-Regel).
	 *
	 * @param int $motif Motiv-Nummer aus dem Hans-Paket (z.B. 30).
	 * @return string Leerstring, wenn kein Bild verfuegbar.
	 */
	public function image_url( int $motif ): string {
		$filename = sprintf( 'hans-%02d.webp', $motif );
		/**
		 * Basis-URL der Hans-Motive (mit Slash am Ende). Leer = keine Bilder.
		 *
		 * @param string $base_url Basis-URL.
		 */
		$base = (string) EventBus::filter( 'hans_image_base_url', '' );
		return '' === $base ? '' : trailingslashit( $base ) . $filename;
	}

	/**
	 * Wurde eine Karte von diesem Nutzer geschlossen?
	 *
	 * @param string $card_id Stabile Karten-ID.
	 * @return bool
	 */
	public function is_dismissed( string $card_id ): bool {
		$dismissed = (array) get_user_meta( get_current_user_id(), self::USER_META_DISMISSED, true );
		return in_array( $card_id, $dismissed, true );
	}

	/**
	 * Merkt eine Karte als geschlossen (pro Nutzer).
	 *
	 * @param string $card_id Karten-ID.
	 * @return void
	 */
	public function dismiss( string $card_id ): void {
		$user_id   = get_current_user_id();
		$dismissed = (array) get_user_meta( $user_id, self::USER_META_DISMISSED, true );
		if ( ! in_array( $card_id, $dismissed, true ) ) {
			$dismissed[] = $card_id;
			update_user_meta( $user_id, self::USER_META_DISMISSED, $dismissed );
		}
	}

	/**
	 * Rendert eine Hans-Karte, sofern erlaubt (aktiv, nicht geschlossen, noch
	 * keine Karte auf diesem Screen). Gibt Leerstring zurueck, wenn unterdrueckt.
	 *
	 * @param array{
	 *   id:string, text:string, motif?:int, title?:string, tone?:string,
	 *   action?:array{label:string,url:string}, dismissible?:bool
	 * } $card Kartendefinition.
	 * @return string HTML.
	 */
	public function render_card( array $card ): string {
		$id = (string) ( $card['id'] ?? '' );
		if ( '' === $id || $this->card_rendered || ! $this->is_enabled() || $this->is_dismissed( $id ) ) {
			return '';
		}
		$this->card_rendered = true;

		$tone        = (string) ( $card['tone'] ?? 'info' );
		$title       = (string) ( $card['title'] ?? '' );
		$text        = (string) ( $card['text'] ?? '' );
		$dismissible = ! isset( $card['dismissible'] ) || (bool) $card['dismissible'];
		$image       = isset( $card['motif'] ) ? $this->image_url( (int) $card['motif'] ) : '';
		$action      = isset( $card['action'] ) && is_array( $card['action'] ) ? $card['action'] : null;

		ob_start();
		?>
		<div class="hs-hans-card hs-hans-card--<?php echo esc_attr( $tone ); ?>" data-hans-card="<?php echo esc_attr( $id ); ?>" role="note">
			<?php if ( '' !== $image ) : ?>
				<img class="hs-hans-card__img" src="<?php echo esc_url( $image ); ?>" alt="" width="72" height="72" loading="lazy" />
			<?php endif; ?>
			<div class="hs-hans-card__body">
				<?php if ( '' !== $title ) : ?>
					<p class="hs-hans-card__title"><?php echo esc_html( $title ); ?></p>
				<?php endif; ?>
				<p class="hs-hans-card__text"><?php echo esc_html( $text ); ?></p>
				<?php if ( $action && '' !== ( $action['label'] ?? '' ) ) : ?>
					<a class="button button-primary hs-hans-card__action" href="<?php echo esc_url( $action['url'] ?? '#' ); ?>">
						<?php echo esc_html( $action['label'] ); ?>
					</a>
				<?php endif; ?>
			</div>
			<?php if ( $dismissible ) : ?>
				<button type="button" class="hs-hans-card__dismiss" aria-label="<?php esc_attr_e( 'Hinweis schliessen', 'hafenstudios-core' ); ?>" data-hans-dismiss="<?php echo esc_attr( $id ); ?>">
					<span aria-hidden="true">&times;</span>
				</button>
			<?php endif; ?>
		</div>
		<?php
		return (string) ob_get_clean();
	}

	/**
	 * Gibt eine Karte direkt aus.
	 *
	 * @param array<string, mixed> $card Kartendefinition.
	 * @return void
	 */
	public function card( array $card ): void {
		echo $this->render_card( $card ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- render_card escaped bereits.
	}

	/**
	 * Registriert die REST-Route zum Persistieren geschlossener Karten.
	 *
	 * @return void
	 */
	public function register_rest(): void {
		register_rest_route(
			'hafenstudios/v1',
			'/hans/dismiss',
			array(
				'methods'             => 'POST',
				'permission_callback' => static function () {
					return is_user_logged_in();
				},
				'args'                => array(
					'card_id' => array(
						'required'          => true,
						'type'              => 'string',
						'sanitize_callback' => 'sanitize_text_field',
					),
				),
				'callback'            => function ( $request ) {
					$this->dismiss( (string) $request->get_param( 'card_id' ) );
					return new \WP_REST_Response( array( 'dismissed' => true ), 200 );
				},
			)
		);
	}

	/**
	 * Laedt Hans-CSS/JS im Admin.
	 *
	 * @return void
	 */
	public function enqueue(): void {
		$base = defined( 'HAFENSTUDIOS_CORE_DIR' ) ? HAFENSTUDIOS_CORE_DIR : __DIR__;
		$url  = $this->dir_to_url( $base );
		if ( '' === $url ) {
			return;
		}
		$ver = defined( 'HAFENSTUDIOS_CORE_VERSION' ) ? HAFENSTUDIOS_CORE_VERSION : '1.0.0';

		wp_enqueue_style( 'hafenstudios-core', $url . 'assets/css/core.css', array(), $ver );
		wp_enqueue_script( 'hafenstudios-core', $url . 'assets/js/core.js', array( 'wp-api-fetch' ), $ver, true );
		wp_localize_script(
			'hafenstudios-core',
			'hafenstudiosCore',
			array(
				'restUrl' => esc_url_raw( rest_url( 'hafenstudios/v1' ) ),
				'nonce'   => wp_create_nonce( 'wp_rest' ),
			)
		);
	}

	/**
	 * Wandelt ein Core-Verzeichnis in eine URL (der Core liegt im vendor-Ordner
	 * des ladenden Plugins).
	 *
	 * @param string $dir Verzeichnis.
	 * @return string URL mit Slash am Ende, oder Leerstring.
	 */
	private function dir_to_url( string $dir ): string {
		$dir           = wp_normalize_path( $dir );
		$content_dir   = wp_normalize_path( WP_CONTENT_DIR );
		if ( 0 === strpos( $dir, $content_dir ) ) {
			return trailingslashit( content_url( substr( $dir, strlen( $content_dir ) ) ) );
		}
		return '';
	}
}
