<?php
/**
 * File Path: includes/admin/class-mbs-book-editor.php
 *
 * Book Editor
 *
 * The single metabox that replaces all individual mbs_book metaboxes. On
 * construction it registers domain config, registers the one metabox,
 * suppresses all legacy metaboxes at priority 99, enqueues editor assets,
 * and wires the unified save hook.
 *
 * Not a singleton. Constructed exactly once via new MBS_Book_Editor() in
 * load_metaboxes(), replacing new MBS_Metabox_Onix() at that position.
 *
 * @package    My_Book_Showroom
 * @subpackage Includes/Admin
 * @since      1.2.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_Book_Editor { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedClassFound -- mbs_/MBS_ prefix is correct; scanner not configured with plugin prefix

	/**
	 * Wire constructor hooks and register domain config.
	 *
	 * @since 1.2.0
	 */
	public function __construct() {
		$this->register_domain_config();
		add_action( 'add_meta_boxes',             array( $this, 'register_metabox' ) );
		add_action( 'add_meta_boxes',             array( $this, 'suppress_legacy_metaboxes' ), 99 );
		add_action( 'save_post_' . MBS_POST_TYPE, array( $this, 'save' ), 10, 2 );
		add_action( 'admin_enqueue_scripts',      array( $this, 'enqueue_assets' ) );
		add_filter( 'redirect_post_location',     array( $this, 'restore_editor_tab' ), 10, 2 );
	}

	/**
	 * Register the single Book Editor metabox.
	 *
	 * Context: normal, priority: high. Render callback is array( $this, 'render' ).
	 *
	 * @return void
	 * @since 1.2.0
	 */
	public function register_metabox(): void {
		add_meta_box(
			'mbs_book_editor',
			__( 'Book Editor', 'my-book-showroom' ),
			array( $this, 'render' ),
			MBS_POST_TYPE,
			'normal',
			'high'
		);
	}

	/**
	 * Remove all legacy MBS and WP core metaboxes from the mbs_book edit screen.
	 *
	 * Runs at priority 99 on add_meta_boxes. remove_meta_box() is called for
	 * all three contexts (normal, side, advanced) per ID — a call with the wrong
	 * context is a silent no-op, so covering all three guarantees removal.
	 *
	 * submitdiv (Publish box) and mbs-export-book-bible (Book Bible sidebar) are
	 * intentionally excluded.
	 *
	 * @return void
	 * @since 1.2.0
	 */
	public function suppress_legacy_metaboxes(): void {
		$ids = array(
			// Legacy MBS metaboxes replaced by this shell:
			'mbs_book_header',
			'mbs_book_club',
			'mbs-book-details',
			'mbs_buy_links',
			'mbs_categorization',
			'mbs-classification',
			'mbs_contributors',
			'mbs_endorsements',
			'mbs_format_repeater',
			'mbs-onix',
			'mbs-publisher-imprint',
			'mbs-related-products',
			'mbs_section_order',
			'mbs_book_universe',
			'mbs_arc',
			// WordPress core metaboxes — always suppress on mbs_book:
			'postcustom',
			'commentsdiv',
			'trackbacksdiv',
			'authordiv',
			// DO NOT add: 'submitdiv' or 'mbs-export-book-bible'
		);
		foreach ( $ids as $id ) {
			remove_meta_box( $id, MBS_POST_TYPE, 'normal' );
			remove_meta_box( $id, MBS_POST_TYPE, 'side' );
			remove_meta_box( $id, MBS_POST_TYPE, 'advanced' );
		}

		// WordPress taxonomy metaboxes — suppressed for all MBS taxonomies.
		// Non-hierarchical: 'tagsdiv-{taxonomy}'.
		// Hierarchical (mbs_genre): '{taxonomy}div' — WordPress default ID pattern.
		$tax_ids = array(
			'tagsdiv-' . MBS_TAX_AUTHOR,
			MBS_TAX_GENRE . 'div',            // hierarchical — WP registers as {taxonomy}div
			'tagsdiv-' . MBS_TAX_CONTRIBUTOR,
			'tagsdiv-' . MBS_TAX_SERIES,
			'tagsdiv-' . MBS_TAX_COLLECTION,
			'tagsdiv-' . MBS_TAX_UNIVERSE,
			'tagsdiv-' . MBS_TAX_TAG,
			'tagsdiv-' . MBS_TAX_THEME,
			'tagsdiv-' . MBS_TAX_CONTENT_WARNING,
			'tagsdiv-' . MBS_TAX_PUBLISHER,
			'tagsdiv-' . MBS_TAX_IMPRINT,
		);
		foreach ( $tax_ids as $id ) {
			remove_meta_box( $id, MBS_POST_TYPE, 'normal' );
			remove_meta_box( $id, MBS_POST_TYPE, 'side' );
			remove_meta_box( $id, MBS_POST_TYPE, 'advanced' );
		}
	}

	/**
	 * Render the editor shell.
	 *
	 * Guards against an empty domain list (misconfigured register_domain_config()).
	 * Opens the wrapper div and adds the .mbs-js class synchronously via an inline
	 * script so that panels are hidden before the browser lays them out (flash prevention).
	 * Renders the tab bar once, then for each domain renders its section sub-nav and
	 * all content panels. Closes the wrapper div after the outer domain loop completes.
	 *
	 * @param WP_Post $post The post being edited.
	 * @return void
	 * @since 1.2.0
	 */
	public function render( WP_Post $post ): void {
		$registry = MBS_Editor_Registry::get_instance();
		$domains  = $registry->get_domains();
		if ( empty( $domains ) ) {
			return;
		}

		echo '<div id="mbs-editor-shell-wrap" class="mbs-editor-wrap">';
		echo '<script>document.getElementById("mbs-editor-shell-wrap").classList.add("mbs-js");</script>';

		$editor_icon_map = self::get_editor_tab_icon_map();

		// Build domain-level icon HTML map (slug comes from '_domain' key or string value).
		$domain_icons = array();
		foreach ( $domains as $domain ) {
			$dk    = $domain['key'];
			$entry = isset( $editor_icon_map[ $dk ] ) ? $editor_icon_map[ $dk ] : null;
			if ( null === $entry ) {
				continue;
			}
			$slug = is_array( $entry ) ? ( isset( $entry['_domain'] ) ? $entry['_domain'] : '' ) : (string) $entry;
			if ( $slug ) {
				$domain_icons[ $dk ] = mbs_icon( $slug, array(
					'size'       => '20px',
					'aria_label' => '', // decorative -- label text is adjacent
				) );
			}
		}

		MBS_Admin_Tab_UI::render_tab_bar( $domains, $domains[0]['key'], $domain_icons );

		foreach ( $domains as $domain ) {
			$dk             = $domain['key'];
			$entry          = isset( $editor_icon_map[ $dk ] ) ? $editor_icon_map[ $dk ] : null;
			$section_icons  = array();
			if ( is_array( $entry ) ) {
				foreach ( $domain['sections'] as $section ) {
					$sk   = $section['key'];
					$slug = isset( $entry[ $sk ] ) ? $entry[ $sk ] : '';
					if ( $slug ) {
						$section_icons[ $sk ] = mbs_icon( $slug, array(
							'size'       => '16px',
							'aria_label' => '', // decorative -- label text is adjacent
						) );
					}
				}
			}
			MBS_Admin_Tab_UI::render_section_nav(
				$domain['sections'],
				! empty( $domain['sections'] ) ? $domain['sections'][0]['key'] : '',
				$dk,
				$section_icons
			);
			foreach ( $domain['sections'] as $section ) {
				MBS_Admin_Tab_UI::render_content_open( $domain['key'], $section['key'] );
				$registry->render_section( $domain['key'], $section['key'], $post );
				MBS_Admin_Tab_UI::render_content_close();
			}
		}

		// Hidden input — JS updates this on every tab/section switch so the
		// save redirect can restore the active tab via restore_editor_tab().
		echo '<input type="hidden" name="mbs_editor_active_tab" id="mbs-editor-active-tab" value="">';
		echo '</div>'; // #mbs-editor-shell-wrap
	}

	/**
	 * Dispatch save to all registered handlers via MBS_Editor_Registry::save_all().
	 *
	 * Guards against unauthorized saves (REST, CLI, or callers lacking edit_post).
	 * Autosave rejection is handled separately by nonce absence in each handler's
	 * verify_save() — no wp_is_post_autosave() guard is needed here.
	 *
	 * @param int     $post_id Post ID.
	 * @param WP_Post $post    The post being saved.
	 * @return void
	 * @since 1.2.0
	 */
	public function save( int $post_id, WP_Post $post ): void {
		if ( wp_is_post_autosave( $post_id ) || wp_is_post_revision( $post_id ) ) {
			return;
		}
		if ( ! current_user_can( 'edit_post', $post_id ) ) {
			return;
		}
		MBS_Editor_Registry::get_instance()->save_all( $post_id, $post );
	}

	/**
	 * Append the active editor tab as a URL fragment to the post save redirect.
	 *
	 * Reads mbs_editor_active_tab from POST data (kept current by JS on every
	 * tab/section switch). Validated against a strict domain/section key pattern
	 * before use. Only modifies the redirect for mbs_book posts.
	 *
	 * @param string $location Redirect URL built by WordPress.
	 * @param int    $post_id  Post ID being saved.
	 * @return string URL with #domain/section appended, or original URL if no
	 *                valid tab value was submitted.
	 * @since 1.2.0
	 */
	public function restore_editor_tab( string $location, int $post_id ): string {
		if ( MBS_POST_TYPE !== get_post_type( $post_id ) ) {
			return $location;
		}
		$tab = isset( $_POST['mbs_editor_active_tab'] )
			? sanitize_text_field( wp_unslash( $_POST['mbs_editor_active_tab'] ) )
			: '';
		if ( $tab && preg_match( '/^[a-z0-9][a-z0-9-]*\/[a-z0-9][a-z0-9-]*$/', $tab ) ) {
			$location .= '#' . $tab;
		}
		return $location;
	}

	/**
	 * Enqueue Book Editor CSS and JS on the mbs_book single-post edit screen.
	 *
	 * get_current_screen() returns null during admin AJAX requests; the null guard
	 * prevents a fatal error on those requests. The base='post' check restricts
	 * enqueueing to post.php only (not the post list screen where base='edit').
	 *
	 * @return void
	 * @since 1.2.0
	 */
	public function enqueue_assets(): void {
		$screen = get_current_screen();
		if ( ! $screen || MBS_POST_TYPE !== $screen->post_type || 'post' !== $screen->base ) {
			return;
		}
		wp_enqueue_style(
			'mbs-book-editor-styles',
			plugins_url( 'assets/css/admin/mbs-book-editor.css', MBS_DIR . 'bookshowroom.php' ),
			array( 'wp-admin' ),
			(string) ( @filemtime( MBS_ASSETS_DIR . 'css/admin/mbs-book-editor.css' ) ?: MBS_VERSION )
		);
		wp_enqueue_script(
			'mbs-editor-shell',
			plugins_url( 'assets/js/adminjs/mbs-editor-shell.js', MBS_DIR . 'bookshowroom.php' ),
			array( 'jquery', 'mbs-book-editor' ),
			(string) ( @filemtime( MBS_ASSETS_DIR . 'js/adminjs/mbs-editor-shell.js' ) ?: MBS_VERSION ),
			true
		);
		wp_enqueue_script(
			'mbs-admin-categorization',
			plugins_url( 'assets/js/adminjs/admin-categorization.js', MBS_DIR . 'bookshowroom.php' ),
			array( 'jquery' ),
			(string) ( @filemtime( MBS_ASSETS_DIR . 'js/adminjs/admin-categorization.js' ) ?: MBS_VERSION ),
			true
		);
		// UX-EDIT-002 Phase 0-E: enqueue section-order JS in the book editor so
		// Reset button (Phase 1-C) and drag-and-drop work in the metabox context.
		// jquery-ui-sortable bundled with WordPress; declared as dependency.
		wp_enqueue_script(
			'mbs-admin-section-order',
			plugins_url( 'assets/js/adminjs/admin-section-order.js', MBS_DIR . 'bookshowroom.php' ),
			array( 'jquery', 'jquery-ui-sortable' ),
			(string) ( @filemtime( MBS_ASSETS_DIR . 'js/adminjs/admin-section-order.js' ) ?: MBS_VERSION ),
			true
		);
		wp_localize_script(
			'mbs-admin-section-order',
			'mbsSectionOrderData',
			array( 'defaultOrder' => MBS_Section_Helper::get_canonical_keys() )
		);
		if ( mbs_is_pub() ) {
			// ONIX CSS + JS: only needed for PUB tier — metabox only renders for PUB users.
			wp_enqueue_style(
				'mbs-onix-metabox',
				plugins_url( 'assets/css/mbs-admin-onix.css', MBS_DIR . 'bookshowroom.php' ),
				array(),
				(string) ( @filemtime( MBS_ASSETS_DIR . 'css/mbs-admin-onix.css' ) ?: MBS_VERSION )
			);
			wp_enqueue_script(
				'mbs-onix-metabox',
				plugins_url( 'assets/js/adminjs/mbs-onix-metabox.js', MBS_DIR . 'bookshowroom.php' ),
				array( 'jquery', 'wp-i18n' ),
				(string) ( @filemtime( MBS_ASSETS_DIR . 'js/adminjs/mbs-onix-metabox.js' ) ?: MBS_VERSION ),
				true
			);
			wp_set_script_translations( 'mbs-onix-metabox', 'my-book-showroom', MBS_DIR . 'languages' );
		}
		// Classification CSS + JS + localization — MBS_Metabox_Classification is
		// registered as a class-name string only and never instantiated before
		// admin_enqueue_scripts fires; assets are promoted here.
		wp_enqueue_style(
			'mbs-classification',
			MBS_ASSETS_URL . 'css/mbs-admin-classification.css',
			array(),
			(string) ( @filemtime( MBS_ASSETS_DIR . 'css/mbs-admin-classification.css' ) ?: MBS_VERSION )
		);
		foreach ( array( 'layout', 'genre-picker', 'chips', 'typeahead' ) as $_comp ) {
			wp_enqueue_style(
				'mbs-classification-' . $_comp,
				plugins_url( 'assets/css/admin/classification/classification-' . $_comp . '.css', MBS_DIR . 'bookshowroom.php' ),
				array( 'mbs-classification' ),
				(string) ( @filemtime( MBS_ASSETS_DIR . 'css/admin/classification/classification-' . $_comp . '.css' ) ?: MBS_VERSION )
			);
		}
		wp_enqueue_script(
			'mbs-classification',
			MBS_ASSETS_URL . 'js/adminjs/mbs-classification.js',
			array( 'jquery', 'wp-i18n' ),
			(string) ( @filemtime( MBS_ASSETS_DIR . 'js/adminjs/mbs-classification.js' ) ?: MBS_VERSION ),
			true
		);
		wp_set_script_translations( 'mbs-classification', 'my-book-showroom', MBS_DIR . 'languages' );
		$_tier_max = mbs_is_pub() ? PHP_INT_MAX : ( mbs_is_pro() ? 10 : 1 );
		wp_localize_script(
			'mbs-classification',
			'mbsClassification',
			array(
				'ajaxUrl'  => admin_url( 'admin-ajax.php' ),
				'nonce'    => wp_create_nonce( 'mbs_classification_nonce' ),
				'tierMax'  => $_tier_max,
				'groupMap' => MBS_Genre_Engine::get_group_map(),
				'l2map'    => MBS_Genre_Engine::build_l2map(),
				'isPub'    => mbs_is_pub(),
			)
		);
	}

	/**
	 * Returns the nested domain → sub-section → icon_slug mapping for Book Editor icons.
	 *
	 * Top-level keys are domain tab keys. Each value is either:
	 *   - A plain string icon slug (domains with no rendered sub-section icons).
	 *   - An array with '_domain' => domain-tab icon slug, plus section_key => section icon slug.
	 * Kept separate from register_domain_config() for SRP — icon data is separate
	 * from label/tier/section-count config. Consumed by render().
	 *
	 * Keys must match actual registered domain/section keys exactly.
	 * 'discovery-social' domain uses actual key (plan used 'reader-links').
	 * 'section-order-config' domain uses actual key (plan used 'section-order').
	 *
	 * @return array<string, string|array<string, string>>
	 * @since 1.2.0
	 */
	private static function get_editor_tab_icon_map(): array {
		return array(
			'core-book-info'       => array(
				'_domain'            => 'editor-core-book-info',
				'main'               => 'cbi-main',
				'media'              => 'cbi-media',
				'awards'             => 'cbi-awards',
				'admin'              => 'cbi-admin',
			),
			'book-details'         => array(
				'_domain'            => 'editor-book-details',
				'discovery'          => 'bkd-discovery',
				'main'               => 'bkd-details', // section key is 'main'; icon is bkd-details
			),
			// Domain is registered as 'discovery-social'; plan referred to it as 'reader-links'.
			'discovery-social'     => array(
				'_domain'            => 'editor-reader-links',
				'main'               => 'rl-platforms',   // section key 'main' = Reader Platforms
				'global-links'       => 'rl-buy-links',   // section key 'global-links' = Buy Links
			),
			'formats'              => 'editor-formats', // no sub-section icons
			'publication-status'   => array(
				'_domain'            => 'editor-publication-status',
				'pub-fields'         => 'ps-publication',   // section key is 'pub-fields'
				'contributors'       => 'ps-contributors',
				'publisher'          => 'ps-publisher',
			),
			'book-club'            => 'editor-book-club',     // no sub-section icons
			'pre-release-arc'      => 'editor-arc',           // no sub-section icons
			'classification'       => array(
				'_domain'            => 'editor-classification',
				'codes'              => 'cls-codes',
				'target-audience'    => 'cls-target-audience',
				'trade-data'         => 'cls-trade-data',
			),
			// Domain is registered as 'section-order-config'; plan referred to it as 'section-order'.
			'section-order-config' => 'editor-section-order', // no sub-section icons
			'onix'                 => array(
				'_domain'            => 'editor-onix',
				'main'               => 'onix-record',
				'product-relations'  => 'onix-product-relations',
			),
		);
	}

	/**
	 * Register domain and section configuration with MBS_Editor_Registry.
	 *
	 * Called from the constructor. Stubbed in Phase 1; populated in Phase 4 Task 4.1.
	 *
	 * @return void
	 * @since 1.2.0
	 */
	private function register_domain_config(): void {
		$reg = MBS_Editor_Registry::get_instance();

		// ── 1. Core Book Info ─────────────────────────────────────────────────
		$reg->register_domain( array(
			'key'      => 'core-book-info',
			'label'    => __( 'Core Book Info', 'my-book-showroom' ),
			'sections' => array(
				array( 'key' => 'main',   'label' => __( 'Main',   'my-book-showroom' ), 'handler' => MBS_Metabox_Book_Header::class ),
				array( 'key' => 'media',  'label' => __( 'Media',  'my-book-showroom' ), 'handler' => MBS_Metabox_Book_Header::class ),
				array( 'key' => 'awards', 'label' => __( 'Awards', 'my-book-showroom' ), 'handler' => MBS_Metabox_Book_Header::class ),
				array( 'key' => 'admin',  'label' => __( 'Admin',  'my-book-showroom' ), 'handler' => MBS_Metabox_Book_Header::class ),
			),
		) );
		// Conditional WooCommerce tab — appended after base domain (register_section
		// throws RuntimeException if core-book-info is not found).
		if ( class_exists( 'WooCommerce' ) ) {
			$reg->register_section( 'core-book-info', array(
				'key'     => 'woocommerce',
				'label'   => __( 'WooCommerce', 'my-book-showroom' ),
				'handler' => MBS_Metabox_Book_Header::class,
			) );
		}

		// ── 2. Book Details ───────────────────────────────────────────────────
		// ── 2. Book Details ────────────────────────────────────────────────────
		$reg->register_domain( array(
			'key'      => 'book-details',
			'label'    => __( 'Book Details', 'my-book-showroom' ),
			'sections' => array(
				array( 'key' => 'discovery', 'label' => __( 'Discovery', 'my-book-showroom' ), 'handler' => MBS_Metabox_Book_Discovery::class ),
				array( 'key' => 'main',      'label' => __( 'Details',   'my-book-showroom' ), 'handler' => MBS_Metabox_Book_Details::class ),
			),
		) );
		// ── 3. Reader Links ────────────────────────────────────────────────────
		// Same handler (MBS_Metabox_Buy_Links) serves both sections; routing is
		// done by $section_key inside render_section() and save_section().
		$reg->register_domain( array(
			'key'      => 'discovery-social',
			'label'    => __( 'Reader Links', 'my-book-showroom' ),
			'sections' => array(
				array( 'key' => 'main',         'label' => __( 'Reader Platforms', 'my-book-showroom' ), 'handler' => MBS_Metabox_Buy_Links::class ),
				array( 'key' => 'global-links', 'label' => __( 'Buy Links',       'my-book-showroom' ), 'handler' => MBS_Metabox_Buy_Links::class ),
			),
		) );

		// ── 4. Formats ────────────────────────────────────────────────────────
		$reg->register_domain( array(
			'key'      => 'formats',
			'label'    => __( 'Formats', 'my-book-showroom' ),
			'sections' => array(
				array( 'key' => 'main', 'label' => __( 'Formats', 'my-book-showroom' ), 'handler' => MBS_Metabox_Format_Repeater::class ),
			),
		) );

		// ── 5. Publication & Status ───────────────────────────────────────────
		// First section key is 'pub-fields' (not 'main') so MBS_Metabox_Book_Header
		// routes to publication fields only in render_section() and save_section().
		$reg->register_domain( array(
			'key'      => 'publication-status',
			'label'    => __( 'Publication & Status', 'my-book-showroom' ),
			'sections' => array(
				array( 'key' => 'pub-fields',   'label' => __( 'Publication', 'my-book-showroom' ), 'handler' => MBS_Metabox_Book_Header::class ),
				array( 'key' => 'contributors', 'label' => __( 'Contributors', 'my-book-showroom' ), 'handler' => MBS_Metabox_Contributors::class ),
				array( 'key' => 'publisher',    'label' => __( 'Publisher',    'my-book-showroom' ), 'handler' => MBS_Metabox_Publisher_Imprint::class ),
			),
		) );

		// ── 6. Book Club Resources ────────────────────────────────────────────
		$reg->register_domain( array(
			'key'      => 'book-club',
			'label'    => __( 'Book Club Resources', 'my-book-showroom' ),
			'sections' => array(
				array( 'key' => 'main', 'label' => __( 'Resources', 'my-book-showroom' ), 'handler' => MBS_Metabox_Book_Club::class ),
			),
		) );

		// ── 7. Pre-Release ARC ───────────────────────────────────────────────
		// MBS_Metabox_Arc_Render is the ARC UI renderer. Save is a no-op (AJAX only).
		$reg->register_domain( array(
			'key'      => 'pre-release-arc',
			'label'    => __( 'Pre-Release ARC', 'my-book-showroom' ),
			'sections' => array(
				array( 'key' => 'main', 'label' => __( 'ARC', 'my-book-showroom' ), 'handler' => MBS_Metabox_Arc_Render::class ),
			),
		) );

		// ── 8. Classification ─────────────────────────────────────────────────
		$reg->register_domain( array(
			'key'      => 'classification',
			'label'    => __( 'Classification', 'my-book-showroom' ),
			'sections' => array(
				array( 'key' => 'codes',           'label' => __( 'Codes',           'my-book-showroom' ), 'handler' => MBS_Metabox_Classification::class ),
				array( 'key' => 'target-audience', 'label' => __( 'Target Audience', 'my-book-showroom' ), 'handler' => MBS_Metabox_Classification::class ),
				array( 'key' => 'trade-data',      'label' => __( 'Trade Data',      'my-book-showroom' ), 'handler' => MBS_Metabox_Classification::class ),
			),
		) );
		// ── 9. Section Order (Task 4.4: Option A — standalone domain) ────────
		// Section Order controls front-end display order, not editor field grouping;
		// isolating it as a standalone domain avoids coupling to core-book-info.
		$reg->register_domain( array(
			'key'      => 'section-order-config',
			'label'    => __( 'Section Order', 'my-book-showroom' ),
			'sections' => array(
				array( 'key' => 'main', 'label' => __( 'Order', 'my-book-showroom' ), 'handler' => MBS_Metabox_Section_Order::class ),
			),
		) );

		// ── 10. ONIX Distribution ──────────────────────────────────────────────
		if ( mbs_is_pub() ) {
			$reg->register_domain( array(
				'key'      => 'onix',
				'label'    => __( 'ONIX Distribution', 'my-book-showroom' ),
				'sections' => array(
					array( 'key' => 'main',              'label' => __( 'ONIX',              'my-book-showroom' ), 'handler' => MBS_Metabox_Onix::class ),
					array( 'key' => 'product-relations', 'label' => __( 'Product Relations', 'my-book-showroom' ), 'handler' => MBS_Metabox_Related_Products::class ),
				),
			) );
		}
	}
}
