/**
 * SiteStaffr — Shared save-UX helpers.
 *
 * One implementation of the two behaviors every settings surface needs:
 *
 * 1. Inline save status — SiteStaffrSaveUX.status( el ) returns
 *    { saving(), saved(msg), error(msg), clear() }. States are CSS classes
 *    on `.sitestaffr-inline-status` (is-saving / is-saved / is-error), never
 *    inline colors; the element gets role="status" for screen readers and
 *    "saved" auto-clears after a moment.
 *
 * 2. Dirty-state tracking — SiteStaffrSaveUX.trackDirty( containerEl )
 *    marks the section dirty on any input/change inside it and arms ONE
 *    shared beforeunload guard while anything tracked is dirty. Call
 *    markClean() after a successful save.
 *
 * Instant-save controls should use status() only — they never hold
 * unsaved state, so they are not tracked.
 *
 * @package    SiteStaffr
 * @subpackage SiteStaffr/admin/js
 * @since      1.33.8
 */
( function () {
	'use strict';

	var l10n = window.sitestaffrSaveUxL10n || {};
	var SAVED_CLEAR_MS = 2000;
	var dirtySections = [];
	var unloadArmed = false;

	function anyDirty() {
		return dirtySections.some( function ( s ) {
			return s.dirty;
		} );
	}

	function onBeforeUnload( e ) {
		if ( ! anyDirty() ) {
			return undefined;
		}
		e.preventDefault();
		// Chrome requires returnValue to be set; the browser shows its own text.
		e.returnValue = '';
		return '';
	}

	function armUnloadGuard() {
		if ( ! unloadArmed ) {
			unloadArmed = true;
			window.addEventListener( 'beforeunload', onBeforeUnload );
		}
	}

	/**
	 * Wrap an inline-status element. Accepts an element or a selector.
	 */
	function status( target ) {
		var el = 'string' === typeof target ? document.querySelector( target ) : target;
		if ( el && el.jquery ) {
			el = el[ 0 ];
		}

		function set( stateClass, text ) {
			if ( ! el ) {
				return;
			}
			// Timer lives on the element, not the wrapper, so two status()
			// wrappers for the same element can't clear each other's state.
			if ( el._sitestaffrStatusTimer ) {
				clearTimeout( el._sitestaffrStatusTimer );
				el._sitestaffrStatusTimer = null;
			}
			el.classList.add( 'sitestaffr-inline-status' );
			if ( ! el.getAttribute( 'role' ) ) {
				el.setAttribute( 'role', 'status' );
			}
			el.classList.remove( 'is-saving', 'is-saved', 'is-error' );
			if ( stateClass ) {
				el.classList.add( stateClass );
			}
			el.textContent = text || '';
		}

		var api = {
			saving: function ( msg ) {
				set( 'is-saving', msg || l10n.saving || 'Saving…' );
				return api;
			},
			saved: function ( msg ) {
				set( 'is-saved', msg || l10n.saved || 'Saved.' );
				if ( el ) {
					el._sitestaffrStatusTimer = setTimeout( function () {
						set( '', '' );
					}, SAVED_CLEAR_MS );
				}
				return api;
			},
			error: function ( msg ) {
				set( 'is-error', msg || l10n.error || 'Save failed.' );
				return api;
			},
			clear: function () {
				set( '', '' );
				return api;
			}
		};
		return api;
	}

	/**
	 * Track unsaved changes inside a container. Any input/change marks the
	 * section dirty; call markClean() after a successful save.
	 */
	function trackDirty( containerEl ) {
		var el = 'string' === typeof containerEl ? document.querySelector( containerEl ) : containerEl;
		if ( el && el.jquery ) {
			el = el[ 0 ];
		}
		var section = { dirty: false };
		if ( ! el ) {
			// Missing container (markup variant) — return an inert tracker so
			// callers don't need to guard.
			return {
				markDirty: function () {},
				markClean: function () {},
				isDirty: function () {
					return false;
				}
			};
		}

		function markDirty() {
			section.dirty = true;
		}
		// Bind through jQuery when present so programmatic .trigger()'d
		// events (e.g. color pickers) mark dirty too, not just native ones.
		if ( window.jQuery ) {
			window.jQuery( el ).on( 'input change', markDirty );
		} else {
			el.addEventListener( 'input', markDirty );
			el.addEventListener( 'change', markDirty );
		}
		dirtySections.push( section );
		armUnloadGuard();

		return {
			// For value writes that fire no event at all (e.g. iris color
			// commits via .val()), callers mark the section dirty directly.
			markDirty: markDirty,
			markClean: function () {
				section.dirty = false;
			},
			isDirty: function () {
				return section.dirty;
			}
		};
	}

	window.SiteStaffrSaveUX = {
		status: status,
		trackDirty: trackDirty
	};
} )();
