<?php
/**
 * Abstract base for all AJAX handlers.
 *
 * Provides shared nonce/cap verification and JSON-array decoding helpers
 * so individual domain handlers can stay focused on their own actions.
 *
 * @package CoffeeWP_Media_Cleanup
 */

namespace CWPM\Ajax;

use CWPM\Core\Logger;

defined( 'ABSPATH' ) || exit;

abstract class BaseHandler {

	/**
	 * Verify nonce + capability for an AJAX request.
	 * On failure either function terminates the request (check_ajax_referer
	 * dies on bad nonce; wp_send_json_error dies after sending JSON), so
	 * callers may treat a return as a successful auth check.
	 *
	 * @param string $cap   Required capability. Defaults to manage_options.
	 * @param mixed  $error Error payload sent on cap failure.
	 */
	protected function verify_request( $cap = 'manage_options', $error = 'Unauthorized' ) {
		check_ajax_referer( 'cwpm_nonce', 'nonce' );
		if ( ! current_user_can( $cap ) ) {

			$action  = isset( $_REQUEST['action'] ) ? sanitize_key( (string) $_REQUEST['action'] ) : 'unknown';
			$user_id = get_current_user_id();
			$ip      = isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( (string) $_SERVER['REMOTE_ADDR'] ) ) : 'unknown';
			Logger::warning(
				sprintf(
					'AJAX cap-check failed: user_id=%d action=%s required=%s ip=%s',
					$user_id,
					$action,
					$cap,
					$ip
				)
			);
			wp_send_json_error( $error, 403 );
		}
	}

	/**
	 * Decode a JSON-encoded array of scalar strings.
	 *
	 * @param string $raw Raw JSON string (already wp_unslash'd).
	 * @return string[]|null Sanitised string array, or null on invalid input.
	 */
	protected function decode_string_array( $raw ) {
		if ( '' === $raw ) {
			return null;
		}

		$decoded = json_decode( $raw, true );

		if ( ! is_array( $decoded ) || empty( $decoded ) ) {
			return null;
		}

		$out = array();
		foreach ( $decoded as $item ) {
			if ( ! is_scalar( $item ) ) {
				return null;
			}
			$clean = sanitize_text_field( (string) $item );
			if ( '' !== $clean ) {
				$out[] = $clean;
			}
		}

		return empty( $out ) ? null : $out;
	}
}
