/**
 * File Path: assets/js/adminjs/mbs-classification.js
 *
 * Classification metabox — 3-level Genre Picker + BISAC/Thema typeahead.
 *
 * Layer 0 : Six group buttons (FICTION / NONFICTION / RELIGION /
 *           PROFESSIONAL / HOBBIES & LEISURE / GENERAL).
 * Layer 1 : L1 category buttons for the selected group.
 * Layer 2 : L2 genre buttons populated from mbsClassification.l2map[l1].
 * Layer 3 : BISAC suggestion chips fetched via mbs_classify_search AJAX
 *           using the 'L1 / L2' description prefix as the query.
 * Typeahead: Free-text BISAC and Thema code search (mbs_classify_search).
 *
 * Localized object: mbsClassification
 *   .ajaxUrl  — admin-ajax.php URL
 *   .nonce    — mbs_classification_nonce
 *   .tierMax  — integer cap for the current tier
 *   .groupMap — object: group key → { label, categories[] }
 *   .l2map    — object: L1 string → sorted string[] of L2 genre names
 *   .i18n     — { remove, tierReached }
 *
 * @package My_Book_Showroom
 */
/* global mbsClassification */
(function ( $ ) {
	'use strict';
	var __ = wp.i18n.__;
	var sprintf = wp.i18n.sprintf;
	var escAttr = MBS_AdminUtils.escAttr;

	if ( typeof mbsClassification === 'undefined' ) {
		return;
	}

	// ── Constants ──────────────────────────────────────────────────────────────
	var AJAX_URL = mbsClassification.ajaxUrl;
	var NONCE    = mbsClassification.nonce;
	var TIER_MAX = parseInt( mbsClassification.tierMax, 10 ) || 1;
	var L2MAP    = mbsClassification.l2map || {};
	var GROUP_MAP = mbsClassification.groupMap || {};

	// Increments on every fetchSuggestions() call; stale AJAX responses are discarded.
	var fetchSuggestionsId = 0;

	// ── Helpers ─────────────────────────────────────────────────────────────────

	/** Build a removable code chip (BISAC / Thema). */
	function makeCodeChip( code, label, name ) {
		var $chip = $( '<span class="mbs-class-chip"></span>' );
		$chip.append( $( '<input type="hidden">' ).attr( 'name', name ).val( code ) );
		$chip.append( $( '<span class="mbs-class-chip-label"></span>' ).text( label ) );
		$chip.append(
			$( '<button type="button" class="mbs-class-chip-remove" aria-label="' + escAttr( __( 'Remove', 'my-book-showroom' ) ) + '">&times;</button>' )
		);
		return $chip;
	}

	/** Count active code chips in a container. */
	function chipCount( $container ) {
		return $container.find( '.mbs-class-chip' ).length;
	}

	/** Convert a string to a URL-safe slug. */
	function toSlug( str ) {
		return str.toLowerCase().replace( /[^a-z0-9]+/g, '-' ).replace( /^-|-$/g, '' );
	}

	/**
	 * Reverse a stored slug back to { l1, l2 } by scanning L2MAP.
	 * Returns null if not found.
	 */
	function slugToL1L2( slug ) {
		var result = null;
		$.each( L2MAP, function ( l1, l2list ) {
			$.each( l2list, function ( i, l2 ) {
				if ( toSlug( l1 ) + '--' + toSlug( l2 ) === slug ) {
					result = { l1: l1, l2: l2 };
					return false;
				}
			} );
			if ( result ) { return false; }
		} );
		return result;
	}

	// ── Chip removal ─────────────────────────────────────────────────────────

	$( document ).on( 'click', '.mbs-class-chip-remove', function () {
		var $chip   = $( this ).closest( '.mbs-class-chip' );
		var code    = $chip.find( 'input[type="hidden"]' ).val();
		var isBisac = $chip.closest( '#mbs-bisac-chips' ).length > 0;
		// Uncheck any corresponding suggestion checkbox so state stays in sync.
		$( '.mbs-suggestion-item input[data-code="' + code + '"]' ).prop( 'checked', false );
		$chip.remove();
		// If a BISAC chip was removed, refresh Thema suggestions.
		if ( isBisac ) { fetchThemaSuggestions(); }
	} );

	// ── Layer 0: Group buttons ───────────────────────────────────────────────

	$( document ).on( 'click', '.mbs-group-btn', function () {
		var $btn   = $( this );
		var group  = $btn.data( 'group' );
		var grp    = GROUP_MAP[ group ];
		var $step1 = $( '#mbs-genre-step-1' );
		var $step2 = $( '#mbs-genre-step-2' );
		var $step3 = $( '#mbs-genre-step-3' );

		// Highlight selected group button.
		$( '.mbs-group-btn' ).removeClass( 'mbs-group-btn--active' );
		$btn.addClass( 'mbs-group-btn--active' );

		// Reset lower layers.
		$( '.mbs-l1-buttons' ).empty();
		$( '.mbs-genre-chips' ).empty();
		$( '#mbs-genre-suggestions' ).empty();
		$step2.hide();
		$step3.hide();

		if ( ! grp || ! grp.categories || ! grp.categories.length ) {
			$step1.hide();
			return;
		}

		var $l1wrap = $( '.mbs-l1-buttons' );
		$.each( grp.categories, function ( i, l1 ) {
			$( '<button type="button" class="mbs-l1-btn button-secondary"></button>' )
				.text( l1 )
				.attr( 'data-l1', l1 )
				.appendTo( $l1wrap );
		} );

		$step1.show();
	} );

	// ── Layer 1: L1 category buttons ─────────────────────────────────────────

	$( document ).on( 'click', '.mbs-l1-btn', function () {
		var $btn   = $( this );
		var l1     = $btn.data( 'l1' );
		var $step2 = $( '#mbs-genre-step-2' );
		var $chips = $step2.find( '.mbs-genre-chips' );
		var $step3 = $( '#mbs-genre-step-3' );

		// Highlight selected L1 button.
		$( '.mbs-l1-btn' ).removeClass( 'mbs-l1-btn--active' );
		$btn.addClass( 'mbs-l1-btn--active' );

		$chips.empty();
		$step3.hide();
		$( '#mbs-genre-suggestions' ).empty();

		var l2list = L2MAP[ l1 ] || [];
		if ( ! l2list.length ) {
			$step2.hide();
			return;
		}

		// Re-order l2list for column-major (top-to-bottom) display in 3-column grid.
		// CSS Grid fills left-to-right, so interleave the items so column 1 reads
		// items[0..colSize-1] top-to-bottom, column 2 reads items[colSize..2*colSize-1], etc.
		var n       = l2list.length;
		var colSize = Math.ceil( n / 3 );
		var ordered = [];
		var row, col, idx;
		for ( row = 0; row < colSize; row++ ) {
			for ( col = 0; col < 3; col++ ) {
				idx = col * colSize + row;
				if ( idx < n ) { ordered.push( l2list[ idx ] ); }
			}
		}

		$.each( ordered, function ( i, l2 ) {
			var $item = $( '<label class="mbs-genre-item"></label>' );
			var $cb   = $( '<input type="checkbox" class="mbs-genre-cb">' )
				.attr( 'data-l2', l2 );
			$item.append( $cb ).append( document.createTextNode( '\u00a0' + l2 ) );
			$chips.append( $item );
		} );

		$step2.show();
	} );

	// ── Layer 2: Genre checkbox change ───────────────────────────────────────

	$( document ).on( 'change', '.mbs-genre-cb', function () {
		fetchSuggestions();
	} );

	// ── Layer 3: BISAC suggestion fetch ──────────────────────────────────────

	/**
	 * Fetch BISAC suggestions for all currently checked genre checkboxes.
	 *
	 * For each checked L2 genre label, scans L2MAP to find ALL L1 categories
	 * that contain that label (e.g. checking "Dystopian" finds FICTION,
	 * JUVENILE FICTION, and YOUNG ADULT FICTION), then queries each combination.
	 * This gives cross-subcategory BISAC results from a single genre selection.
	 */
	function fetchSuggestions() {
		var $step3    = $( '#mbs-genre-step-3' );
		var $bisacSug = $( '#mbs-genre-suggestions' );
		var myId      = ++fetchSuggestionsId;

		// Collect unique checked L2 genre labels.
		var checkedL2s = [];
		$( '.mbs-genre-cb:checked' ).each( function () {
			var l2 = $( this ).data( 'l2' );
			if ( l2 && checkedL2s.indexOf( l2 ) === -1 ) {
				checkedL2s.push( l2 );
			}
		} );

		$bisacSug.empty();

		if ( ! checkedL2s.length ) {
			$step3.hide();
			return;
		}
		$step3.show();

		// For each checked L2, find every L1 that contains it and build queries.
		var queries = [];
		$.each( checkedL2s, function ( i, l2 ) {
			$.each( L2MAP, function ( l1, l2list ) {
				if ( l2list.indexOf( l2 ) !== -1 ) {
					queries.push( l1 + ' / ' + l2 );
				}
			} );
		} );
		// Deduplicate.
		queries = queries.filter( function ( v, i, a ) { return a.indexOf( v ) === i; } );

		if ( ! queries.length ) { return; }

		var seen    = {};
		var pending = queries.length;

		$.each( queries, function ( i, query ) {
			$.post( AJAX_URL, {
				action : 'mbs_classify_search',
				nonce  : NONCE,
				scheme : 'bisac',
				query  : query
			} ).done( function ( response ) {
				if ( myId !== fetchSuggestionsId ) { return; }
				if ( response.success ) {
					$.each( response.data, function ( j, item ) {
						if ( seen[ item.code ] ) { return; }
						seen[ item.code ] = true;
						var lbl     = item.description ? item.code + ' \u2014 ' + item.description : item.code;
						var isAdded = $( '#mbs-bisac-chips input[value="' + item.code + '"]' ).length > 0;
						var $label  = $( '<label class="mbs-suggestion-item"></label>' );
						var $cb     = $( '<input type="checkbox">' )
							.attr( 'data-code',   item.code )
							.attr( 'data-scheme', 'bisac' )
							.prop( 'checked', isAdded );
						$label.append( $cb ).append( $( '<span></span>' ).text( lbl ) );
						$bisacSug.append( $label );
					} );
				}
				pending--;
				if ( pending === 0 ) { fetchThemaSuggestions(); }
			} ).fail( function () {
				pending--;
				if ( pending === 0 ) { fetchThemaSuggestions(); }
			} );
		} );
	}

	/**
	 * Query Thema suggestions directly from the BISAC codes currently in
	 * #mbs-bisac-chips. Called after any BISAC chip add or remove.
	 */
	function fetchThemaSuggestions() {
		var $themaSugGroup = $( '#mbs-thema-suggestions-group' );
		var $themaSug      = $( '#mbs-thema-suggestions' );
		var bisacCodes     = [];

		$( '#mbs-bisac-chips input[type="hidden"]' ).each( function () {
			bisacCodes.push( $( this ).val() );
		} );

		$themaSug.empty();
		$themaSugGroup.hide();

		if ( ! bisacCodes.length ) { return; }

		$.post( AJAX_URL, {
			action      : 'mbs_classify_thema_suggest',
			nonce       : NONCE,
			bisac_codes : bisacCodes
		} ).done( function ( response ) {
			if ( ! response.success || ! response.data.length ) { return; }
			var seen = {};
			$.each( response.data, function ( j, item ) {
				if ( seen[ item.code ] ) { return; }
				seen[ item.code ] = true;
				var lbl     = item.description ? item.code + ' — ' + item.description : item.code;
				var isAdded = $( '#mbs-thema-chips input[value="' + item.code + '"]' ).length > 0;
				var $label  = $( '<label class="mbs-suggestion-item"></label>' );
				var $cb     = $( '<input type="checkbox">' )
					.attr( 'data-code',   item.code )
					.attr( 'data-scheme', 'thema' )
					.prop( 'checked', isAdded );
				$label.append( $cb ).append( $( '<span></span>' ).text( lbl ) );
				$themaSug.append( $label );
			} );
			$themaSugGroup.show();
		} );
	}

	$( document ).on( 'change', '.mbs-suggestion-item input[type="checkbox"][data-code]', function () {
		var $cb       = $( this );
		var code      = $cb.data( 'code' );
		var scheme    = $cb.data( 'scheme' ) || 'bisac';
		var label     = $cb.siblings( 'span' ).text();
		var $target   = ( scheme === 'bisac' ) ? $( '#mbs-bisac-chips' ) : $( '#mbs-thema-chips' );
		var inputName = ( scheme === 'bisac' ) ? 'mbs_bisac_codes[]' : 'mbs_thema_codes[]';

		if ( $cb.prop( 'checked' ) ) {
			if ( chipCount( $target ) >= TIER_MAX ) {
				alert( sprintf( __( 'Limit of %d code(s) reached for your plan.', 'my-book-showroom' ), TIER_MAX ) );
				$cb.prop( 'checked', false );
				return;
			}
			// Already added (e.g. manually via typeahead) — keep checkbox checked but don't duplicate.
			if ( $target.find( 'input[value="' + code + '"]' ).length ) { return; }
			$target.append( makeCodeChip( code, label, inputName ) );
			// Flash the newly added chip so the user sees where it landed.
			var $newChip = $target.find( 'input[value="' + code + '"]' ).closest( '.mbs-class-chip' );
			$newChip.addClass( 'mbs-chip--flash' );
			setTimeout( function () { $newChip.removeClass( 'mbs-chip--flash' ); }, 1400 );
			// If a BISAC chip was just added, refresh Thema suggestions.
			if ( scheme === 'bisac' ) { fetchThemaSuggestions(); }
		} else {
			// Unchecked — remove from BISAC/Thema chips.
			$target.find( 'input[value="' + code + '"]' ).closest( '.mbs-class-chip' ).remove();
			// If a BISAC chip was just removed, refresh Thema suggestions.
			if ( scheme === 'bisac' ) { fetchThemaSuggestions(); }
		}
	} );

	// ── Typeahead (BISAC and Thema) ───────────────────────────────────────────

	var typeaheadTimer = null;

	$( document ).on( 'input', '.mbs-class-search', function () {
		var $input   = $( this );
		var scheme   = $input.data( 'scheme' );
		var query    = $.trim( $input.val() );
		var $results = $( '#' + $input.attr( 'id' ).replace( '-search', '-results' ) );

		clearTimeout( typeaheadTimer );
		$results.empty().hide();
		if ( query.length < 2 ) { return; }

		typeaheadTimer = setTimeout( function () {
			$.post( AJAX_URL, {
				action : 'mbs_classify_search',
				nonce  : NONCE,
				scheme : scheme,
				query  : query
			} ).done( function ( response ) {
				if ( ! response.success || ! response.data.length ) { return; }
				$.each( response.data, function ( i, item ) {
					var lbl = item.description ? item.code + ' — ' + item.description : item.code;
					$( '<li class="mbs-class-result"></li>' )
						.text( lbl )
						.attr( 'data-code', item.code )
						.attr( 'data-scheme', scheme )
						.appendTo( $results );
				} );
				$results.show();
			} );
		}, 300 );
	} );

	$( document ).on( 'click', '.mbs-class-result', function () {
		var $li       = $( this );
		var code      = $li.data( 'code' );
		var scheme    = $li.data( 'scheme' );
		var label     = $li.text();
		var $results  = $li.closest( 'ul' );
		var $input    = $( '#' + $results.attr( 'id' ).replace( '-results', '-search' ) );
		var $target   = ( scheme === 'bisac' ) ? $( '#mbs-bisac-chips' ) : $( '#mbs-thema-chips' );
		var inputName = ( scheme === 'bisac' ) ? 'mbs_bisac_codes[]' : 'mbs_thema_codes[]';

		if ( chipCount( $target ) >= TIER_MAX ) {
			alert( sprintf( __( 'Limit of %d code(s) reached for your plan.', 'my-book-showroom' ), TIER_MAX ) );
			$results.empty().hide();
			return;
		}
		if ( ! $target.find( 'input[value="' + code + '"]' ).length ) {
			$target.append( makeCodeChip( code, label, inputName ) );
			// If a BISAC code was added via typeahead, refresh Thema suggestions.
			if ( scheme === 'bisac' ) { fetchThemaSuggestions(); }
		}
		$results.empty().hide();
		$input.val( '' );
	} );

	$( document ).on( 'click', function ( e ) {
		if ( ! $( e.target ).closest( '.mbs-class-typeahead-wrap' ).length ) {
			$( '.mbs-class-results' ).empty().hide();
		}
	} );

}( jQuery ) );


/* ── Bowker mutual-exclusion ────────────────────────────────────────────────
 * If the user picks code X in Primary, X is hidden from Secondary (and vice
 * versa). If the other select already holds the now-excluded value, it resets
 * to the blank placeholder.  Snapshot of all options is taken once on load so
 * options can be fully restored when the opposing select is cleared.
 * ─────────────────────────────────────────────────────────────────────────── */
( function ( $ ) {
	'use strict';

	var PRIMARY   = '#mbs_bowker_primary';
	var SECONDARY = '#mbs_bowker_secondary';

	/**
	 * Rebuild the option list of $target, excluding excludeVal.
	 * If $target's current value equals excludeVal, reset it to ''.
	 *
	 * @param {jQuery} $target     The select to rebuild.
	 * @param {Array}  allOptions  Snapshot: [{val, text}, ...].
	 * @param {string} excludeVal  Value to omit ('' = omit nothing).
	 */
	function syncSelect( $target, allOptions, excludeVal ) {
		var current = $target.val();
		$target.empty();
		$.each( allOptions, function ( i, opt ) {
			if ( opt.val !== excludeVal ) {
				$target.append(
					$( '<option>' ).val( opt.val ).text( opt.text )
				);
			}
		} );
		// Restore selection if it still exists, otherwise reset to blank.
		if ( current !== excludeVal && $target.find( 'option[value="' + current + '"]' ).length ) {
			$target.val( current );
		} else {
			$target.val( '' );
		}
	}

	$( function () {
		var $primary   = $( PRIMARY );
		var $secondary = $( SECONDARY );

		if ( ! $primary.length || ! $secondary.length ) {
			return;
		}

		// Snapshot all options once from the primary select (both share the same list).
		var allOptions = [];
		$primary.find( 'option' ).each( function () {
			allOptions.push( { val: $( this ).val(), text: $( this ).text() } );
		} );

		// Apply initial state (handles saved values on page load).
		syncSelect( $secondary, allOptions, $primary.val() );
		syncSelect( $primary,   allOptions, $secondary.val() );

		$primary.on( 'change', function () {
			syncSelect( $secondary, allOptions, $( this ).val() );
		} );

		$secondary.on( 'change', function () {
			syncSelect( $primary, allOptions, $( this ).val() );
		} );
	} );

}( jQuery ) );

/* ── Content Warnings collapsible toggle ────────────────────────────────────
 * Toggles aria-expanded on the button and the hidden attribute on the list.
 * Starts collapsed when no warnings are checked (set by PHP); starts expanded
 * when at least one warning is saved (also set by PHP via aria-expanded attr).
 * ─────────────────────────────────────────────────────────────────────────── */
( function ( $ ) {
	'use strict';

	$( function () {
		var $btn  = $( '.mbs-warnings-toggle' );
		var $list = $( '#mbs-warnings-list' );

		if ( ! $btn.length || ! $list.length ) {
			return;
		}

		$btn.on( 'click', function () {
			var expanded = $btn.attr( 'aria-expanded' ) === 'true';
			$btn.attr( 'aria-expanded', expanded ? 'false' : 'true' );
			if ( expanded ) {
				$list.attr( 'hidden', '' );
			} else {
				$list.removeAttr( 'hidden' );
			}
		} );
	} );

}( jQuery ) );
