/*
 * Advanced Booking form mode.
 *
 * Refactored: module-style (DOM / State / Editor / Sync / Clipboard / Events / UI)
 *
 * @file ../includes/page-form-builder/advanced-mode/_out/bfb-advanced-form-editor.js
 */
(function (w, d) {
	'use strict';

	// True after wpbc:bfb:structure:loaded fired at least once.
	var structure_loaded_once = false;

	function adapted_has_any_field(adapted) {
		if ( ! adapted || ! Array.isArray( adapted.pages ) ) {
			return false;
		}

		var has = false;

		function walk_section(sec) {
			if ( has || ! sec ) { return; }
			var cols = Array.isArray( sec.columns ) ? sec.columns : [];
			for ( var i = 0; i < cols.length; i++ ) {
				var col = cols[i] || {};
				if ( Array.isArray( col.fields ) && col.fields.length ) { has = true; return; }
				var nested = Array.isArray( col.sections ) ? col.sections : [];
				for ( var j = 0; j < nested.length; j++ ) {
					walk_section( nested[j] );
					if ( has ) { return; }
				}
			}
		}

		for ( var p = 0; p < adapted.pages.length; p++ ) {
			var page  = adapted.pages[p] || {};
			var items = Array.isArray( page.items ) ? page.items : [];
			for ( var k = 0; k < items.length; k++ ) {
				var it = items[k] || {};
				if ( it.kind === 'field' ) { return true; }
				if ( it.kind === 'section' ) {
					walk_section( it.data );
					if ( has ) { return true; }
				}
			}
		}
		return has;
	}

	// ==  Constants  ==================================================================================================
	var IDS = {
		panels       : 'wpbc_bfb__advanced_mode_panels',
		ta_form      : 'wpbc_bfb__advanced_form_editor',
		ta_content   : 'wpbc_bfb__content_form_editor',
		btn_regen    : 'wpbc_bfb__advanced_regenerate_btn',
		btn_copy_form: 'wpbc_bfb__advanced_copy_form_btn',
		btn_copy_cnt : 'wpbc_bfb__advanced_copy_content_btn',
		cb_autosync  : 'wpbc_bfb__advanced_autosync',
		dirty_hint   : 'wpbc_bfb__advanced_dirty_hint'
	};

	var KEY = {
		FORM   : 'advanced_form',
		CONTENT: 'content_form'
	};

	var TA_ID_BY_KEY          = {};
	TA_ID_BY_KEY[KEY.FORM]    = IDS.ta_form;
	TA_ID_BY_KEY[KEY.CONTENT] = IDS.ta_content;

	// ==  DOM helpers  ================================================================================================
	var DOM = (function () {

		function get_by_id(id) {
			return d.getElementById( id );
		}

		function on(el, type, fn) {
			if ( el ) {
				el.addEventListener( type, fn );
			}
		}

		function is_advanced_ui_present() {
			return !! d.querySelector( '#' + IDS.panels );
		}

		return {
			get   : get_by_id,
			on    : on,
			has_ui: is_advanced_ui_present
		};
	})();

	// ==  State  ======================================================================================================
	var State = (function () {

		var state = {
			editors                : {},  // key -> wp.codeEditor instance (or null)
			autosync_user_value    : null, // null = not decided by user, boolean = user explicitly set
			sync_state_bootstrapped: false,
			is_dirty               : false,
			is_programmatic_update : false,
			is_inited              : false,
			textarea_fallback_bound: false
		};

		state.editors[KEY.FORM]    = null;
		state.editors[KEY.CONTENT] = null;

		function set_live_badges() {
			var cb      = DOM.get( IDS.cb_autosync );
			var sync_on = !! (cb && cb.checked);

			var live = (sync_on && ! state.is_dirty) ? 'builder' : 'advanced';

			var root = d.documentElement;
			if ( ! root ) {
				return;
			}

			root.setAttribute( 'data-wpbc-bfb-live-source', live === 'builder' ? 'builder' : 'advanced' );
			root.setAttribute( 'data-wpbc-bfb-sync-mode', sync_on ? 'on' : 'off' );
		}

		function set_dirty(on) {
			state.is_dirty = !! on;

			var hint = DOM.get( IDS.dirty_hint );
			if ( hint ) {
				hint.style.display = state.is_dirty ? 'inline' : 'none';
			}

			var autosync = DOM.get( IDS.cb_autosync );

			if ( state.is_dirty ) {
				// User edited Advanced => explicit opt-out of autosync for this session.
				state.autosync_user_value     = false;
				state.sync_state_bootstrapped = true;

				if ( autosync ) {
					autosync.checked = false;
				}
			} else {
				// IMPORTANT:
				// When clearing dirty state, do NOT force autosync checkbox ON.
				// Checkbox state is controlled by:
				// - user action (cb change)
				// - Sync.apply_autosync_state() when autosync_user_value === null
			}

			set_live_badges();
		}


		function is_autosync_on() {
			var cb = DOM.get( IDS.cb_autosync );
			return !! (cb && cb.checked && ! state.is_dirty);
		}

		return {
			raw           : state,
			set_dirty     : set_dirty,
			is_autosync_on: is_autosync_on,
			update_badges : set_live_badges
		};
	})();

	// ==  Editor (CodeMirror + textarea fallback + shortcode highlighting mode)  ======================================
	var Editor = (function () {

		var is_oshortcode_defined = false;

		function can_init_codemirror() {
			var wpns = w.wp || null;
			return !! (
				wpns &&
				wpns.codeEditor &&
				typeof wpns.codeEditor.initialize === 'function' &&
				w.wpbc_bfb_code_editor_settings
			);
		}

		function ensure_oshortcode_mode() {
			if ( is_oshortcode_defined ) {
				return;
			}

			var CM = w.wp && w.wp.CodeMirror ? w.wp.CodeMirror : null;
			if ( ! CM || typeof CM.defineMode !== 'function' ) {
				return;
			}

			CM.defineMode( 'oshortcode', function (config, parserConfig) {

				var overlay = {
					token: function (stream) {
						var ch;

						// [name ...] or [name* ...]
						if ( stream.match( /^\[([a-zA-Z0-9_]+)\*?\s?/ ) ) {
							while ( (ch = stream.next()) != null ) {
								if ( ch === ']' ) {
									return 'oshortcode';
								}
							}
						}

						while ( stream.next() != null && ! stream.match( /^\[([a-zA-Z0-9_]+)\*?\s?/, false ) ) {}
						return null;
					}
				};

				var base = CM.getMode( config, (parserConfig && parserConfig.backdrop) || 'htmlmixed' );

				// Fallback if overlay addon is missing.
				if ( typeof CM.overlayMode !== 'function' ) {
					return base;
				}

				return CM.overlayMode( base, overlay );
			} );

			is_oshortcode_defined = true;
		}

		function bind_textarea_dirty_fallback() {
			if ( State.raw.textarea_fallback_bound ) {
				return;
			}

			var ta_form = DOM.get( IDS.ta_form );
			var ta_cnt  = DOM.get( IDS.ta_content );
			if ( ! ta_form || ! ta_cnt ) {
				return;
			}

			function on_change() {
				if ( State.raw.is_programmatic_update ) {
					return;
				}
				State.set_dirty( true );
			}

			ta_form.addEventListener( 'input', on_change );
			ta_form.addEventListener( 'change', on_change );
			ta_cnt.addEventListener( 'input', on_change );
			ta_cnt.addEventListener( 'change', on_change );

			State.raw.textarea_fallback_bound = true;
		}

		function init_editor(textarea_el, key) {
			var wpns = w.wp || null;

			if ( ! textarea_el || ! wpns || ! wpns.codeEditor || typeof wpns.codeEditor.initialize !== 'function' ) {
				return null;
			}

			var base = w.wpbc_bfb_code_editor_settings || null;
			if ( ! base ) {
				return null;
			}

			ensure_oshortcode_mode();

			// Clone so we don't mutate localized shared settings object.
			var settings             = Object.assign( {}, base );
			settings.codemirror      = Object.assign( {}, base.codemirror || {} );
			settings.codemirror.mode = 'oshortcode';

			var inst = wpns.codeEditor.initialize( textarea_el, settings );

			if ( inst && inst.codemirror ) {
				inst.codemirror.on( 'change', function () {
					if ( State.raw.is_programmatic_update ) {
						return;
					}
					State.set_dirty( true );
				} );
			}

			State.raw.editors[key] = inst;
			return inst;
		}

		function ensure_inited() {
			if ( State.raw.is_inited ) {
				return true;
			}

			var ta_form = DOM.get( IDS.ta_form );
			var ta_cnt  = DOM.get( IDS.ta_content );
			if ( ! ta_form || ! ta_cnt ) {
				return false;
			}

			bind_textarea_dirty_fallback();

			if ( can_init_codemirror() ) {
				var i1 = init_editor( ta_form, KEY.FORM );
				var i2 = init_editor( ta_cnt, KEY.CONTENT );

				if ( ! i1 ) {
					State.raw.editors[KEY.FORM] = null;
				}
				if ( ! i2 ) {
					State.raw.editors[KEY.CONTENT] = null;
				}
			}

			State.raw.is_inited = true;
			return true;
		}

		function refresh_all() {
			var keys = Object.keys( State.raw.editors );
			for ( var i = 0; i < keys.length; i++ ) {
				var inst = State.raw.editors[keys[i]];
				try {
					if ( inst && inst.codemirror && typeof inst.codemirror.refresh === 'function' ) {
						inst.codemirror.refresh();
					}
				} catch ( e ) {}
			}
		}

		function textarea_id_for(key) {
			return TA_ID_BY_KEY[key] || '';
		}

		function get_value(key) {
			var inst = State.raw.editors[key];
			if ( inst && inst.codemirror && typeof inst.codemirror.getValue === 'function' ) {
				return String( inst.codemirror.getValue() || '' );
			}
			var ta = DOM.get( textarea_id_for( key ) );
			return ta ? String( ta.value || '' ) : '';
		}

		function set_value(key, value) {
			value = (value == null) ? '' : String( value );

			var ta = DOM.get( textarea_id_for( key ) );

			State.raw.is_programmatic_update = true;
			try {
				if ( ta ) {
					ta.value = value;
				}

				var inst = State.raw.editors[key];
				if ( inst && inst.codemirror && typeof inst.codemirror.setValue === 'function' ) {
					inst.codemirror.setValue( value );
					if ( typeof inst.codemirror.save === 'function' ) {
						inst.codemirror.save();
					}
				}
			} finally {
				State.raw.is_programmatic_update = false;
			}
		}

		function focus_and_select(key) {
			var ta = DOM.get( textarea_id_for( key ) );
			if ( ! ta ) {
				return;
			}
			try {
				ta.focus();
				ta.select();
			} catch ( e ) {}
		}

		function save_all_to_textareas() {
			var keys = Object.keys( State.raw.editors );
			for ( var i = 0; i < keys.length; i++ ) {
				var inst = State.raw.editors[keys[i]];
				try {
					if ( inst && inst.codemirror && typeof inst.codemirror.save === 'function' ) {
						inst.codemirror.save();
					}
				} catch ( e ) {}
			}
		}

		return {
			ensure_inited: ensure_inited,
			refresh_all  : refresh_all,
			get_value    : get_value,
			set_value    : set_value,
			focus_select : focus_and_select,
			save_all     : save_all_to_textareas
		};
	})();

	// ==  Builder export + Sync  ======================================================================================
	var Sync = (function () {

		var poll_timer_id     = null;
		var debounce_timer_id = null;

		function can_export_from_builder() {
			return !! (
				w.wpbc_bfb &&
				typeof w.wpbc_bfb.get_structure === 'function' &&
				w.WPBC_BFB_Exporter &&
				typeof w.WPBC_BFB_Exporter.export_all === 'function'
			);
		}

		function get_current_structure() {
			return (w.wpbc_bfb && typeof w.wpbc_bfb.get_structure === 'function') ? w.wpbc_bfb.get_structure() : [];
		}

		function export_all_from_builder() {
			if ( ! w.WPBC_BFB_Exporter || typeof w.WPBC_BFB_Exporter.export_all !== 'function' ) {
				return null;
			}
			try {
				return w.WPBC_BFB_Exporter.export_all( get_current_structure(), { gapPercent: 3 } );
			} catch ( e ) {
				return null;
			}
		}

		function export_output_looks_ready(out) {
			if ( ! out ) {
				return false;
			}

			var af = (out.advanced_form == null) ? '' : String( out.advanced_form );
			var cf = (out.fields_data == null) ? '' : String( out.fields_data );

			af = af.trim();
			cf = cf.trim();

			if ( ! af && ! cf ) {
				return false;
			}

			// If structure is loaded and there are NO fields, accept empty export.
			if ( structure_loaded_once && ! adapted_has_any_field( out.adapted ) ) {
				return true;
			}

			// Real BFB export includes layout tags (<r>, <c>, <item>).
			var has_layout_tags = /<\s*(r|c|item)\b/i.test( af ) || /<\s*(r|c|item)\b/i.test( cf );
			var has_any_item    = /<\s*item\b/i.test( af ) || /<\s*item\b/i.test( cf );

			return has_layout_tags && has_any_item;
		}

		function get_builder_export_if_ready() {
			if ( ! can_export_from_builder() ) {
				return null;
			}

			var out = export_all_from_builder();
			if ( ! out ) {
				return null;
			}

			if ( ! export_output_looks_ready( out ) ) {
				return null;
			}

			return out;
		}

		// --- Normalization for compare -------------------------------------------------------
		function normalize_style_value(css) {
			css = (css == null) ? '' : String( css );

			css = css.replace( /\r\n/g, '\n' ).replace( /\r/g, '\n' );
			css = css.replace( /\t/g, ' ' );
			css = css.replace( /\s+/g, ' ' );

			css = css.replace( /\s*;\s*/g, ';' );
			css = css.replace( /\s*:\s*/g, ':' );

			css = css.trim().replace( /;+\s*$/g, '' );
			if ( css !== '' ) {
				css += ';';
			}

			return css;
		}

		function normalize_inline_styles_in_markup(html) {
			html = (html == null) ? '' : String( html );

			return html.replace( /\bstyle=(["'])(.*?)\1/gi, function (_m, quote, css) {
				return 'style=' + quote + normalize_style_value( css ) + quote;
			} );
		}

		function normalize_text(s) {
			s = (s == null) ? '' : String( s );

			s = s.replace( /\r\n/g, '\n' ).replace( /\r/g, '\n' );
			s = normalize_inline_styles_in_markup( s );

			s = s.replace( /[ \t]+$/gm, '' );
			s = s.replace( /[ ]{2,}/g, ' ' );
			s = s.replace( /^\s+/gm, '' );
			s = s.replace( /\n{3,}/g, '\n\n' );

			return s.trim();
		}

		function get_current_advanced_texts() {
			return {
				advanced_form: Editor.get_value( KEY.FORM ),
				content_form : Editor.get_value( KEY.CONTENT )
			};
		}

		function detect_sync_state_with_export() {
			var out = get_builder_export_if_ready();
			if ( ! out ) {
				return null;
			}

			var cur = get_current_advanced_texts();

			var a1 = normalize_text( cur.advanced_form );
			var a2 = normalize_text( cur.content_form );

			var b1 = normalize_text( out.advanced_form || '' );
			var b2 = normalize_text( out.fields_data || '' );

			return {
				is_synced: (a1 === b1) && (a2 === b2),
				out      : out
			};
		}

		function apply_autosync_from_sync_state(is_synced) {
			var cb = DOM.get( IDS.cb_autosync );
			if ( ! cb ) {
				return;
			}

			if ( State.raw.is_dirty ) {
				State.raw.autosync_user_value     = false;
				State.raw.sync_state_bootstrapped = true;
				cb.checked                        = false;
				State.update_badges();
				return;
			}

			// Only auto-set checkbox if user never explicitly touched it.
			if ( State.raw.autosync_user_value === null ) {
				cb.checked = !! is_synced;
			}

			State.raw.sync_state_bootstrapped = true;
			State.update_badges();
		}

		function regenerate_from_builder(out_opt) {
			var out = out_opt || get_builder_export_if_ready();
			if ( ! out ) {
				return false;
			}

			Editor.set_value( KEY.FORM, out.advanced_form || '' );
			Editor.set_value( KEY.CONTENT, out.fields_data || '' );

			State.set_dirty( false );
			State.update_badges();

			return true;
		}

		function sync_detect_and_apply() {
			var res = detect_sync_state_with_export();
			if ( res === null ) {
				return false;
			}

			var cb      = DOM.get( IDS.cb_autosync );
			var sync_on = !! (cb && cb.checked);

			State.raw.sync_state_bootstrapped = true;

			if ( State.raw.is_dirty ) {
				apply_autosync_from_sync_state( false );
				return true;
			}

			if ( sync_on ) {
				if ( ! res.is_synced ) {
					regenerate_from_builder( res.out );
				} else {
					State.update_badges();
				}
				return true;
			}

			apply_autosync_from_sync_state( res.is_synced );
			return true;
		}

		// --- Poll / debounce -------------------------------------------------------
		function schedule_sync_detect(reason) {

			if ( poll_timer_id ) {
				clearTimeout( poll_timer_id );
				poll_timer_id = null;
			}

			var started_ms   = Date.now();
			var max_total_ms = 12000;
			var delay_ms     = 150;

			(function tick() {

				if ( ! DOM.has_ui() ) {
					poll_timer_id = null;
					return;
				}

				Editor.ensure_inited();

				if ( State.raw.is_dirty ) {
					apply_autosync_from_sync_state( false );
					poll_timer_id = null;
					return;
				}

				if ( sync_detect_and_apply() ) {
					poll_timer_id = null;
					return;
				}

				if ( (Date.now() - started_ms) < max_total_ms ) {
					delay_ms      = Math.min( 600, Math.floor( delay_ms * 1.25 ) );
					poll_timer_id = setTimeout( tick, delay_ms );
					return;
				}

				poll_timer_id = null;
			})();
		}

		function schedule_sync_detect_debounced(reason) {
			if ( debounce_timer_id ) {
				clearTimeout( debounce_timer_id );
			}
			debounce_timer_id = setTimeout( function () {
				debounce_timer_id = null;
				schedule_sync_detect( reason );
			}, 250 );
		}

		return {
			schedule_detect          : schedule_sync_detect,
			schedule_detect_debounced: schedule_sync_detect_debounced,
			regenerate               : regenerate_from_builder,
			apply_autosync_state     : apply_autosync_from_sync_state
		};
	})();

	// ==  Clipboard  ==================================================================================================
	var Clipboard = (function () {

		async function copy_text(text) {
			text = (text == null) ? '' : String( text );

			if ( typeof w.wpbc_copy_to_clipboard === 'function' ) {
				try { return await w.wpbc_copy_to_clipboard( text ); } catch ( e ) {}
			}

			try {
				if ( w.isSecureContext && navigator.clipboard && navigator.clipboard.writeText ) {
					await navigator.clipboard.writeText( text );
					return true;
				}
			} catch ( e ) {}

			try {
				var ta   = d.createElement( 'textarea' );
				ta.value = text;
				ta.setAttribute( 'readonly', '' );
				ta.style.position = 'fixed';
				ta.style.top      = '-9999px';
				ta.style.opacity  = '0';
				d.body.appendChild( ta );
				ta.focus();
				ta.select();
				var ok = d.execCommand( 'copy' );
				d.body.removeChild( ta );
				return !! ok;
			} catch ( e ) {
				return false;
			}
		}

		function feedback_button(btn, ok) {
			if ( ! btn ) {
				return;
			}

			var original = btn.getAttribute( 'data-wpbc-original-text' ) || btn.textContent;
			btn.setAttribute( 'data-wpbc-original-text', original );

			btn.textContent = ok ? 'Copied!' : 'Press Ctrl/Cmd+C to copy';
			setTimeout( function () {
				btn.textContent = original;
			}, 1500 );
		}

		async function copy_editor_value(key, btn) {
			Editor.ensure_inited();

			var ok = await copy_text( Editor.get_value( key ) );
			if ( ! ok ) {
				Editor.focus_select( key );
			}

			feedback_button( btn, ok );
		}

		return {
			copy_editor_value: copy_editor_value
		};
	})();

	// ==  UI bindings
	var UI = (function () {

		function bind_ui() {

			DOM.on( DOM.get( IDS.btn_regen ), 'click', function (e) {
				e.preventDefault();
				Editor.ensure_inited();
				Sync.regenerate();
				Editor.refresh_all();

				// Safe default until we can confirm sync state.
				var cb = DOM.get( IDS.cb_autosync );
				if ( cb ) {
					cb.checked = true;
					State.update_badges();
				}
			} );

			DOM.on( DOM.get( IDS.cb_autosync ), 'change', function () {

				var cb = DOM.get( IDS.cb_autosync );
				if ( ! cb ) {
					return;
				}

				State.raw.autosync_user_value     = !! cb.checked; // explicit user choice
				State.raw.sync_state_bootstrapped = true;

				if ( cb.checked ) {
					State.set_dirty( false );
					Editor.ensure_inited();

					if ( ! Sync.regenerate() ) {
						Sync.schedule_detect( 'autosync_user_on_wait_ready' );
					}

					Editor.refresh_all();
				}

				State.update_badges();
			} );

			DOM.on( DOM.get( IDS.btn_copy_form ), 'click', function (e) {
				e.preventDefault();
				Clipboard.copy_editor_value( KEY.FORM, DOM.get( IDS.btn_copy_form ) );
			} );

			DOM.on( DOM.get( IDS.btn_copy_cnt ), 'click', function (e) {
				e.preventDefault();
				Clipboard.copy_editor_value( KEY.CONTENT, DOM.get( IDS.btn_copy_cnt ) );
			} );
		}

		return {
			bind: bind_ui
		};
	})();

	// ==  WP / Builder events  ========================================================================================
	var Events = (function () {

		function hook_events() {

			/**
			 * Infer apply source without relying on any "advanced_source" key.
			 *
			 * Returns:
			 * - 'builder'  : if texts match current Builder export (when export is ready)
			 * - 'advanced' : if texts do not match Builder export (but have any content)
			 * - 'auto'     : if Builder export is not available/ready (safe default)
			 *
			 * @param {string} af
			 * @param {string} cf
			 * @return {'builder'|'advanced'|'auto'}
			 */
			function infer_apply_source(af, cf) {

				af = (af == null) ? '' : String(af);
				cf = (cf == null) ? '' : String(cf);

				function has_text(v) {
					return !! (v && String(v).trim());
				}

				function normalize_style_value(css) {
					css = (css == null) ? '' : String( css );

					css = css.replace( /\r\n/g, '\n' ).replace( /\r/g, '\n' );
					css = css.replace( /\t/g, ' ' );
					css = css.replace( /\s+/g, ' ' );

					// Normalize spacing around separators.
					css = css.replace( /\s*;\s*/g, ';' );
					css = css.replace( /\s*:\s*/g, ':' );

					// Ensure stable trailing semicolon (important for compare).
					css = css.trim().replace( /;+\s*$/g, '' );
					if ( css !== '' ) {
						css += ';';
					}
					return css;
				}

				function normalize_inline_styles_in_markup(html) {
					html = (html == null) ? '' : String( html );

					// Handles: style="..." and style='...' (with optional spaces around '=')
					return html.replace( /\bstyle\s*=\s*(["'])(.*?)\1/gi, function (_m, quote, css) {
						return 'style=' + quote + normalize_style_value( css ) + quote;
					} );
				}

				function normalize_text(s) {
					s = (s == null) ? '' : String(s);
					s = s.replace(/\r\n/g, '\n').replace(/\r/g, '\n');

					// IMPORTANT: normalize inline styles so "flex:1" == "flex:1;"
					s = normalize_inline_styles_in_markup(s);

					s = s.replace(/[ \t]+$/gm, '');
					s = s.replace(/[ ]{2,}/g, ' ');
					s = s.replace(/^\s+/gm, '');
					s = s.replace(/\n{3,}/g, '\n\n');
					return s.trim();
				}

				function export_output_looks_ready(out) {
					if (!out) return false;
					var a = normalize_text(out.advanced_form || '');
					var b = normalize_text(out.fields_data || '');
					if (!a && !b) return false;
					// If structure is loaded and there are NO fields, accept empty export.
					if ( structure_loaded_once && ! adapted_has_any_field( out.adapted ) ) {
						return true;
					}
					// Expect layout tags to exist in real export.
					var has_layout_tags = /<\s*(r|c|item)\b/i.test(a) || /<\s*(r|c|item)\b/i.test(b);
					var has_any_item    = /<\s*item\b/i.test(a) || /<\s*item\b/i.test(b);
					return has_layout_tags && has_any_item;
				}

				function try_get_builder_export() {
					try {
						if (
							!w.wpbc_bfb ||
							typeof w.wpbc_bfb.get_structure !== 'function' ||
							!w.WPBC_BFB_Exporter ||
							typeof w.WPBC_BFB_Exporter.export_all !== 'function'
						) {
							return null;
						}
						var out = w.WPBC_BFB_Exporter.export_all(w.wpbc_bfb.get_structure(), { gapPercent: 3 });
						if (!export_output_looks_ready(out)) {
							return null;
						}
						return out;
					} catch (_e) {
						return null;
					}
				}

				var out = try_get_builder_export();
				if (!out) {
					return 'auto';
				}

				var in_af = normalize_text(af);
				var in_cf = normalize_text(cf);
				var ex_af = normalize_text(out.advanced_form || '');
				var ex_cf = normalize_text(out.fields_data || '');

				if (in_af === ex_af && in_cf === ex_cf) {
					return 'builder';
				}

				if (has_text(af) || has_text(cf)) {
					return 'advanced';
				}

				return 'auto';
			}


			d.addEventListener( 'wpbc:bfb:structure:change', function () {
				if ( ! Editor.ensure_inited() ) {
					return;
				}

				if ( State.raw.is_dirty ) {
					return;
				}

				if ( State.raw.autosync_user_value === false ) {
					return;
				}

				var cb = DOM.get( IDS.cb_autosync );
				if ( ! cb ) {
					return;
				}

				if ( ! State.raw.sync_state_bootstrapped ) {
					Sync.schedule_detect_debounced( 'structure_change_pre_bootstrap' );
					return;
				}

				Sync.schedule_detect_debounced( 'structure_change' );
			} );

			d.addEventListener( 'wpbc:bfb:structure:loaded', function () {
				structure_loaded_once = true;
				Sync.schedule_detect( 'structure_loaded' );
			} );

			d.addEventListener( 'wpbc:bfb:top-tab', function (ev) {

				var tab_id   = (ev && ev.detail && ev.detail.tab) ? String( ev.detail.tab ) : '';
				var is_inner = (tab_id === 'advanced_mode__booking_form' || tab_id === 'advanced_mode__booking_data');

				// Entering Advanced Mode (root tab) or switching inner tabs.
				if ( tab_id !== 'advanced_tab' && ! is_inner ) {
					return;
				}

				if ( ! Editor.ensure_inited() ) {
					return;
				}

				if ( State.is_autosync_on() ) {
					if ( ! Sync.regenerate() ) {
						Sync.schedule_detect( 'top_tab_wait_ready' );
					}
				}


				setTimeout( Editor.refresh_all, 60 );
				if ( tab_id === 'advanced_mode__booking_data' ) {
					setTimeout( Editor.refresh_all, 120 );
				}
			} );

			d.addEventListener( 'wpbc:bfb:advanced_text:apply', function (ev) {

				var det = (ev && ev.detail) ? ev.detail : {};
				var af  = (det.advanced_form == null) ? '' : String( det.advanced_form );
				var cf  = (det.content_form == null) ? '' : String( det.content_form );

				var src = String( det.advanced_mode_source || 'auto' ).toLowerCase(); // builder|advanced|auto.
				if ( src !== 'builder' && src !== 'advanced' && src !== 'auto' ) {
					src = 'auto';
				}
				if ( src === 'auto' ) {
					src = infer_apply_source( af, cf ); // builder|advanced|auto.
				}


				var cb = DOM.get( IDS.cb_autosync );

				// Always use Editor API (keeps textarea + CodeMirror in sync).
				Editor.ensure_inited();
				Editor.set_value( KEY.FORM, af );
				Editor.set_value( KEY.CONTENT, cf );
				setTimeout( Editor.refresh_all, 60 );

				if ( src === 'advanced' ) {
					// Advanced is authoritative => dirty + autosync OFF.
					if ( cb ) { cb.checked = false; }
					State.set_dirty( true );                 // also sets autosync_user_value=false and bootstrapped=true .

				} else if ( src === 'builder' ) {
					// Builder is authoritative => autosync ON.
					if ( cb ) { cb.checked = true; }
					State.raw.autosync_user_value     = true;
					State.raw.sync_state_bootstrapped = true;
					State.set_dirty( false );

				} else {
					// auto/unknown:  Safe default = autosync OFF until we confirm real sync state.  This prevents Preview using Builder before export is ready / comparison is done.
					if ( cb ) { cb.checked = false; }
					State.raw.autosync_user_value     = null;
					State.raw.sync_state_bootstrapped = false;
					State.set_dirty( false );
				}

				State.update_badges();
				Sync.schedule_detect( 'advanced_text_apply' );
			} );


		}

		return {
			hook: hook_events
		};
	})();

	// == Public API (unchanged)  ======================================================================================
	w.wpbc_bfb_advanced_editor_api = w.wpbc_bfb_advanced_editor_api || {};

	w.wpbc_bfb_advanced_editor_api.get_values = function () {

		// Ensure CodeMirror is ready (if enabled).
		Editor.ensure_inited();

		// Push CodeMirror -> textarea (no-op if not inited).
		Editor.save_all();

		// In your system, "manual mode" (autosync OFF) must be treated as "use Advanced".
		var use_advanced = !! State.raw.is_dirty || ! State.is_autosync_on();

		return {
			advanced_form: Editor.get_value( KEY.FORM ),
			content_form : Editor.get_value( KEY.CONTENT ),
			is_dirty     : use_advanced
		};
	};


	w.wpbc_bfb_advanced_editor_api.set_dirty = function (state) {
		State.set_dirty( !! state );
	};

	// ==  Boot  =======================================================================================================
	d.addEventListener( 'DOMContentLoaded', function () {

		UI.bind();
		Events.hook();

		if ( DOM.has_ui() ) {
			Editor.ensure_inited();
			setTimeout( Editor.refresh_all, 60 );

			// Safe default until we can confirm sync state.
			var cb = DOM.get( IDS.cb_autosync );
			if ( cb && State.raw.autosync_user_value === null && ! State.raw.sync_state_bootstrapped ) {
				cb.checked = false;
				State.update_badges();
			}
		}

		// Initial autosync checkbox state (based on real sync status).
		Sync.schedule_detect( 'boot' );
	} );

})( window, document );
