/**
 * Tam Rank™ frontend output — block editor script.
 *
 * Editor half of the two dynamic blocks registered in
 * includes/class-tamrank-frontend-blocks.php. Deliberately handwritten ES5
 * against the wp.* globals: no JSX, no npm, no webpack, no src/ directory and no
 * compiled bundle. A build pipeline would drag node_modules, a second copy of
 * every source file and a minified artefact into the wp.org zip, and wp.org asks
 * to see the readable source next to any compiled JavaScript. This file IS its
 * own readable source, and it ships as-is.
 *
 * There is no markup here, on purpose. Both blocks save nothing and render
 * through a PHP render_callback that calls the very same method the shortcode
 * calls, so the editor previews the real output through wp.serverSideRender
 * rather than an approximation of it. Do not "improve" the preview by rebuilding
 * the markup in JavaScript — that is the second renderer both features were
 * written to avoid.
 *
 * Titles, descriptions, icons and attributes are NOT declared here either: they
 * come from the server-side register_block_type() call and reach the editor
 * through WordPress' own block bootstrap. Only edit() and save() live here.
 *
 * @package TamRank
 * @since 1.3.7
 */

( function ( wp, config ) {
	'use strict';

	if ( ! wp || ! wp.blocks || ! wp.element || ! wp.blockEditor || ! wp.components ) {
		return;
	}

	var el = wp.element.createElement;
	var InspectorControls = wp.blockEditor.InspectorControls;
	var useBlockProps = wp.blockEditor.useBlockProps;
	var PanelBody = wp.components.PanelBody;
	var TextControl = wp.components.TextControl;
	var SelectControl = wp.components.SelectControl;
	var ToggleControl = wp.components.ToggleControl;

	// The global build exports either the component itself or a module namespace
	// with it on `default`, depending on the WordPress version. Accept both.
	var ServerSideRender = wp.serverSideRender && wp.serverSideRender.default
		? wp.serverSideRender.default
		: wp.serverSideRender;

	var controlsByBlock = ( config && config.controls ) || {};
	var panelTitle = ( config && config.panelTitle ) || 'Display';

	/**
	 * Is this yes/no attribute value a "yes"?
	 *
	 * Same rule as TamRank_HTML_Sitemap::is_yes(); the list of "no" spellings is
	 * handed over by PHP so the two cannot drift. It matters for a page that was
	 * written as a shortcode with home="off" and later converted to a block.
	 *
	 * @param {string} value    Raw attribute value.
	 * @param {Array}  noValues Values that count as "no".
	 * @return {boolean} True when the value reads as yes.
	 */
	function isYes( value, noValues ) {
		var list = noValues || [];
		var normalised = String( value === undefined || value === null ? '' : value )
			.trim()
			.toLowerCase();

		return list.indexOf( normalised ) === -1;
	}

	/**
	 * Build one sidebar control from its PHP descriptor.
	 *
	 * @param {Object}   descriptor  Control descriptor from PHP.
	 * @param {Object}   attributes  Current block attributes.
	 * @param {Function} setAttributes Block attribute setter.
	 * @return {Object} A wp.element node.
	 */
	function renderControl( descriptor, attributes, setAttributes ) {
		var key = descriptor.key;
		var value = attributes[ key ];

		if ( descriptor.type === 'toggle' ) {
			return el( ToggleControl, {
				key: key,
				label: descriptor.label,
				help: descriptor.help || undefined,
				checked: isYes( value, descriptor.noValues ),
				onChange: function ( checked ) {
					var next = {};
					next[ key ] = checked ? descriptor.onValue : descriptor.offValue;
					setAttributes( next );
				}
			} );
		}

		// __next40pxDefaultSize opts into the control size that becomes the
		// default in WordPress 7.1. Without it, 6.8 and up log a deprecation
		// warning to the console on every editor load. Older WordPress simply
		// does not know the prop; its production React build ignores unknown
		// props silently, so the opt-in is safe down to the 6.0 minimum.
		if ( descriptor.type === 'select' ) {
			return el( SelectControl, {
				key: key,
				label: descriptor.label,
				help: descriptor.help || undefined,
				value: value,
				options: descriptor.options || [],
				__next40pxDefaultSize: true,
				onChange: function ( selected ) {
					var next = {};
					next[ key ] = selected;
					setAttributes( next );
				}
			} );
		}

		return el( TextControl, {
			key: key,
			label: descriptor.label,
			help: descriptor.help || undefined,
			value: value === undefined || value === null ? '' : value,
			__next40pxDefaultSize: true,
			onChange: function ( typed ) {
				var next = {};
				next[ key ] = typed;
				setAttributes( next );
			}
		} );
	}

	/**
	 * The edit component shared by both blocks.
	 *
	 * @param {string} blockName Registered block name.
	 * @return {Function} An edit() implementation.
	 */
	function makeEdit( blockName ) {
		return function edit( props ) {
			var descriptors = controlsByBlock[ blockName ] || [];
			var blockProps = useBlockProps();

			var controls = descriptors.map( function ( descriptor ) {
				return renderControl( descriptor, props.attributes, props.setAttributes );
			} );

			// urlQueryArgs carries the post being edited to the block-renderer
			// endpoint. The breadcrumb needs it: its trail is built from the page
			// it sits on, and a REST request carries no query context of its own.
			var preview = ServerSideRender
				? el( ServerSideRender, {
					block: blockName,
					attributes: props.attributes,
					urlQueryArgs: { post_id: currentPostId() }
				} )
				: null;

			return el(
				'div',
				blockProps,
				el(
					InspectorControls,
					{},
					el( PanelBody, { title: panelTitle, initialOpen: true }, controls )
				),
				preview
			);
		};
	}

	/**
	 * The post currently open in the editor, if the editor store is there.
	 *
	 * @return {number|undefined} Post id, or undefined outside the post editor
	 *                            (the widget screen and the site editor).
	 */
	function currentPostId() {
		if ( ! wp.data || ! wp.data.select ) {
			return undefined;
		}

		var editor = wp.data.select( 'core/editor' );
		if ( ! editor || ! editor.getCurrentPostId ) {
			return undefined;
		}

		return editor.getCurrentPostId() || undefined;
	}

	[ 'tamrank/sitemap', 'tamrank/breadcrumb' ].forEach( function ( blockName ) {
		wp.blocks.registerBlockType( blockName, {
			apiVersion: 3,
			edit: makeEdit( blockName ),
			// Dynamic block: nothing is written into post_content but the block
			// comment itself. The markup comes from the PHP render_callback.
			save: function () {
				return null;
			}
		} );
	} );
} )( window.wp, window.tamrankFrontendBlocks );
