/**
 * SiteStaffr — Shared modal component.
 *
 * One chrome implementation (backdrop, card, close X, focus trap, Esc,
 * focus-return, body scroll lock, mobile bottom-sheet) for every admin
 * dialog, plus promise-based confirm/alert replacements for the native
 * browser dialogs.
 *
 * Markup contract for enhance(): an overlay element (shown/hidden by the
 * component) containing a `.sitestaffr-modal` card. Elements inside the
 * overlay with `.sitestaffr-modal-close` or `[data-sitestaffr-dismiss]`
 * close it.
 *
 * Exposes window.SiteStaffrModal:
 *   enhance(overlayEl, opts) -> { open(), close(), el }
 *   confirm({ title, message, confirmLabel, cancelLabel, danger }) -> Promise<boolean>
 *   alertDialog({ title, message, okLabel }) -> Promise<void>
 *
 * @package    SiteStaffr
 * @subpackage SiteStaffr/admin/js
 * @since      1.33.6
 */
( function () {
	'use strict';

	var FOCUSABLE = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
	var openCount = 0;
	var l10n = window.sitestaffrModalL10n || {};

	function isVisible( el ) {
		return !! ( el.offsetWidth || el.offsetHeight || el.getClientRects().length );
	}

	function lockScroll() {
		openCount++;
		document.body.style.overflow = 'hidden';
	}

	function unlockScroll() {
		openCount = Math.max( 0, openCount - 1 );
		if ( 0 === openCount ) {
			document.body.style.overflow = '';
		}
	}

	/**
	 * Wire an existing overlay element with the shared chrome behaviors.
	 *
	 * opts:
	 *   onOpen(controller)   — after the modal is shown
	 *   onClose(controller)  — after the modal is hidden
	 *   closeOnBackdrop      — default true
	 *   closeOnEsc           — default true
	 *   initialFocus         — CSS selector focused on open (default: the card)
	 *   card                 — CSS selector for the dialog card (default: .sitestaffr-modal)
	 */
	function enhance( overlayEl, opts ) {
		opts = opts || {};
		var closeOnBackdrop = false !== opts.closeOnBackdrop;
		var closeOnEsc = false !== opts.closeOnEsc;
		var lastFocused = null;
		var isOpen = false;

		var card = ( opts.card ? overlayEl.querySelector( opts.card ) : null ) ||
			overlayEl.querySelector( '.sitestaffr-modal' ) ||
			overlayEl.firstElementChild;
		if ( card && ! card.hasAttribute( 'tabindex' ) ) {
			card.setAttribute( 'tabindex', '-1' );
		}

		function onKeydown( e ) {
			if ( ! isOpen ) {
				return;
			}
			if ( 'Escape' === e.key && closeOnEsc ) {
				e.preventDefault();
				controller.close();
				return;
			}
			if ( 'Tab' !== e.key ) {
				return;
			}
			var focusables = Array.prototype.filter.call(
				overlayEl.querySelectorAll( FOCUSABLE ),
				isVisible
			);
			if ( ! focusables.length ) {
				e.preventDefault();
				if ( card ) {
					card.focus();
				}
				return;
			}
			var first = focusables[ 0 ];
			var last = focusables[ focusables.length - 1 ];
			var inside = overlayEl.contains( document.activeElement );
			if ( e.shiftKey ) {
				if ( document.activeElement === first || ! inside ) {
					e.preventDefault();
					last.focus();
				}
			} else if ( document.activeElement === last || ! inside ) {
				e.preventDefault();
				first.focus();
			}
		}

		var controller = {
			el: overlayEl,
			open: function () {
				if ( isOpen ) {
					return;
				}
				isOpen = true;
				lastFocused = document.activeElement;
				// Fixed-position overlays center precisely only from <body>.
				if ( overlayEl.parentNode !== document.body ) {
					document.body.appendChild( overlayEl );
				}
				overlayEl.hidden = false;
				overlayEl.style.display = 'flex';
				overlayEl.setAttribute( 'aria-hidden', 'false' );
				lockScroll();
				document.addEventListener( 'keydown', onKeydown, true );
				var target = opts.initialFocus ? overlayEl.querySelector( opts.initialFocus ) : card;
				if ( target ) {
					target.focus();
				}
				if ( opts.onOpen ) {
					opts.onOpen( controller );
				}
			},
			close: function () {
				if ( ! isOpen ) {
					return;
				}
				isOpen = false;
				overlayEl.style.display = 'none';
				overlayEl.setAttribute( 'aria-hidden', 'true' );
				unlockScroll();
				document.removeEventListener( 'keydown', onKeydown, true );
				if ( lastFocused && document.contains( lastFocused ) ) {
					lastFocused.focus();
				}
				lastFocused = null;
				if ( opts.onClose ) {
					opts.onClose( controller );
				}
			},
			isOpen: function () {
				return isOpen;
			}
		};

		overlayEl.addEventListener( 'click', function ( e ) {
			if ( e.target === overlayEl && closeOnBackdrop ) {
				controller.close();
				return;
			}
			if ( e.target.closest( '.sitestaffr-modal-close, [data-sitestaffr-dismiss]' ) ) {
				e.preventDefault();
				controller.close();
			}
		} );

		return controller;
	}

	/**
	 * Build a small dialog card on demand. Used by confirm()/alertDialog().
	 * All text is inserted via textContent — safe for arbitrary strings.
	 */
	function buildDialog( title, message, buttons ) {
		var overlay = document.createElement( 'div' );
		overlay.className = 'sitestaffr-modal-overlay';
		overlay.style.display = 'none';
		overlay.setAttribute( 'aria-hidden', 'true' );

		var card = document.createElement( 'div' );
		card.className = 'sitestaffr-modal sitestaffr-modal--confirm';
		card.setAttribute( 'role', 'dialog' );
		card.setAttribute( 'aria-modal', 'true' );

		var header = document.createElement( 'div' );
		header.className = 'sitestaffr-modal-header';
		var h2 = document.createElement( 'h2' );
		h2.textContent = title;
		card.setAttribute( 'aria-label', title );
		header.appendChild( h2 );

		var body = document.createElement( 'div' );
		body.className = 'sitestaffr-modal-body';
		var p = document.createElement( 'p' );
		p.textContent = message;
		body.appendChild( p );

		var footer = document.createElement( 'div' );
		footer.className = 'sitestaffr-modal-footer';
		buttons.forEach( function ( b ) {
			footer.appendChild( b );
		} );

		card.appendChild( header );
		card.appendChild( body );
		card.appendChild( footer );
		overlay.appendChild( card );
		return overlay;
	}

	function makeButton( label, className ) {
		var btn = document.createElement( 'button' );
		btn.type = 'button';
		btn.className = className;
		btn.textContent = label;
		return btn;
	}

	/**
	 * Promise-based replacement for window.confirm().
	 * Resolves true (confirm) / false (cancel, Esc, backdrop, X).
	 */
	function confirmDialog( opts ) {
		opts = opts || {};
		return new Promise( function ( resolve ) {
			var confirmBtn = makeButton(
				opts.confirmLabel || l10n.confirm || 'Confirm',
				'button button-primary' + ( opts.danger ? ' sitestaffr-modal-danger' : '' )
			);
			var cancelBtn = makeButton( opts.cancelLabel || l10n.cancel || 'Cancel', 'button' );
			var overlay = buildDialog( opts.title || l10n.areYouSure || 'Are you sure?', opts.message || '', [ cancelBtn, confirmBtn ] );
			document.body.appendChild( overlay );

			var settled = false;
			var controller = enhance( overlay, {
				initialFocus: opts.danger ? '.sitestaffr-modal-footer .button:not(.button-primary)' : '.button-primary',
				onClose: function () {
					if ( ! settled ) {
						settled = true;
						resolve( false );
					}
					if ( overlay.parentNode ) {
						overlay.parentNode.removeChild( overlay );
					}
				}
			} );
			confirmBtn.addEventListener( 'click', function () {
				settled = true;
				resolve( true );
				controller.close();
			} );
			cancelBtn.addEventListener( 'click', function () {
				controller.close();
			} );
			controller.open();
		} );
	}

	/**
	 * Promise-based replacement for window.alert().
	 */
	function alertDialog( opts ) {
		opts = opts || {};
		return new Promise( function ( resolve ) {
			var okBtn = makeButton( opts.okLabel || l10n.ok || 'OK', 'button button-primary' );
			var overlay = buildDialog( opts.title || l10n.notice || 'Notice', opts.message || '', [ okBtn ] );
			document.body.appendChild( overlay );

			var controller = enhance( overlay, {
				initialFocus: '.button-primary',
				onClose: function () {
					resolve();
					if ( overlay.parentNode ) {
						overlay.parentNode.removeChild( overlay );
					}
				}
			} );
			okBtn.addEventListener( 'click', function () {
				controller.close();
			} );
			controller.open();
		} );
	}

	/**
	 * Declarative confirm for links/buttons: put the question in a
	 * data-sitestaffr-confirm attribute (add data-sitestaffr-confirm-danger
	 * for destructive styling) and the component intercepts the click,
	 * asks, and replays the original action on confirm.
	 */
	document.addEventListener( 'click', function ( e ) {
		var el = e.target.closest( '[data-sitestaffr-confirm]' );
		if ( ! el || '1' === el.getAttribute( 'data-sitestaffr-confirmed' ) ) {
			return;
		}
		e.preventDefault();
		e.stopImmediatePropagation();
		confirmDialog( {
			message: el.getAttribute( 'data-sitestaffr-confirm' ),
			danger: el.hasAttribute( 'data-sitestaffr-confirm-danger' )
		} ).then( function ( ok ) {
			if ( ! ok ) {
				return;
			}
			el.setAttribute( 'data-sitestaffr-confirmed', '1' );
			if ( 'A' === el.tagName && el.href ) {
				window.location.href = el.href;
			} else {
				el.click();
			}
			el.removeAttribute( 'data-sitestaffr-confirmed' );
		} );
	}, true );

	window.SiteStaffrModal = {
		enhance: enhance,
		confirm: confirmDialog,
		alertDialog: alertDialog
	};
} )();
