<?php
/**
 * File Path: includes/metaboxes/class-mbs-metabox-section-order.php
 *
 * Section Order Metabox
 *
 * Manages per-book section ordering and visibility using the authoritative
 * mbs_section_order table. Visibility checkboxes allow authors to hide
 * individual sections without removing them from the order (UX-NAV-001).
 *
 * @package MyBookShowroom
 * @since   1.0.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
// phpcs:disable WordPress.Security.ValidatedSanitizedInput -- input sanitized at point of use (sanitize_text_field, absint, wp_kses_post, etc.); false positives from PHPCS not tracing sanitization helpers called before this method
class MBS_Metabox_Section_Order extends MBS_Metabox_Base { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedClassFound -- mbs_/MBS_ prefix is correct; scanner not configured with plugin prefix

	public function __construct() {
	}

	public function render( WP_Post $post ): void {
		wp_nonce_field( 'mbs_section_order_save', 'mbs_section_order_nonce' );

		// Build available section map from option-filtered canonical data.
		$section_data       = MBS_Section_Helper::get_available_section_data();
		$is_pro_user        = mbs_is_pro();
		$available_sections = array_combine(
			array_keys( $section_data ),
			array_column( $section_data, 'label' )
		);

		// PLG-033: IDs, data attributes, and class names must exactly match the
		// selectors in mbs-book-editor.js: #mbs-section-sortable, data-slug,
		// .mbs-sortable-item, #mbs-section-order-table, #mbs-section-order-body,
		// and #mbs_section_order_input. The Formatting Settings tab renders these
		// correctly; the per-book metabox previously used different names.

		// Switch to get_order_admin() so hidden sections still appear in the drag
		// list with their checkbox unchecked (UX-NAV-001 Phase G).
		$admin_rows    = MBS_Section_Order_DAO::get_order_admin( $post->ID );
		$per_book_keys = array_values( array_filter(
			array_column( $admin_rows, 'key' ),
			static function( $s ) use ( $available_sections ) {
				return isset( $available_sections[ $s ] );
			}
		) );

		// Build visibility state map. Default to visible for new/missing keys.
		$visibility_state = array();
		foreach ( $admin_rows as $row ) {
			$visibility_state[ $row['key'] ] = $row['is_visible'];
		}

		// UX-EDIT-002 Phase 3-A: Merge per-book order with any missing canonical
		// sections so newly-added sections always appear in the UI.
		$missing_keys = array_values( array_diff( array_keys( $available_sections ), $per_book_keys ) );
		$merged_order = array_merge( $per_book_keys, $missing_keys );

		echo '<div class="mbs-section-order">';
		echo '<p>' . esc_html__( 'Sort sections and save to set the order they appear on this book\'s front-end page.', 'my-book-showroom' ) . '</p>';

		// Sortable list -- shown when jQuery UI Sortable is available (mbs-book-editor.js).
		echo '<ul id="mbs-section-sortable" style="display:none;">';
		foreach ( $merged_order as $slug ) {
			if ( isset( $available_sections[ $slug ] ) ) {
				$is_pro_section = isset( $section_data[ $slug ] ) && 'pro' === $section_data[ $slug ]['tier'];
				$locked         = ! $is_pro_user && $is_pro_section;
				$locked_class   = $locked ? ' mbs-pro-locked' : '';
				$is_visible     = $visibility_state[ $slug ] ?? true;
				echo '<li data-slug="' . esc_attr( $slug ) . '" class="mbs-sortable-item' . esc_attr( $locked_class ) . '">';
				if ( $is_pro_user ) {
					echo '<span class="dashicons dashicons-menu mbs-drag-handle"></span>';
				}
				if ( $locked ) {
					echo '<span class="dashicons dashicons-lock mbs-pro-lock-icon"></span>';
				}
				echo '<span class="mbs-sortable-label">' . esc_html( $available_sections[ $slug ] ) . '</span>';
				echo '<label class="mbs-section-visibility">';
				echo '<input type="checkbox"'
				   . ' name="mbs_section_visible[' . esc_attr( $slug ) . ']"'
				   . ' value="1"'
				   . checked( $is_visible, true, false )
				   . disabled( $locked, true, false ) . '>';
				echo ' ' . esc_html__( 'Show on page', 'my-book-showroom' );
				echo '</label>';
				echo '</li>';
			}
		}
		echo '</ul>';

		// Fallback table -- shown when Sortable is unavailable; mbs-book-editor.js
		// hides it and shows the sortable list when jQuery UI Sortable is present.
		echo '<table id="mbs-section-order-table" style="display:none;"><tbody id="mbs-section-order-body">';
		foreach ( $merged_order as $slug ) {
			if ( isset( $available_sections[ $slug ] ) ) {
				$is_pro_section = isset( $section_data[ $slug ] ) && 'pro' === $section_data[ $slug ]['tier'];
				$locked         = ! $is_pro_user && $is_pro_section;
				$row_class      = $locked ? ' class="mbs-pro-locked"' : '';
				$is_visible     = $visibility_state[ $slug ] ?? true;
				echo '<tr data-slug="' . esc_attr( $slug ) . '"' . $row_class . '>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $row_class is a hardcoded CSS class string
				echo '<td class="mbs-drag-handle"><span class="dashicons dashicons-menu"></span></td>';
				echo '<td>';
				if ( $locked ) {
					echo '<span class="dashicons dashicons-lock mbs-pro-lock-icon"></span>';
				}
				echo esc_html( $available_sections[ $slug ] ) . '</td>';
				echo '<td>';
				echo '<label class="mbs-section-visibility">';
				echo '<input type="checkbox"'
				   . ' name="mbs_section_visible[' . esc_attr( $slug ) . ']"'
				   . ' value="1"'
				   . checked( $is_visible, true, false )
				   . disabled( $locked, true, false ) . '>';
				echo ' ' . esc_html__( 'Show on page', 'my-book-showroom' );
				echo '</label>';
				echo '</td>';
				echo '<td>';
				echo '<button type="button" class="button button-small mbs-up" title="'
				       . esc_attr__( 'Move up', 'my-book-showroom' ) . '">&#8593;</button> ';
				echo '<button type="button" class="button button-small mbs-down" title="'
				       . esc_attr__( 'Move down', 'my-book-showroom' ) . '">&#8595;</button>';
				echo '</td></tr>';
			}
		}
		echo '</tbody></table>';

		// BUG-SO-005 fix: hidden input carries ALL merged_order keys (including
		// is_visible=0 rows) so save_order() does not delete hidden-section rows.
		echo '<input type="hidden" name="mbs_section_order" id="mbs_section_order_input"'
		   . ' value="' . esc_attr( implode( ',', $merged_order ) ) . '">';

		// Phase 1-C (UX-EDIT-002): Reset button -- triggers bindReset() in admin-section-order.js.
		echo '<p class="mbs-section-order-actions">'
		   . '<button type="button" class="button button-secondary mbs-reset-order">'
		   . esc_html__( 'Reset to Global Default', 'my-book-showroom' )
		   . '</button></p>';

		echo '</div>';
	}

	public function save( int $post_id ): void {
		if ( ! $this->verify_save( $post_id, 'mbs_section_order_nonce', 'mbs_section_order_save' ) ) {
			return;
		}

		if ( isset( $_POST['mbs_section_order'] ) && is_string( $_POST['mbs_section_order'] ) ) {
			// E-004: wp_unslash() before explode().
			$order_array = array_filter(
				array_map( 'sanitize_key', explode( ',', wp_unslash( $_POST['mbs_section_order'] ) ) )
			);
			// Allowlist: strip PRO keys for FREE users.
			$canonical     = MBS_Section_Helper::get_canonical_keys();
			$section_data  = MBS_Section_Helper::get_canonical_section_data();
			$allowed_tiers = mbs_is_pro() ? array( 'free', 'all', 'pro' ) : array( 'free', 'all' );
			$allowed_keys  = array_filter(
				$canonical,
				static function( $key ) use ( $section_data, $allowed_tiers ) {
					return isset( $section_data[ $key ] )
						&& in_array( $section_data[ $key ]['tier'], $allowed_tiers, true );
				}
			);
			$order_array = array_values( array_filter(
				$order_array,
				static function( $key ) use ( $allowed_keys ) {
					return in_array( $key, $allowed_keys, true );
				}
			) );

			if ( ! empty( $order_array ) ) {
				$raw_visible       = isset( $_POST['mbs_section_visible'] )
					? (array) wp_unslash( $_POST['mbs_section_visible'] )
					: array();
				$safe_visible_keys = array_map( 'sanitize_key', array_keys( $raw_visible ) );

				$visibility_map = array();
				foreach ( $order_array as $section_key ) {
					$visibility_map[ $section_key ] = in_array( $section_key, $safe_visible_keys, true );
				}

				MBS_Section_Order_DAO::save_order( $post_id, $order_array );
				MBS_Section_Order_DAO::save_visibility( $post_id, $visibility_map );
			}
		}
	}

	/**
	 * Public helper used by templates and shortcodes.
	 */
	public static function get_order( int $post_id ): array {
		return MBS_Section_Order_DAO::get_order( $post_id );
	}

	/**
	 * Render one editor section within the unified book editor shell.
	 *
	 * Passthrough -- single-section handler (Sprint 169).
	 *
	 * @since  1.0.0
	 * @param  string  $section_key  Section key (ignored for single-section handlers).
	 * @param  WP_Post $post         Current post object.
	 * @return void
	 */
	public function render_section( string $section_key, WP_Post $post ): void {
		$this->render( $post );
	}

	/**
	 * Save one editor section within the unified book editor shell.
	 *
	 * Passthrough -- single-section handler (Sprint 169).
	 *
	 * @since  1.0.0
	 * @param  string  $section_key  Section key (ignored for single-section handlers).
	 * @param  int     $post_id      Post ID.
	 * @param  WP_Post $post         Post object.
	 * @return void
	 */
	public function save_section( string $section_key, int $post_id, WP_Post $post ): void {
		$this->save( $post_id );
	}

}
