<?php
/**
 * File Path: includes/admin/class-mbs-admin-bulk-edit-save.php
 *
 * MBS Admin Bulk Edit — Save Handler
 *
 * Processes the "Apply Changes" form submission from MBS_Admin_Bulk_Edit.
 * Extracted from MBS_Admin_Bulk_Edit so the primary class retains only
 * registration and rendering (REF-013, Session 137).
 *
 * Responsibility:
 *   - Validate nonce and capability.
 *   - Read and consume the one-time transient.
 *   - Sanitize submitted field values via sanitize_fields().
 *   - Apply each change to each post via apply_to_post().
 *   - Redirect back to the books list with an applied-count indicator.
 *
 * @package MyBookShowroom
 * @since   1.1.0
 */

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

// phpcs:disable WordPress.Security.NonceVerification -- nonce verified in parent class verify_save() / check_admin_referer() before $_POST access; PHPCS cannot trace cross-method nonce verification
class MBS_Admin_Bulk_Edit_Save {

	/** Transient prefix — must match MBS_Admin_Bulk_Edit::TRANSIENT_PREFIX. */
	private const TRANSIENT_PREFIX = 'mbs_bulk_edit_';

	public function __construct() {
		add_action( 'admin_post_mbs_bulk_edit_apply', array( $this, 'handle_apply' ) );
	}

	// =========================================================================
	// Step 5 — Apply changes
	// =========================================================================

	/**
	 * Process the bulk edit apply form submission (admin_post_mbs_bulk_edit_apply).
	 *
	 * Validates nonce, reads transient, sanitizes fields via sanitize_fields(),
	 * applies changes via apply_to_post(), then redirects with an applied count.
	 *
	 * @return void
	 */
	public function handle_apply(): void {
		// SA-L5: Verify nonce BEFORE capability check — prevents user enumeration
		// and rejects forged requests before any auth state is evaluated.
		check_admin_referer( 'mbs_bulk_edit_apply', 'mbs_bulk_nonce' );

		if ( ! current_user_can( 'edit_posts' ) ) {
			wp_die( esc_html__( 'You do not have permission to edit books.', 'my-book-showroom' ), 403 );
		}

		$token = sanitize_key( wp_unslash( $_POST['mbs_bulk_token'] ?? '' ) );
		if ( empty( $token ) ) {
			wp_die( esc_html__( 'Invalid bulk edit request.', 'my-book-showroom' ) );
		}

		$post_ids = get_transient( self::TRANSIENT_PREFIX . $token );
		if ( false === $post_ids || ! is_array( $post_ids ) ) {
			wp_die( esc_html__( 'Bulk edit session expired. Please start over.', 'my-book-showroom' ) );
		}

		// Consume the transient so it cannot be replayed.
		delete_transient( self::TRANSIENT_PREFIX . $token );

		$fields  = $this->sanitize_fields();
		$applied = 0;

		foreach ( $post_ids as $post_id ) {
			$post_id = absint( $post_id );
			if ( ! $post_id ) {
				continue;
			}

			// Verify this is actually an MBS book post the current user can edit.
			$post = get_post( $post_id );
			if ( ! $post || MBS_POST_TYPE !== $post->post_type || ! current_user_can( 'edit_post', $post_id ) ) {
				continue;
			}

			if ( $this->apply_to_post( $post_id, $fields['genre_id'], $fields['series_id'], $fields['status'] ) ) {
				$applied++;
			}
		}

		// Redirect back to the books list with a success indicator.
		$redirect = add_query_arg(
			array(
				'post_type'        => MBS_POST_TYPE,
				'mbs_bulk_applied' => $applied,
			),
			admin_url( 'edit.php' )
		);
		wp_safe_redirect( $redirect );
		exit;
	}

	// ─────────────────────────────────────────────────────────────────────────
	// Private helpers
	// ─────────────────────────────────────────────────────────────────────────

	/**
	 * Sanitize and validate the three submitted field values.
	 *
	 * @return array{ genre_id: int, series_id: string, status: string }
	 */
	private function sanitize_fields(): array {
		$genre_id  = absint( wp_unslash( $_POST['mbs_bulk_genre']  ?? '' ) );
		$series_id = sanitize_key( wp_unslash( $_POST['mbs_bulk_series'] ?? '' ) );
		$status    = sanitize_key( wp_unslash( $_POST['mbs_bulk_status'] ?? '' ) );

		$allowed_statuses = array( 'publish', 'draft', 'private' );
		$status           = in_array( $status, $allowed_statuses, true ) ? $status : '';

		return array(
			'genre_id'  => $genre_id,
			'series_id' => $series_id,
			'status'    => $status,
		);
	}

	/**
	 * Apply the sanitized field changes to a single post.
	 *
	 * @param int    $post_id   Post being updated.
	 * @param int    $genre_id  Term ID to set, or 0 for no change.
	 * @param string $series_id Term ID string, '__remove__', or '' for no change.
	 * @param string $status    Post status string, or '' for no change.
	 * @return bool True if at least one field was changed.
	 */
	private function apply_to_post( int $post_id, int $genre_id, string $series_id, string $status ): bool {
		$changed = false;

		// Apply genre change.
		if ( $genre_id ) {
			wp_set_object_terms( $post_id, array( $genre_id ), MBS_TAX_GENRE );
			$changed = true;
		}

		// Apply series change.
		if ( '__remove__' === $series_id ) {
			wp_set_object_terms( $post_id, array(), MBS_TAX_SERIES );
			$changed = true;
		} elseif ( ! empty( $series_id ) && is_numeric( $series_id ) ) {
			wp_set_object_terms( $post_id, array( absint( $series_id ) ), MBS_TAX_SERIES );
			$changed = true;
		}

		// Apply status change.
		if ( ! empty( $status ) ) {
			wp_update_post( array(
				'ID'          => $post_id,
				'post_status' => $status,
			) );
			$changed = true;
		}

		return $changed;
	}
}
