<?php
/**
 * File Path: includes/admin/class-mbs-classification-ajax.php
 *
 * Classification AJAX Handler
 *
 * Registers and handles the wp_ajax_mbs_classify_search and
 * wp_ajax_mbs_search_bowker actions used by the Classification and
 * Categorization metaboxes. Delegates all DB work to MBS_Classification_DAO.
 * No business logic here.
 *
 * @package MyBookShowroom
 * @since   1.1.0
 */

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

class MBS_Classification_Ajax {

	/**
	 * @var MBS_Classification_DAO
	 */
	private MBS_Classification_DAO $dao;

	/**
	 * Constructor -- registers the AJAX hooks.
	 */
	public function __construct() {
		$this->dao = new MBS_Classification_DAO();
		add_action( 'wp_ajax_mbs_classify_search',        array( $this, 'handle_search' ) );
		add_action( 'wp_ajax_mbs_classify_thema_suggest', array( $this, 'handle_thema_suggest' ) );
		add_action( 'wp_ajax_mbs_search_bowker',          array( $this, 'handle_bowker_search' ) );
	}

	/**
	 * Handle the mbs_classify_search AJAX request.
	 *
	 * Validates nonce + capability, sanitizes input, delegates to DAO,
	 * returns JSON. Never exposes internal error detail.
	 *
	 * @return void  Terminates via wp_send_json_success() or wp_send_json_error().
	 */
	public function handle_search(): void {
		// 1. Nonce verification.
		if ( ! check_ajax_referer( 'mbs_classification_nonce', 'nonce', false ) ) {
			wp_send_json_error( array( 'message' => 'Invalid request.' ), 403 );
		}

		// 2. Capability check.
		if ( ! current_user_can( 'edit_posts' ) ) {
			wp_send_json_error( array( 'message' => 'Insufficient permissions.' ), 403 );
		}

		// 3. Sanitize and validate query string.
		$query = sanitize_text_field( wp_unslash( $_POST['query'] ?? '' ) );
		if ( mb_strlen( $query ) < 2 ) {
			wp_send_json_error( array( 'message' => 'Query too short.' ), 400 );
		}

		// 4. Validate scheme parameter.
		$scheme = sanitize_key( wp_unslash( $_POST['scheme'] ?? '' ) );
		if ( ! in_array( $scheme, array( 'bisac', 'thema' ), true ) ) {
			wp_send_json_error( array( 'message' => 'Invalid scheme.' ), 400 );
		}

		// 5. Delegate to DAO.
		if ( 'bisac' === $scheme ) {
			$results = $this->dao->get_bisac_by_prefix( $query );
		} else {
			$results = $this->dao->get_thema_by_prefix( $query );
		}

		wp_send_json_success( $results );
	}

	/**
	 * Handle the mbs_classify_thema_suggest AJAX request.
	 *
	 * Accepts a list of BISAC codes and returns the Thema codes mapped to any
	 * of them via the mbs_bisac_thema_map table. Used by Layer 3 Thema suggestions.
	 *
	 * @return void
	 */
	public function handle_thema_suggest(): void {
		if ( ! check_ajax_referer( 'mbs_classification_nonce', 'nonce', false ) ) {
			wp_send_json_error( array( 'message' => 'Invalid request.' ), 403 );
		}
		if ( ! current_user_can( 'edit_posts' ) ) {
			wp_send_json_error( array( 'message' => 'Insufficient permissions.' ), 403 );
		}

		$raw_codes = isset( $_POST['bisac_codes'] ) && is_array( $_POST['bisac_codes'] )
			? $_POST['bisac_codes'] // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotValidated -- input sanitized via sanitize_* helpers or validated before use; see inline sanitization
			: array();

		$codes = array_slice(
			array_filter( array_map( 'sanitize_text_field', array_map( 'wp_unslash', $raw_codes ) ) ),
			0,
			20
		);

		if ( empty( $codes ) ) {
			wp_send_json_success( array() );
		}

		wp_send_json_success( $this->dao->get_thema_for_bisac_batch( $codes ) );
	}

	/**
	 * Handle the mbs_search_bowker AJAX request.
	 *
	 * Returns Bowker subject codes matching the given query string. Used by
	 * the Bowker section in the Categorization metabox typeahead.
	 *
	 * @return void  Terminates via wp_send_json_success() or wp_send_json_error().
	 * @since 1.5.0
	 */
	public function handle_bowker_search(): void {
		// 1. Nonce verification.
		if ( ! check_ajax_referer( 'mbs_classification_nonce', 'nonce', false ) ) {
			wp_send_json_error( array( 'message' => 'Invalid request.' ), 403 );
		}

		// 2. Capability check.
		if ( ! current_user_can( 'edit_posts' ) ) {
			wp_send_json_error( array( 'message' => 'Insufficient permissions.' ), 403 );
		}

		// 3. Sanitize and validate query string.
		$query = sanitize_text_field( wp_unslash( $_POST['query'] ?? '' ) );
		if ( mb_strlen( $query ) < 2 ) {
			wp_send_json_error( array( 'message' => 'Query too short.' ), 400 );
		}

		wp_send_json_success( $this->dao->get_bowker_by_prefix( $query, 20 ) );
	}
}
