<?php
/**
 * File Path: includes/admin/tabs/imp-exp/class-mbs-ajax-import-export-guard.php
 *
 * Import / Export AJAX Request Guard Trait -- My Book Showroom
 *
 * DUP-007: Shared nonce + capability guard for all import/export AJAX handlers.
 * Used by MBS_Tab_Child_Import_ISBN, MBS_Tab_Child_Import_WXR, MBS_Tab_Child_Import_Competitors,
 * MBS_Tab_Child_Import_CSV, MBS_Tab_Child_Export,
 * and the split classes introduced by REF-004.
 *
 * @package MyBookShowroom
 * @since   1.1.0
 */

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

trait MBS_Ajax_Import_Export_Guard {

	/**
	 * Verify the AJAX request nonce and caller capability.
	 *
	 * Terminates with a 403 JSON error on failure. Must be called as the very first
	 * line of every import/export AJAX handler that uses the 'mbs_import_export' nonce.
	 * Handlers with a different nonce (e.g. handle_ajax_mbs_download_csv_template)
	 * must perform their own nonce check and should not call this method.
	 *
	 * @return void
	 */
	private function verify_import_export_request(): void {
		check_ajax_referer( 'mbs_import_export', 'nonce' );
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array( 'message' => 'Permission denied.' ), 403 );
		}
	}

	/**
	 * Determine whether an uploaded file is an acceptable XML (WXR) upload.
	 *
	 * The primary check uses WordPress's extension/MIME allowlist. When that
	 * rejects the file, fall back to the detected media type and accept either
	 * standard XML type. PLG-NEW-014: some environments (e.g. XAMPP/Windows)
	 * report XML as application/xml, which the text/xml allowlist rejects.
	 *
	 * The detected MIME passes through the 'mbs_xml_upload_detected_mime'
	 * filter so tests and environment shims can override finfo's result
	 * deterministically across runner operating systems (PLG-NEW-014 was
	 * environment-specific and never reproduced on a Linux CI runner).
	 *
	 * @since  1.3.7
	 * @param  string $tmp  Absolute path to the uploaded temp file.
	 * @param  string $name Original client file name (used for extension mapping).
	 * @return bool         True when the file is an acceptable XML upload.
	 */
	public static function is_accepted_xml_upload( string $tmp, string $name ): bool {
		$check        = wp_check_filetype_and_ext( $tmp, $name, array( 'xml' => 'text/xml' ) );
		$is_valid_xml = ( ! empty( $check['ext'] ) && 'xml' === $check['ext'] );

		if ( ! $is_valid_xml ) {
			$real_mime = '';
			if ( function_exists( 'finfo_open' ) ) {
				$finfo     = finfo_open( FILEINFO_MIME_TYPE );
				$real_mime = $finfo ? (string) finfo_file( $finfo, $tmp ) : '';
				if ( $finfo ) {
					finfo_close( $finfo );
				}
			}

			/**
			 * Filter the detected MIME type of an XML upload before validation.
			 *
			 * @since 1.3.7
			 * @param string $real_mime Detected media type (may be empty).
			 * @param string $tmp       Absolute path to the uploaded temp file.
			 * @param string $name      Original client file name.
			 */
			$real_mime    = (string) apply_filters( 'mbs_xml_upload_detected_mime', $real_mime, $tmp, $name ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- mbs_ is the registered plugin prefix; checker uses slug-derived prefix incorrectly.
			$is_valid_xml = in_array( $real_mime, array( 'text/xml', 'application/xml' ), true );
		}

		return $is_valid_xml;
	}
}
