<?php
declare(strict_types=1);

namespace Wohno\Plugin\Ajax;

use Wohno\Plugin\Api\Client;
use Wohno\Plugin\Api\ListingQuery;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * `admin-ajax` endpoint that returns a single listing's full description, so the
 * detail modal can lazy-load long texts on open instead of embedding them in
 * every card (saves page weight with many/long listings).
 *
 * Public, nonce-protected and IP-rate-limited. Returns only the public, non-PII
 * description; the underlying fetch is cached by {@see Client::listing()}.
 */
final class ListingDetail {

	public const ACTION = 'wohno_listing';
	public const NONCE  = 'wohno_listing';

	private const RATE_LIMIT       = 60;
	private const RATE_WINDOW_SECS = MINUTE_IN_SECONDS;

	/**
	 * Constructor.
	 *
	 * @param Client $client API client.
	 */
	public function __construct( private readonly Client $client ) {
	}

	/**
	 * Register the AJAX handlers (logged-in + logged-out visitors).
	 *
	 * @return void
	 */
	public function register(): void {
		add_action( 'wp_ajax_' . self::ACTION, array( $this, 'handle' ) );
		add_action( 'wp_ajax_nopriv_' . self::ACTION, array( $this, 'handle' ) );
	}

	/**
	 * Handle a detail request and emit a JSON response.
	 *
	 * @return void
	 */
	public function handle(): void {
		if ( ! check_ajax_referer( self::NONCE, 'nonce', false ) ) {
			wp_send_json_error( array( 'message' => __( 'Sicherheitsprüfung fehlgeschlagen.', 'wohno' ) ), 403 );
		}
		if ( ! $this->allow_request() ) {
			wp_send_json_error( array( 'message' => __( 'Zu viele Anfragen.', 'wohno' ) ), 429 );
		}

		// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- sanitized via ListingQuery::sanitize_id() (accepts a canonical UUID only).
		$id = isset( $_POST['id'] ) ? ListingQuery::sanitize_id( wp_unslash( $_POST['id'] ) ) : '';
		if ( '' === $id ) {
			wp_send_json_error( array( 'message' => __( 'Ungültige Anfrage.', 'wohno' ) ), 400 );
		}

		$listing = $this->client->listing( $id );
		if ( is_wp_error( $listing ) ) {
			wp_send_json_error( array( 'message' => $listing->get_error_message() ) );
		}

		wp_send_json_success(
			array( 'description' => isset( $listing['description'] ) ? (string) $listing['description'] : '' )
		);
	}

	/**
	 * Simple per-IP rate limit (best-effort, transient-backed).
	 *
	 * @return bool True if the request is within the limit.
	 */
	private function allow_request(): bool {
		$ip  = isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : 'unknown';
		$key = 'wohno_ld_' . md5( $ip );

		$count = (int) get_transient( $key );
		if ( $count >= self::RATE_LIMIT ) {
			return false;
		}
		set_transient( $key, $count + 1, self::RATE_WINDOW_SECS );

		return true;
	}
}
