<?php
/**
 * File Path: includes/blocks/class-mbs-blocks-registry.php
 *
 * Gutenberg Blocks Registry
 *
 * Registers all My Book Showroom Gutenberg blocks and enqueues editor assets
 * using the client-approved flat filenames: blocks-editor.js and blocks-editor.css.
 *
 * This class is pure wiring. PHP render logic lives in dedicated block classes.
 * Block definitions live in MBS_Block_Definitions::get() (class-mbs-block-definitions.php).
 * To add a new block: create class-mbs-block-[name].php with a static render() method,
 * then append one entry to MBS_Block_Definitions::get(). Do not add render logic here.
 *
 * Block render classes:
 *   MBS_Block_Author_Directory, MBS_Block_Author, MBS_Block_Bestseller,
 *   MBS_Block_Book_Club, MBS_Block_Book_Cover, MBS_Block_Book_Grid,
 *   MBS_Block_Book_Teaser, MBS_Block_Buy_Links, MBS_Block_Catalog,
 *   MBS_Block_Collection, MBS_Block_Coming_Soon, MBS_Block_Endorsements,
 *   MBS_Block_Esp_Signup, MBS_Block_Featured_Book, MBS_Block_Genre_Cloud,
 *   MBS_Block_Genre, MBS_Block_Tags, MBS_Block_New_Releases, MBS_Block_Reading_Order,
 *   MBS_Block_Related_Books, MBS_Block_Search, MBS_Block_Series_Directory,
 *   MBS_Block_Series, MBS_Block_Single_Book, MBS_Block_Term_List,
 *   MBS_Block_Timeline, MBS_Block_Universe
 *
 * @package    My_Book_Showroom
 * @subpackage Includes/Blocks
 * @since      1.0.0
 */

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

class MBS_Blocks_Registry {

	/**
	 * Constructor -- hooks registration to the 'init' action.
	 *
	 * Priority 15: MBS_Plugin::load_services() runs at init p10, which is where
	 * new MBS_Blocks_Registry() is called. PHP 8's foreach copy-on-write means
	 * any callback added to p10 mid-iteration is never seen by the current pass.
	 * Using p15 (a new priority level) ensures resort_active_iterations() inserts
	 * it into the future iteration queue and register() fires reliably.
	 */
	public function __construct() {
		add_action( 'init',                array( $this, 'register' ), 15 );
		add_filter( 'block_categories_all', array( $this, 'register_block_category' ), 10, 2 );
	}

	/**
	 * Add the My Book Showroom category to the Gutenberg block inserter.
	 *
	 * Prepends the MBS category so it appears at the top of the inserter
	 * panel rather than being sorted alphabetically among plugin categories.
	 *
	 * @param array   $categories Existing block categories.
	 * @param WP_Post $post       Post being edited.
	 * @return array
	 */
	public function register_block_category( array $categories, $post ): array {
		return array_merge(
			array(
				array(
					'slug'  => 'mbs',
					'title' => __( 'My Book Showroom', 'my-book-showroom' ),
					'icon'  => null,
				),
			),
			$categories
		);
	}

	/**
	 * Register all blocks, patterns, and editor assets.
	 */
	public function register(): void {
		if ( ! function_exists( 'register_block_type' ) ) {
			return;
		}

		// Idempotency guard: skip re-registration if already done (e.g. unit tests).
		if ( WP_Block_Type_Registry::get_instance()->is_registered( 'mbs/featured-book' ) ) {
			return;
		}

		// Enqueue editor assets (flat filenames per client request).
		add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_editor_assets' ) );

		// Catalog block uses directory-based registration (reads block.json).
		// Registered first so it appears at the top of the FREE section in the inserter.
		// render_callback is passed separately because block.json has no renderCallback key.
		register_block_type(
			plugin_dir_path( dirname( __FILE__ ) ) . 'blocks/mbs-catalog/',
			array(
				'render_callback' => array( 'MBS_Block_Catalog', 'render' ),
			)
		);

		// Register all data-driven blocks.
		foreach ( MBS_Block_Definitions::get() as $def ) {
			register_block_type(
				$def['name'],
				array(
					'attributes'      => $def['attributes'],
					'render_callback' => array( $def['class'], 'render' ),
				)
			);
		}

		// Register the MBS block pattern category and patterns (S13-16).
		$this->register_block_pattern_category();
		$this->register_block_patterns();
	}

	/**
	 * Enqueue block editor assets using client-specified flat filenames.
	 *
	 * After enqueueing blocks-editor.css, appends an inline style that overrides
	 * :root token defaults with saved color choices from Settings -> Appearance.
	 * Falls back to FREE defaults on fresh install or if mbs_get_setting() is absent.
	 *
	 * S13-16: Also registers the `mbs-blocks` handle (mbs-blocks.js) that
	 * block.json references as its editorScript for mbs/catalog.
	 */
	public function enqueue_editor_assets(): void {
		wp_enqueue_script(
			'mbs-blocks-editor',
			MBS_ASSETS_URL . 'js/blocks-editor.js',
			array( 'wp-blocks', 'wp-element', 'wp-block-editor', 'wp-components', 'wp-server-side-render', 'wp-i18n' ),
			MBS_VERSION,
			true
		);
		wp_set_script_translations( 'mbs-blocks-editor', 'my-book-showroom', MBS_DIR . 'languages' );

		// mbs-blocks.js (editorScript for mbs/catalog via block.json).
		// Enqueued explicitly before the per-block loop so registerBlockType('mbs/catalog')
		// fires first in JS, placing Book Catalog at the top of the mbs inserter panel.
		wp_enqueue_script(
			'mbs-blocks',
			MBS_ASSETS_URL . 'js/mbs-blocks.js',
			array( 'mbs-blocks-editor' ),
			MBS_VERSION,
			true
		);
		wp_set_script_translations( 'mbs-blocks', 'my-book-showroom', MBS_DIR . 'languages' );

		// Per-block JS files — each depends on the shared core (mbs-blocks-editor).
		$block_slugs = array(
			// FREE blocks first -- controls inserter order within the mbs category.
			'featured-book', 'book-grid', 'buy-links', 'term-list', 'single-book',
			'series', 'collection', 'genre', 'tags', 'author', 'genre-cloud',
			'search', 'book-cover', 'book-teaser', 'endorsements', 'new-releases',
			'timeline',
			// PRO blocks last -- sorted to bottom of the mbs inserter panel.
			'universe', 'author-directory', 'series-directory', 'reading-order',
			'coming-soon', 'book-club', 'arc-signup', 'related-books', 'bestseller',
			'esp-signup',
		);
		foreach ( $block_slugs as $slug ) {
			wp_enqueue_script(
				'mbs-block-' . $slug,
				MBS_ASSETS_URL . 'js/blocks/block-' . $slug . '.js',
				array( 'mbs-blocks-editor' ),
				MBS_VERSION,
				true
			);
			wp_set_script_translations( 'mbs-block-' . $slug, 'my-book-showroom', MBS_DIR . 'languages' );
		}

		wp_enqueue_style(
			'mbs-blocks-editor',
			MBS_ASSETS_URL . 'css/blocks-editor.css',
			array( 'wp-edit-blocks' ),
			MBS_VERSION
		);

		// Propagate saved color tokens into the editor (Task #41).
		if ( function_exists( 'mbs_get_setting' ) ) {
			$accent    = MBS_Color_Helper::sanitize( mbs_get_setting( 'appearance', 'accent',    '#4AABB8' ) );
			$button_bg = MBS_Color_Helper::sanitize( mbs_get_setting( 'appearance', 'button_bg', '#2C3E6B' ) );
			$navy      = MBS_Color_Helper::sanitize( mbs_get_setting( 'appearance', 'navy',      '#2C3E6B' ) );

			if ( $accent || $button_bg || $navy ) {
				wp_add_inline_style(
					'mbs-blocks-editor',
					sprintf(
						':root { --mbs-accent: %s; --mbs-button-bg: %s; --mbs-navy: %s; }',
						esc_attr( $accent    ?: '#4AABB8' ),
						esc_attr( $button_bg ?: '#2C3E6B' ),
						esc_attr( $navy      ?: '#2C3E6B' )
					)
				);
			}
		}

		// UNREGFREE-13-17: Inject front-end CSS into the Gutenberg editor iframe
		// so that ServerSideRender previews render with correct grid layout,
		// cover aspect ratios, and column counts. Without this, the editor iframe
		// has no mbs-front.css and all blocks default to single-column block flow.
		// add_editor_style() is the WordPress-prescribed mechanism for loading CSS
		// into the editor canvas (editor-styles-wrapper) when called within
		// the enqueue_block_editor_assets callback.
		$front_editor_styles = array(
			MBS_ASSETS_URL . 'css/mbs-front.css',
			MBS_ASSETS_URL . 'css/front/mbs-front-fse.css',
			MBS_ASSETS_URL . 'css/front/mbs-front-modal.css',
			MBS_ASSETS_URL . 'css/front/mbs-front-term-list.css',
			MBS_ASSETS_URL . 'css/front/mbs-front-buy-links.css',
			MBS_ASSETS_URL . 'css/front/mbs-front-list-layout.css',
			MBS_ASSETS_URL . 'css/front/mbs-front-card-hover.css',
			MBS_ASSETS_URL . 'css/front/mbs-front-categorization.css',
			MBS_ASSETS_URL . 'css/front/mbs-front-responsive.css',
			MBS_ASSETS_URL . 'css/front/mbs-front-dark.css',
			MBS_ASSETS_URL . 'css/front/mbs-front-single.css',
			MBS_ASSETS_URL . 'css/front/mbs-front-hero.css',
			MBS_ASSETS_URL . 'css/front/mbs-front-series-badge.css',
			MBS_ASSETS_URL . 'css/front/mbs-front-power-badge.css',
			MBS_ASSETS_URL . 'css/front/mbs-front-coming-soon-badge.css',
			MBS_ASSETS_URL . 'css/front/mbs-front-timeline.css',
			MBS_ASSETS_URL . 'css/front/mbs-front-series-shelf.css',
			MBS_ASSETS_URL . 'css/front/mbs-front-signup.css',
			MBS_ASSETS_URL . 'css/front/mbs-front-social-share.css',
			MBS_ASSETS_URL . 'css/front/mbs-front-look-inside.css',
			MBS_ASSETS_URL . 'css/front/mbs-front-press-kit.css',
			MBS_ASSETS_URL . 'css/front/mbs-front-pro-author-hero.css',
			MBS_ASSETS_URL . 'css/front/mbs-front-pro-flip.css',
			// EDIT-CSS-001: PRO author directory grid (CRITICAL — collapses to single column without).
			MBS_ASSETS_URL . 'css/front/mbs-front-pro-author-directory.css',
			// EDIT-CSS-002: PRO layout engine (filter/popup layouts — HIGH).
			MBS_ASSETS_URL . 'css/mbs-front-pro.css',
			// EDIT-CSS-003: PRO slider layout (HIGH).
			MBS_ASSETS_URL . 'css/front/mbs-front-pro-slider.css',
			// EDIT-CSS-004: ARC signup form — is_admin() guard in block render skips enqueue (HIGH).
			MBS_ASSETS_URL . 'css/mbs-arc-signup.css',
			// EDIT-CSS-005: Banner container overflow:hidden for series/universe/collection (LOW).
			MBS_ASSETS_URL . 'css/front/mbs-front-pro-series-archive.css',
			MBS_ASSETS_URL . 'css/front/mbs-front-pro-universe-archive.css',
			MBS_ASSETS_URL . 'css/front/mbs-front-pro-collection-archive.css',
		);
		foreach ( $front_editor_styles as $_mbs_editor_css ) {
			add_editor_style( $_mbs_editor_css );
		}
		unset( $front_editor_styles, $_mbs_editor_css );
	}

	// -------------------------------------------------------------------------
	// Block definitions
	// -------------------------------------------------------------------------

	/**
	 * Return all block definitions as a flat array.
	 *
	 * Each entry: [ 'name' => 'mbs/slug', 'class' => 'MBS_Block_Xxx', 'attributes' => array(...) ].
	 * Exclude mbs/catalog -- it uses directory-based registration (see register()).
	 *
	 * To add a block: create the render class, then append one array entry here.
	 *
	 * @return array[]
	 */

	// -------------------------------------------------------------------------
	// Block patterns (S13-16)
	// -------------------------------------------------------------------------

	/**
	 * Register the MBS block pattern category.
	 *
	 * Called from register() so it runs on 'init', the correct hook for
	 * register_block_pattern_category().
	 */
	private function register_block_pattern_category(): void {
		if ( ! function_exists( 'register_block_pattern_category' ) ) {
			return;
		}

		register_block_pattern_category(
			'mbs',
			array(
				'label'       => _x( 'My Book Showroom', 'Block pattern category', 'my-book-showroom' ),
				'description' => __( 'Patterns for displaying book catalogs, featured books, and author pages.', 'my-book-showroom' ),
			)
		);
	}

	/**
	 * Register built-in MBS block patterns (S13-16 AC 3).
	 *
	 * "MBS: 3-column book grid" and "MBS: Featured book spotlight".
	 * Both appear in the block inserter under the MBS category.
	 */
	private function register_block_patterns(): void {
		if ( ! function_exists( 'register_block_pattern' ) ) {
			return;
		}

		register_block_pattern(
			'mbs/3-column-book-grid',
			array(
				'title'       => __( 'MBS: 3-column book grid', 'my-book-showroom' ),
				'description' => __( 'A three-column book catalog grid. Adjust the number of books, sort order, and taxonomy filters in the block settings panel.', 'my-book-showroom' ),
				'categories'  => array( 'mbs' ),
				'keywords'    => array( 'books', 'catalog', 'grid', 'library', 'showroom' ),
				'content'     => '<!-- wp:group {"align":"wide","layout":{"type":"constrained"}} -->'
					. '<div class="wp-block-group alignwide">'
					. '<!-- wp:heading {"level":2} -->'
					. '<h2 class="wp-block-heading">' . esc_html( mbs_get_setting( 'formatting', 'archive_title', 'pub' === mbs_get_license_tier_label() ? __( 'Our Books', 'my-book-showroom' ) : __( 'My Books', 'my-book-showroom' ) ) ) . '</h2>'
					. '<!-- /wp:heading -->'
					. '<!-- wp:mbs/catalog {"columns":3,"layout":"grid","limit":6,"orderby":"date"} /-->'
					. '</div>'
					. '<!-- /wp:group -->',
			)
		);

		register_block_pattern(
			'mbs/featured-book-spotlight',
			array(
				'title'       => __( 'MBS: Featured book spotlight', 'my-book-showroom' ),
				'description' => __( 'Highlight a single book with its cover, title, and buy links.', 'my-book-showroom' ),
				'categories'  => array( 'mbs' ),
				'keywords'    => array( 'book', 'featured', 'spotlight', 'hero', 'showroom' ),
				'content'     => '<!-- wp:group {"align":"wide","layout":{"type":"constrained"}} -->'
					. '<div class="wp-block-group alignwide">'
					. '<!-- wp:mbs/featured-book {"bookId":0} /-->'
					. '</div>'
					. '<!-- /wp:group -->',
			)
		);
	}
}
