<?php
/**
 * File Path: includes/metaboxes/class-mbs-metabox-universe.php
 *
 * Universe / Shared World Metabox — My Book Showroom
 *
 * Standalone metabox on the mbs_book editor screen.
 * Renders a multi-select list of mbs_universe terms.
 *
 * Gate behavior (F-017, locked 2026-04-22):
 *   · Toggle OFF (mbs_setting_advanced_universe_enabled = 0):
 *       Metabox is not registered at all — add_meta_box() is never called.
 *   · Toggle ON, not PRO:
 *       Metabox renders with an HTML comment only:
 *       <!-- MBS: Universe requires PRO license -->
 *   · Toggle ON, PRO:
 *       Full multi-select UI rendered; saves via wp_set_object_terms().
 *
 * Save is guarded by the same mbs_book_header_nonce that the parent book-header
 * metabox uses — we read the nonce from the shared book header save hook so
 * Universe assignments are always saved atomically with the rest of the book
 * header on every mbs_book save.
 *
 * @package    My_Book_Showroom
 * @subpackage Includes/Metaboxes
 * @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_Universe extends MBS_Metabox_Base { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedClassFound -- mbs_/MBS_ prefix is correct; scanner not configured with plugin prefix

	public function __construct() {
		// Only register the metabox when the toggle is on.
	}

	// -------------------------------------------------------------------------
	// Registration
	// -------------------------------------------------------------------------


	// -------------------------------------------------------------------------
	// Render
	// -------------------------------------------------------------------------

	/**
	 * Render the Universe metabox.
	 *
	 * @param WP_Post $post
	 * @return void
	 */
	public function render( WP_Post $post ): void {
		if ( ! mbs_is_pro() ) {
			// Project rule: PRO-only fields output an HTML comment, never a visible element.
			echo '<!-- MBS: Universe requires PRO license -->';
			return;
		}

		$all_universes      = get_terms( array( 'taxonomy' => MBS_TAX_UNIVERSE, 'hide_empty' => false ) );
		$assigned_ids       = wp_get_object_terms( $post->ID, MBS_TAX_UNIVERSE, array( 'fields' => 'ids' ) );
		$assigned_ids       = is_wp_error( $assigned_ids ) ? array() : array_map( 'intval', $assigned_ids );
		$universe_ro_hidden  = (int) MBS_Book_Meta_DAO::get( $post->ID, 'mbs_universe_reading_order_hidden', 0 );
		$universe_ro_pos     = MBS_Book_Meta_DAO::get_universe_reading_order_position( $post->ID );
		?>
		<div class="mbs-metabox-universe">

			<?php if ( is_wp_error( $all_universes ) || empty( $all_universes ) ) : ?>
				<p class="description">
					<?php esc_html_e( 'No universes exist yet.', 'my-book-showroom' ); ?>
					<a href="<?php echo esc_url( admin_url( 'edit-tags.php?taxonomy=' . MBS_TAX_UNIVERSE . '&post_type=' . MBS_POST_TYPE ) ); ?>">
						<?php esc_html_e( 'Add a universe →', 'my-book-showroom' ); ?>
					</a>
				</p>
			<?php else : ?>
				<ul style="margin:0; padding:0; list-style:none;">
					<?php foreach ( $all_universes as $term ) :
						$term_id    = (int) $term->term_id;
						$is_checked = in_array( $term_id, $assigned_ids, true );
					?>
						<li style="margin-bottom:4px;">
							<label style="display:flex; align-items:center; gap:6px; cursor:pointer;">
								<input type="checkbox"
								       name="mbs_universe_ids[]"
								       value="<?php echo esc_attr( $term_id ); ?>"
								       <?php checked( $is_checked ); ?> />
								<?php echo esc_html( $term->name ); ?>
							</label>
						</li>
					<?php endforeach; ?>
				</ul>
				<p class="description" style="margin-top:8px;">
					<?php esc_html_e( 'A book may belong to multiple universes.', 'my-book-showroom' ); ?>
				</p>
			<?php endif; ?>

			<?php
			// Inline "Create new universe" quick-add — no JS dependency beyond jQuery (always available in admin).
			?>
			<div style="margin-top:10px; border-top:1px solid #ddd; padding-top:8px;">
				<input type="text"
				       id="mbs-new-universe-name"
				       class="widefat"
				       placeholder="<?php esc_attr_e( 'New universe name…', 'my-book-showroom' ); ?>"
				       autocomplete="off"
				       style="margin-bottom:4px;">
				<button type="button" id="mbs-create-universe-btn" class="button button-small" style="width:100%;">
					<?php esc_html_e( 'Add Universe', 'my-book-showroom' ); ?>
				</button>
				<span id="mbs-create-universe-msg" style="display:none; font-size:12px; margin-top:4px; display:block;"></span>
			</div>

			<!-- Sprint RO-Display 5c + UX-RO-002: Universe RO controls -->
			<?php
			$_mbs_u_toggle_on      = (int) get_option( 'mbs_setting_advanced_universe_enabled', 0 );
			$_mbs_u_ro_on          = '1' === get_option( 'mbs_setting_show_universe_reading_order', '0' );
			$_mbs_u_disabled       = ( ! $_mbs_u_toggle_on || ! $_mbs_u_ro_on );
			$_mbs_u_ro_pos_enabled = ( $_mbs_u_toggle_on && $_mbs_u_ro_on );
			$_mbs_u_ro_pos_val     = null !== $universe_ro_pos ? (string) $universe_ro_pos : '';
			?>

			<?php /* ── Universe Reading Order Position (UX-RO-002) ──────────────── */ ?>
			<p style="margin-top:10px; border-top:1px solid #ddd; padding-top:8px;">
				<label for="mbs_universe_reading_order_position">
					<strong><?php esc_html_e( 'Universe Reading Order Position', 'my-book-showroom' ); ?></strong>
				</label><br>
				<input type="text"
				       id="mbs_universe_reading_order_position"
				       name="mbs_universe_reading_order_position"
				       class="small-text"
				       value="<?php echo esc_attr( $_mbs_u_ro_pos_val ); ?>"
				       placeholder="e.g. 1, 2, 0.5"
				       autocomplete="off"
				       <?php disabled( ! $_mbs_u_ro_pos_enabled, true ); ?>>
				<p class="description">
					<?php if ( ! $_mbs_u_ro_pos_enabled ) : ?>
						<?php esc_html_e( 'Enable Universe Reading Order in Settings -> Advanced to use this field.', 'my-book-showroom' ); ?>
					<?php else : ?>
						<?php esc_html_e( 'Suggested narrative sequence position within the universe. Leave blank for alphabetical fallback. Use decimals for books that fall between whole positions (e.g. 1.5).', 'my-book-showroom' ); ?>
					<?php endif; ?>
				</p>
			</p>

			<?php /* ── Suppress reading order toggle ──────────────────────────── */ ?>
			<p style="margin-top:10px; border-top:1px solid #ddd; padding-top:8px;">
				<label>
					<input type="checkbox"
					       name="mbs_universe_reading_order_hidden"
					       value="1"
					       <?php checked( 1, $universe_ro_hidden ); ?>
					       <?php disabled( $_mbs_u_disabled, true ); ?>>
					<strong><?php esc_html_e( 'Suppress reading order display for this book (all universes)', 'my-book-showroom' ); ?></strong>
				</label>
				<?php if ( $_mbs_u_disabled ) : ?>
				<p class="description"><?php esc_html_e( 'Enable Universe Reading Order in Settings -> Advanced to use this setting.', 'my-book-showroom' ); ?></p>
				<?php else : ?>
				<p class="description"><?php esc_html_e( 'When checked, the Reading Order row is hidden for this book on single-book pages, even if Universe Reading Order is enabled globally.', 'my-book-showroom' ); ?></p>
				<?php endif; ?>
			</p>

			<?php
			wp_add_inline_script( 'mbs-book-editor', <<<'JS'
(function($){
    $('#mbs-create-universe-btn').on('click', function(){
        var name = $('#mbs-new-universe-name').val().trim();
        var $msg = $('#mbs-create-universe-msg');
        if (!name) {
            $msg.css('color','#d63638').text('Please enter a universe name.').show();
            return;
        }
        var nonce = (window.MBS_BookEditorAjax || {}).nonce || '';
        $msg.css('color','#646970').text('Creating…').show();
        $.post(ajaxurl, {
            action: 'mbs_create_universe_term',
            nonce:  nonce,
            name:   name
        }, function(res){
            if (!res.success) {
                $msg.css('color','#d63638').text(res.data || 'Error.').show();
                return;
            }
            var d = res.data;
            var label = d.existed
                ? '"' + d.name + '" already exists — checked.'
                : '"' + d.name + '" added and checked.';
            // Add checkbox if not already present.
            var listId = 'mbs-universe-cb-' + d.term_id;
            if (!$('#' + listId).length) {
                var html = '<li style="margin-bottom:4px;"><label style="display:flex;align-items:center;gap:6px;cursor:pointer;"><input type="checkbox" id="' + listId + '" name="mbs_universe_ids[]" value="' + d.term_id + '" checked /> ' + $('<span>').text(d.name).html() + '</label></li>';
                $('.mbs-metabox-universe ul').append(html);
            } else {
                $('#' + listId).prop('checked', true);
            }
            $msg.css('color','#008a00').text(label).show();
            $('#mbs-new-universe-name').val('');
        }).fail(function(){
            $msg.css('color','#d63638').text('Server error. Please try again.').show();
        });
    });
    $('#mbs-new-universe-name').on('keydown', function(e){
        if (e.key === 'Enter') { e.preventDefault(); $('#mbs-create-universe-btn').trigger('click'); }
    });
})(jQuery);
JS
			);
			?>
		</div>
		<?php
	}

	// -------------------------------------------------------------------------
	// Save
	// -------------------------------------------------------------------------

	/**
	 * Save Universe taxonomy assignments on book save.
	 *
	 * Shares the book header nonce — fires on every mbs_book save when the
	 * Universe metabox is registered (toggle is on). PRO gate re-checked here
	 * to prevent crafted requests from assigning universe terms to non-PRO sites.
	 *
	 * @param int     $post_id
	 * @param WP_Post $post
	 * @return void
	 */
	public function save( int $post_id, WP_Post $post ): void {
		if ( ! $this->verify_save( $post_id, 'mbs_book_header_main_nonce', 'mbs_book_header_main' ) ) {
			return;
		}

		// PRO gate: silently clear universe assignments on non-PRO saves.
		if ( ! mbs_is_pro() ) {
			wp_set_object_terms( $post_id, array(), MBS_TAX_UNIVERSE );
			return;
		}

		$submitted_ids = array();
		if ( isset( $_POST['mbs_universe_ids'] ) && is_array( $_POST['mbs_universe_ids'] ) ) {
			foreach ( $_POST['mbs_universe_ids'] as $raw_id ) {
				$term_id = absint( $raw_id );
				if ( $term_id > 0 && term_exists( $term_id, MBS_TAX_UNIVERSE ) ) {
					$submitted_ids[] = $term_id;
				}
			}
		}

		wp_set_object_terms( $post_id, $submitted_ids, MBS_TAX_UNIVERSE );

		// Universe Reading Order suppress (Sprint RO-Display 5c — B-003 PRO gate on save).
		$universe_ro_hidden = isset( $_POST['mbs_universe_reading_order_hidden'] ) ? 1 : 0;
		MBS_Book_Meta_DAO::set_universe_reading_order_hidden( $post_id, $universe_ro_hidden );

		// Universe Reading Order Position (UX-RO-002).
		$uro_raw = isset( $_POST['mbs_universe_reading_order_position'] )
			? (string) wp_unslash( $_POST['mbs_universe_reading_order_position'] )
			: '';
		$uro_pos = ( '' === trim( $uro_raw ) ) ? null : max( 0.0, (float) $uro_raw );
		MBS_Book_Meta_DAO::set_universe_reading_order_position( $post_id, $uro_pos );
	}

	/**
	 * 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 {
		wp_nonce_field( 'mbs_universe_save', 'mbs_universe_nonce' );
		$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 {
		if ( ! $this->verify_save( $post_id, 'mbs_universe_nonce', 'mbs_universe_save' ) ) {
			return;
		}

		// PRO gate: silently clear universe assignments on non-PRO saves.
		if ( ! mbs_is_pro() ) {
			wp_set_object_terms( $post_id, array(), MBS_TAX_UNIVERSE );
			return;
		}

		$submitted_ids = array();
		if ( isset( $_POST['mbs_universe_ids'] ) && is_array( $_POST['mbs_universe_ids'] ) ) {
			foreach ( $_POST['mbs_universe_ids'] as $raw_id ) {
				$term_id = absint( $raw_id );
				if ( $term_id > 0 && term_exists( $term_id, MBS_TAX_UNIVERSE ) ) {
					$submitted_ids[] = $term_id;
				}
			}
		}

		wp_set_object_terms( $post_id, $submitted_ids, MBS_TAX_UNIVERSE );
	}

}
