/**
 * Kramar -- Checkout Field Builder
 *
 * 3-panel visual builder: field-type palette (left), checkout preview canvas
 * (center), field settings panel (right).  Vanilla JS, no jQuery.
 *
 * Depends on the `kramarFieldBuilder` object localised by
 * Field_Editor::enqueue_builder_assets():
 *   ajaxUrl, nonce, sections { billing, shipping, additional },
 *   fieldTypes { slug: label, ... }, i18n { ... }
 *
 * @package Kramar
 * @since   1.0.0
 */

( function () {
	'use strict';

	var FIELD_ICONS = {
		text:      'dashicons-editor-textcolor',
		textarea:  'dashicons-editor-paragraph',
		select:    'dashicons-arrow-down-alt2',
		radio:     'dashicons-marker',
		checkbox:  'dashicons-yes-alt',
		number:    'dashicons-editor-ol',
		email:     'dashicons-email',
		tel:       'dashicons-phone',
		url:       'dashicons-admin-links',
		date:      'dashicons-calendar-alt',
		time:      'dashicons-clock',
		hidden:    'dashicons-hidden',
		heading:   'dashicons-heading',
		paragraph: 'dashicons-editor-alignleft',
	};

	var state = {
		sections:        {},
		selectedSection: 'billing',
		selectedKey:     null,
		dirty:           false,
		newFieldCounter: 1,
	};

	var el = {};

	var SECTION_IDS = [ 'billing', 'shipping', 'additional' ];

	/* =========================================================================
	   Initialise
	   ====================================================================== */

	function init() {
		el = {
			palette:    document.getElementById( 'kramar-fb-palette' ),
			canvas:     document.getElementById( 'kramar-fb-canvas' ),
			settings:   document.getElementById( 'kramar-fb-settings' ),
			saveBtn:    document.getElementById( 'kramar-fb-save' ),
			saveStat:   document.getElementById( 'kramar-fb-save-status' ),
			fieldCount: document.getElementById( 'kramar-fb-field-count' ),
		};

		if ( ! el.palette || ! el.canvas || ! el.settings ) {
			return;
		}

		state.sections = JSON.parse( JSON.stringify( kramarFieldBuilder.sections ) );

		render();
		initCanvasEvents();
		initDragDrop();
		bindGlobalEvents();
	}

	/* =========================================================================
	   Top-level render dispatcher
	   ====================================================================== */

	function render() {
		renderPalette();
		renderCanvas();
		renderSettings();
		updateFieldCount();
	}

	/* =========================================================================
	   Left panel — Field type palette
	   ====================================================================== */

	function renderPalette() {
		var types    = kramarFieldBuilder.fieldTypes;
		var standard = [ 'text', 'textarea', 'select', 'radio', 'checkbox', 'number', 'email', 'tel', 'url', 'date', 'time', 'hidden' ];
		var display  = [ 'heading', 'paragraph' ];

		var html = '<div class="kramar-fb-palette__header">' +
			'<h3>Field Types</h3>' +
			'<input type="search" class="kramar-fb-palette__search" id="kramar-fb-palette-search" placeholder="Search fields...">' +
			'</div>';

		html += '<div class="kramar-fb-palette__body">';

		html += '<div class="kramar-fb-palette-group">';
		html += '<p class="kramar-fb-palette-group__label">Standard</p>';
		standard.forEach( function ( slug ) {
			if ( types[ slug ] ) {
				html += paletteItemHtml( slug, types[ slug ] );
			}
		} );
		html += '</div>';

		html += '<div class="kramar-fb-palette-group">';
		html += '<p class="kramar-fb-palette-group__label">Display</p>';
		display.forEach( function ( slug ) {
			if ( types[ slug ] ) {
				html += paletteItemHtml( slug, types[ slug ] );
			}
		} );
		html += '</div>';

		html += '</div>';

		el.palette.innerHTML = html;

		el.palette.querySelectorAll( '.kramar-fb-palette-item' ).forEach( function ( btn ) {
			btn.addEventListener( 'click', function () {
				handleAddField( btn.dataset.type );
			} );

			btn.setAttribute( 'draggable', 'true' );
			btn.addEventListener( 'dragstart', function ( e ) {
				e.dataTransfer.setData( 'kramar/new-field', btn.dataset.type );
				e.dataTransfer.effectAllowed = 'copy';
				el.canvas.classList.add( 'kramar-fb-canvas--drop-target' );
			} );
			btn.addEventListener( 'dragend', function () {
				el.canvas.classList.remove( 'kramar-fb-canvas--drop-target' );
			} );
		} );

		bindPaletteSearch();
		initCanvasDrop();
	}

	function paletteItemHtml( slug, label ) {
		var icon = FIELD_ICONS[ slug ] || 'dashicons-admin-generic';
		return '<button type="button" class="kramar-fb-palette-item" data-type="' + esc( slug ) + '">' +
			'<span class="kramar-fb-palette-icon dashicons ' + esc( icon ) + '"></span>' +
			'<span class="kramar-fb-palette-label">' + esc( label ) + '</span>' +
			'</button>';
	}

	function bindPaletteSearch() {
		var search = document.getElementById( 'kramar-fb-palette-search' );
		if ( ! search ) { return; }

		search.addEventListener( 'input', function () {
			var q      = search.value.toLowerCase().trim();
			var items  = el.palette.querySelectorAll( '.kramar-fb-palette-item' );
			var groups = el.palette.querySelectorAll( '.kramar-fb-palette-group' );

			items.forEach( function ( item ) {
				var label = item.querySelector( '.kramar-fb-palette-label' );
				var text  = label ? label.textContent.toLowerCase() : '';
				var type  = ( item.dataset.type || '' ).toLowerCase();
				var match = ! q || text.indexOf( q ) !== -1 || type.indexOf( q ) !== -1;
				item.classList.toggle( 'kramar-fb-palette-item--hidden', ! match );
			} );

			groups.forEach( function ( group ) {
				var visible = group.querySelectorAll( '.kramar-fb-palette-item:not(.kramar-fb-palette-item--hidden)' );
				var label   = group.querySelector( '.kramar-fb-palette-group__label' );
				if ( label ) {
					label.style.display = visible.length ? '' : 'none';
				}
			} );
		} );
	}

	function initCanvasDrop() {
		el.canvas.addEventListener( 'dragover', function ( e ) {
			if ( ! e.dataTransfer.types.includes( 'kramar/new-field' ) ) { return; }
			e.preventDefault();
			e.dataTransfer.dropEffect = 'copy';
			var target = document.getElementById( 'kramar-fb-form-preview' ) || el.canvas.querySelector( '.kramar-fb-canvas__empty' );
			if ( target ) { target.classList.add( 'is-drag-over' ); }
		} );

		el.canvas.addEventListener( 'dragleave', function ( e ) {
			if ( e.target === el.canvas || ! el.canvas.contains( e.relatedTarget ) ) {
				var target = document.getElementById( 'kramar-fb-form-preview' ) || el.canvas.querySelector( '.kramar-fb-canvas__empty' );
				if ( target ) { target.classList.remove( 'is-drag-over' ); }
			}
		} );

		el.canvas.addEventListener( 'drop', function ( e ) {
			var type = e.dataTransfer.getData( 'kramar/new-field' );
			if ( ! type ) { return; }
			e.preventDefault();

			el.canvas.classList.remove( 'kramar-fb-canvas--drop-target' );
			var target = document.getElementById( 'kramar-fb-form-preview' ) || el.canvas.querySelector( '.kramar-fb-canvas__empty' );
			if ( target ) { target.classList.remove( 'is-drag-over' ); }

			handleAddField( type );
		} );
	}

	/* =========================================================================
	   Center panel — Canvas
	   ====================================================================== */

	function renderCanvas() {
		var i18n = kramarFieldBuilder.i18n;

		var tabsHtml = '<div class="kramar-fb-canvas-tabs">';
		SECTION_IDS.forEach( function ( sectionId ) {
			var label   = i18n[ sectionId ] || sectionId;
			var fields  = state.sections[ sectionId ] || [];
			var isActive = state.selectedSection === sectionId;
			tabsHtml += '<button type="button" class="kramar-fb-canvas-tab' +
				( isActive ? ' kramar-fb-canvas-tab--active' : '' ) + '" data-section="' + esc( sectionId ) + '">' +
				esc( label ) +
				'<span class="kramar-fb-canvas-tab__count">' + fields.length + '</span>' +
				'</button>';
		} );
		tabsHtml += '</div>';

		var sectionId = state.selectedSection;
		var fields    = state.sections[ sectionId ] || [];
		var sorted    = fields.slice().sort( function ( a, b ) {
			return ( ( a.priority || 10 ) - ( b.priority || 10 ) );
		} );

		var bodyHtml;
		if ( sorted.length === 0 ) {
			bodyHtml = '<div class="kramar-fb-canvas__empty">' +
				'<span class="dashicons dashicons-forms"></span>' +
				'<p>No fields yet. Drag a field type from the palette or click to add.</p>' +
				'</div>';
		} else {
			bodyHtml = '<div class="kramar-fb-form-preview" id="kramar-fb-form-preview">';
			sorted.forEach( function ( field ) {
				bodyHtml += renderFieldPreview( field, sectionId );
			} );
			bodyHtml += '</div>';
		}

		el.canvas.innerHTML = tabsHtml +
			'<div class="kramar-fb-canvas-body">' + bodyHtml + '</div>';

		updateFieldCount();
	}

	function renderFieldPreview( field, sectionId ) {
		var isSelected = state.selectedSection === sectionId && state.selectedKey === field.key;
		var isDisabled = ! field.enabled;
		var isCustom   = !! field.custom;
		var typeLabel  = kramarFieldBuilder.fieldTypes[ field.type ] || field.type;

		var widthClass = 'kramar-fb-form-field--wide';
		if ( Array.isArray( field.class ) ) {
			if ( field.class.indexOf( 'form-row-first' ) !== -1 ) {
				widthClass = 'kramar-fb-form-field--first';
			} else if ( field.class.indexOf( 'form-row-last' ) !== -1 ) {
				widthClass = 'kramar-fb-form-field--last';
			}
		}

		var classes = 'kramar-fb-form-field ' + widthClass;
		if ( isSelected ) { classes += ' kramar-fb-form-field--selected'; }
		if ( isDisabled ) { classes += ' kramar-fb-form-field--disabled'; }

		var deleteHtml = isCustom
			? '<span class="kramar-fb-form-field__delete dashicons dashicons-trash" title="Delete"></span>'
			: '';

		var actionsHtml = '<div class="kramar-fb-form-field__actions">' +
			'<span class="kramar-fb-form-field__handle dashicons dashicons-move" title="Drag"></span>' +
			deleteHtml +
			'</div>';

		var isDisplayType = field.type === 'heading' || field.type === 'paragraph';

		var labelRowHtml = '';
		if ( ! isDisplayType ) {
			var requiredHtml = field.required
				? '<span class="kramar-fb-form-field__required">*</span>'
				: '';
			var customHtml = isCustom
				? ' <span class="kramar-fb-form-field__custom">Custom</span>'
				: '';

			labelRowHtml = '<div class="kramar-fb-form-field__label-row">' +
				'<span class="kramar-fb-form-field__label-text">' + esc( field.label || field.key ) + '</span>' +
				requiredHtml +
				customHtml +
				'</div>';
		}

		var previewHtml = '<div class="kramar-fb-form-field__preview">' + fieldPreviewElement( field ) + '</div>';

		return '<div class="' + classes + '" data-key="' + esc( field.key ) + '" data-section="' + esc( sectionId ) + '" draggable="true">' +
			actionsHtml +
			labelRowHtml +
			previewHtml +
			'</div>';
	}

	function fieldPreviewElement( field ) {
		var ph  = esc( field.placeholder || field.label || '' );
		var lbl = esc( field.label || field.key );

		switch ( field.type ) {
			case 'text':
			case 'email':
			case 'tel':
			case 'url':
			case 'number':
				return '<input type="' + esc( field.type ) + '" class="kramar-fb-preview-input" disabled placeholder="' + ph + '">';

			case 'textarea':
				return '<textarea class="kramar-fb-preview-textarea" disabled rows="2" placeholder="' + ph + '"></textarea>';

			case 'select': {
				var selOpts = '<option>' + ( field.placeholder ? esc( field.placeholder ) : 'Select ' + lbl + '...' ) + '</option>';
				if ( field.options ) {
					field.options.split( '|' ).forEach( function ( opt ) {
						var o = opt.trim();
						if ( o ) { selOpts += '<option>' + esc( o ) + '</option>'; }
					} );
				}
				return '<select class="kramar-fb-preview-select" disabled>' + selOpts + '</select>';
			}

			case 'radio': {
				if ( ! field.options ) {
					return '<div class="kramar-fb-preview-radio"><span class="kramar-fb-preview-placeholder">No options set</span></div>';
				}
				var radioHtml = '<div class="kramar-fb-preview-radio">';
				field.options.split( '|' ).forEach( function ( opt ) {
					var o = opt.trim();
					if ( o ) {
						radioHtml += '<label><input type="radio" disabled> ' + esc( o ) + '</label>';
					}
				} );
				radioHtml += '</div>';
				return radioHtml;
			}

			case 'checkbox':
				return '<label class="kramar-fb-preview-checkbox"><input type="checkbox" disabled> ' + lbl + '</label>';

			case 'date':
				return '<input type="date" class="kramar-fb-preview-input" disabled>';

			case 'time':
				return '<input type="time" class="kramar-fb-preview-input" disabled>';

			case 'hidden':
				return '<div class="kramar-fb-hidden-badge"><span class="dashicons dashicons-hidden"></span> ' + esc( field.key ) + '</div>';

			case 'heading':
				return '<h3 class="kramar-fb-preview-heading">' + lbl + '</h3>';

			case 'paragraph':
				return '<p class="kramar-fb-preview-paragraph">' + lbl + '</p>';

			default:
				return '<input type="text" class="kramar-fb-preview-input" disabled placeholder="' + ph + '">';
		}
	}

	function initCanvasEvents() {
		el.canvas.addEventListener( 'click', function ( e ) {
			var tab = e.target.closest( '.kramar-fb-canvas-tab' );
			if ( tab ) {
				state.selectedSection = tab.dataset.section;
				state.selectedKey     = null;
				renderCanvas();
				renderSettings();
				return;
			}

			if ( e.target.closest( '.kramar-fb-form-field__handle' ) ) { return; }

			var deleteBtn = e.target.closest( '.kramar-fb-form-field__delete' );
			if ( deleteBtn ) {
				var card = deleteBtn.closest( '.kramar-fb-form-field' );
				if ( card && window.confirm( kramarFieldBuilder.i18n.confirmDelete ) ) {
					deleteField( card.dataset.section, card.dataset.key );
				}
				return;
			}

			var fieldCard = e.target.closest( '.kramar-fb-form-field' );
			if ( fieldCard ) {
				selectField( fieldCard.dataset.section, fieldCard.dataset.key );
			}
		} );
	}

	/* =========================================================================
	   Right panel — Field settings
	   ====================================================================== */

	function renderSettings() {
		if ( ! state.selectedKey ) {
			el.settings.innerHTML =
				'<div class="kramar-fb-settings__empty" id="kramar-fb-settings-empty">' +
					'<span class="dashicons dashicons-edit-large"></span>' +
					'<p>' + esc( kramarFieldBuilder.i18n.selectField ) + '</p>' +
				'</div>';
			return;
		}

		var field = findField( state.selectedSection, state.selectedKey );
		if ( ! field ) {
			state.selectedKey = null;
			renderSettings();
			return;
		}

		var i18n      = kramarFieldBuilder.i18n;
		var isCustom  = !! field.custom;
		var typeLabel = kramarFieldBuilder.fieldTypes[ field.type ] || field.type;

		var badgeHtml = isCustom
			? '<span class="kramar-fb-badge kramar-fb-badge--custom">' + esc( i18n.custom ) + '</span>'
			: '<span class="kramar-fb-badge kramar-fb-badge--default">' + esc( i18n.default ) + '</span>';

		var html =
			'<div class="kramar-fb-settings__header">' +
				'<div class="kramar-fb-settings__icon"><span class="dashicons ' + esc( FIELD_ICONS[ field.type ] || 'dashicons-admin-generic' ) + '"></span></div>' +
				'<div>' +
					'<div class="kramar-fb-settings__title">' + esc( field.label || field.key ) + ' ' + badgeHtml + '</div>' +
					'<div class="kramar-fb-settings__subtitle">' + esc( typeLabel ) + ' &mdash; ' + esc( field.key ) + '</div>' +
				'</div>' +
			'</div>' +
			'<div class="kramar-fb-settings-form" id="kramar-fb-settings-body">' +
				buildSettingsBodyHtml( field, isCustom ) +
			'</div>';

		el.settings.innerHTML = html;

		el.settings.classList.remove( 'kramar-fb-settings--animate' );
		void el.settings.offsetWidth;
		el.settings.classList.add( 'kramar-fb-settings--animate' );

		bindSettingsEvents( field );
	}

	function buildSettingsBodyHtml( field, isCustom ) {
		var i18n = kramarFieldBuilder.i18n;
		var html = '';

		html += settingText( 'label', i18n.label, field.label || '' );

		if ( field.type !== 'heading' && field.type !== 'paragraph' ) {
			html += settingText( 'placeholder', i18n.placeholder, field.placeholder || '' );
		}

		html += settingSelect(
			'type',
			i18n.type,
			kramarFieldBuilder.fieldTypes,
			field.type,
			! isCustom
		);

		var currentWidth = 'form-row-wide';
		if ( Array.isArray( field.class ) ) {
			if ( field.class.indexOf( 'form-row-first' ) !== -1 ) { currentWidth = 'form-row-first'; }
			else if ( field.class.indexOf( 'form-row-last' ) !== -1 ) { currentWidth = 'form-row-last'; }
		}

		var widthOptions = [
			{ value: 'form-row-wide',  label: 'Full' },
			{ value: 'form-row-first', label: 'Left Half' },
			{ value: 'form-row-last',  label: 'Right Half' },
		];
		html += '<div class="kramar-fb-setting">' +
			'<label class="kramar-fb-setting__label">' + esc( i18n.width || 'Width' ) + '</label>' +
			'<div class="kramar-fb-width-control">';
		widthOptions.forEach( function ( opt ) {
			var isActive = opt.value === currentWidth;
			html += '<button type="button" class="kramar-fb-width-btn' +
				( isActive ? ' kramar-fb-width-btn--active' : '' ) +
				'" data-width="' + esc( opt.value ) + '">' + esc( opt.label ) + '</button>';
		} );
		html += '</div></div>';

		if ( field.type !== 'heading' && field.type !== 'paragraph' && field.type !== 'hidden' ) {
			html += settingToggle( 'required', i18n.required, !! field.required );
		}

		html += settingToggle( 'enabled', i18n.enabled, !! field.enabled );

		html += '<div class="kramar-fb-setting__divider"></div>';

		html += '<div class="kramar-fb-collapsible" id="kramar-fb-advanced">' +
			'<div class="kramar-fb-collapsible__trigger">Advanced <span class="dashicons dashicons-arrow-down-alt2"></span></div>' +
			'<div class="kramar-fb-collapsible__body">';

		html += settingNumber( 'priority', i18n.priority, field.priority || 10 );

		var cssVal = Array.isArray( field.class ) ? field.class.join( ', ' ) : ( field.class || '' );
		html += settingText( 'css_class', i18n.cssClass, cssVal );
		html += '<div class="kramar-fb-setting__hint">form-row-wide, form-row-first, form-row-last</div>';

		if ( field.type !== 'heading' && field.type !== 'paragraph' ) {
			html += settingText( 'default', i18n.defaultValue, field.default || '' );
		}

		html += '</div></div>';

		if ( field.type === 'select' || field.type === 'radio' ) {
			html += '<div class="kramar-fb-setting__divider"></div>';
			var optionsDisplay = ( field.options || '' ).split( '|' ).join( '\n' );
			html += settingTextarea( 'options', i18n.options, optionsDisplay );
			html += '<div class="kramar-fb-setting__hint">One per line — used as both value and label.</div>';
		}

		if ( isCustom ) {
			html += '<div class="kramar-fb-setting__divider"></div>';
			html += '<div class="kramar-fb-setting__group-label">' + esc( i18n.fieldKey ) + '</div>';
			html += '<div class="kramar-fb-setting">' +
				'<input type="text" class="kramar-fb-setting__input kramar-fb-setting__input--key" ' +
				'id="kramar-fb-key-input" value="' + esc( field.key ) + '" ' +
				'pattern="[a-z0-9_]+" placeholder="my_custom_field">' +
				'<div class="kramar-fb-setting__hint">Lowercase letters, numbers, underscores.</div>' +
				'</div>';

			html += '<div class="kramar-fb-settings__footer">' +
				'<button type="button" class="kramar-fb-btn-delete" id="kramar-fb-delete-field">' +
					esc( i18n.delete ) +
				'</button>' +
				'</div>';
		}

		return html;
	}

	/* -------------------------------------------------------------------------
	   Settings helpers
	   ---------------------------------------------------------------------- */

	function settingText( prop, label, value ) {
		return '<div class="kramar-fb-setting">' +
			'<label class="kramar-fb-setting__label">' + esc( label ) + '</label>' +
			'<input type="text" class="kramar-fb-setting__input" data-prop="' + esc( prop ) + '" value="' + esc( String( value ) ) + '">' +
			'</div>';
	}

	function settingNumber( prop, label, value ) {
		return '<div class="kramar-fb-setting">' +
			'<label class="kramar-fb-setting__label">' + esc( label ) + '</label>' +
			'<input type="number" class="kramar-fb-setting__input kramar-fb-setting__input--small" ' +
			'data-prop="' + esc( prop ) + '" value="' + esc( String( value ) ) + '" min="1" step="1">' +
			'</div>';
	}

	function settingTextarea( prop, label, value ) {
		return '<div class="kramar-fb-setting">' +
			'<label class="kramar-fb-setting__label">' + esc( label ) + '</label>' +
			'<textarea class="kramar-fb-setting__input" data-prop="' + esc( prop ) + '" rows="4">' + esc( String( value ) ) + '</textarea>' +
			'</div>';
	}

	function settingToggle( prop, label, checked ) {
		return '<label class="kramar-fb-toggle-row">' +
			'<input type="checkbox" data-prop="' + esc( prop ) + '"' + ( checked ? ' checked' : '' ) + '>' +
			'<span class="kramar-fb-toggle-track"><span class="kramar-fb-toggle-thumb"></span></span>' +
			'<span>' + esc( label ) + '</span>' +
			'</label>';
	}

	function settingSelect( prop, label, optionsMap, selected, disabled ) {
		var opts = '';
		Object.keys( optionsMap ).forEach( function ( key ) {
			opts += '<option value="' + esc( key ) + '"' +
				( key === selected ? ' selected' : '' ) + '>' +
				esc( optionsMap[ key ] ) + '</option>';
		} );
		return '<div class="kramar-fb-setting">' +
			'<label class="kramar-fb-setting__label">' + esc( label ) + '</label>' +
			'<select class="kramar-fb-setting__input" data-prop="' + esc( prop ) + '"' + ( disabled ? ' disabled' : '' ) + '>' +
			opts + '</select>' +
			'</div>';
	}

	/* =========================================================================
	   Settings panel event binding
	   ====================================================================== */

	function bindSettingsEvents( field ) {
		var body = document.getElementById( 'kramar-fb-settings-body' );
		if ( ! body ) { return; }

		body.querySelectorAll( '[data-prop]' ).forEach( function ( input ) {
			var prop  = input.dataset.prop;
			var event = ( input.type === 'checkbox' ) ? 'change' : 'input';

			input.addEventListener( event, function () {
				var sectionId = state.selectedSection;
				var key       = state.selectedKey;
				var f         = findField( sectionId, key );
				if ( ! f ) { return; }

				if ( prop === 'enabled' || prop === 'required' ) {
					f[ prop ] = input.checked;
					updateFieldCount();
				} else if ( prop === 'priority' ) {
					f.priority = parseInt( input.value, 10 ) || 1;
				} else if ( prop === 'css_class' ) {
					f.class = input.value.split( ',' )
						.map( function ( s ) { return s.trim(); } )
						.filter( function ( s ) { return s !== ''; } );
					if ( f.class.length === 0 ) { f.class = [ 'form-row-wide' ]; }
				} else if ( prop === 'type' ) {
					f.type = input.value;
				} else if ( prop === 'options' ) {
					f.options = input.value.split( '\n' )
						.map( function ( s ) { return s.trim(); } )
						.filter( Boolean )
						.join( '|' );
				} else {
					f[ prop ] = input.value;
				}

				state.dirty = true;

				if ( prop === 'label' || prop === 'required' || prop === 'enabled' || prop === 'options' || prop === 'placeholder' ) {
					refreshFieldPreview( sectionId, key );
					if ( prop === 'label' ) {
						var titleEl = el.settings.querySelector( '.kramar-fb-settings__title' );
						if ( titleEl ) {
							var badge = titleEl.querySelector( '.kramar-fb-badge' );
							titleEl.textContent = input.value + ' ';
							if ( badge ) { titleEl.appendChild( badge ); }
						}
					}
				}
				if ( prop === 'type' ) {
					renderSettings();
				}
			} );
		} );

		body.querySelectorAll( '.kramar-fb-width-btn' ).forEach( function ( btn ) {
			btn.addEventListener( 'click', function () {
				var sectionId = state.selectedSection;
				var key       = state.selectedKey;
				var f         = findField( sectionId, key );
				if ( ! f ) { return; }

				var widthVal   = btn.dataset.width;
				var otherWidth = widthVal === 'form-row-first' ? 'form-row-last'
					: widthVal === 'form-row-last' ? 'form-row-first'
					: null;

				var classes = Array.isArray( f.class ) ? f.class.slice() : [ 'form-row-wide' ];
				classes = classes.filter( function ( c ) {
					return c !== 'form-row-wide' && c !== 'form-row-first' && c !== 'form-row-last';
				} );
				classes.push( widthVal );
				f.class = classes;

				body.querySelectorAll( '.kramar-fb-width-btn' ).forEach( function ( b ) {
					b.classList.toggle( 'kramar-fb-width-btn--active', b.dataset.width === widthVal );
				} );

				state.dirty = true;
				refreshFieldPreview( sectionId, key );
			} );
		} );

		var collapsible = body.querySelector( '.kramar-fb-collapsible' );
		if ( collapsible ) {
			var trigger = collapsible.querySelector( '.kramar-fb-collapsible__trigger' );
			if ( trigger ) {
				trigger.addEventListener( 'click', function () {
					collapsible.classList.toggle( 'kramar-fb-collapsible--open' );
				} );
			}
		}

		var keyInput = document.getElementById( 'kramar-fb-key-input' );
		if ( keyInput ) {
			keyInput.addEventListener( 'change', function () {
				renameFieldKey( keyInput.value.toLowerCase().replace( /[^a-z0-9_]/g, '_' ) );
			} );
		}

		var delBtn = document.getElementById( 'kramar-fb-delete-field' );
		if ( delBtn ) {
			delBtn.addEventListener( 'click', function () {
				if ( ! window.confirm( kramarFieldBuilder.i18n.confirmDelete ) ) { return; }
				deleteField( state.selectedSection, state.selectedKey );
			} );
		}
	}

	/* =========================================================================
	   Field operations
	   ====================================================================== */

	function selectField( sectionId, key ) {
		state.selectedSection = sectionId;
		state.selectedKey     = key;
		renderCanvas();
		renderSettings();
	}

	function handleAddField( type ) {
		var sectionId = state.selectedSection;
		var fields    = state.sections[ sectionId ];

		var base    = sectionId + '_custom_' + state.newFieldCounter++;
		var key     = base;
		var attempt = 0;
		while ( fieldExists( sectionId, key ) ) {
			attempt++;
			key = base + '_' + attempt;
		}

		var maxPriority = 10;
		if ( fields && fields.length ) {
			fields.forEach( function ( f ) {
				if ( ( f.priority || 0 ) > maxPriority ) { maxPriority = f.priority; }
			} );
		}

		var newField = {
			key:         key,
			type:        type,
			label:       kramarFieldBuilder.fieldTypes[ type ] || type,
			placeholder: '',
			required:    false,
			class:       [ 'form-row-wide' ],
			'default':   '',
			options:     '',
			enabled:     true,
			priority:    maxPriority + 10,
			custom:      true,
		};

		if ( ! state.sections[ sectionId ] ) {
			state.sections[ sectionId ] = [];
		}
		state.sections[ sectionId ].push( newField );
		state.dirty = true;

		selectField( sectionId, key );

		var newCard = el.canvas.querySelector( '[data-key="' + key + '"]' );
		if ( newCard ) {
			newCard.classList.add( 'kramar-fb-form-field--new' );
			newCard.scrollIntoView( { behavior: 'smooth', block: 'nearest' } );
		}

		updateFieldCount();
	}

	function deleteField( sectionId, key ) {
		var card = el.canvas.querySelector( '[data-key="' + key + '"][data-section="' + sectionId + '"]' );
		if ( card ) {
			card.classList.add( 'kramar-fb-form-field--removing' );
			setTimeout( function () {
				state.sections[ sectionId ] = state.sections[ sectionId ].filter( function ( f ) { return f.key !== key; } );
				state.selectedKey = null;
				state.dirty = true;
				render();
			}, 200 );
		} else {
			state.sections[ sectionId ] = state.sections[ sectionId ].filter( function ( f ) { return f.key !== key; } );
			state.selectedKey = null;
			state.dirty = true;
			render();
		}
	}

	function renameFieldKey( newKey ) {
		if ( ! newKey || newKey === state.selectedKey ) { return; }
		if ( fieldExists( state.selectedSection, newKey ) ) {
			alert( 'That key is already in use.' );
			return;
		}
		var f = findField( state.selectedSection, state.selectedKey );
		if ( ! f ) { return; }
		f.key             = newKey;
		state.selectedKey = newKey;
		state.dirty       = true;
		render();
	}

	/* -------------------------------------------------------------------------
	   Helpers
	   ---------------------------------------------------------------------- */

	function findField( sectionId, key ) {
		var fields = state.sections[ sectionId ];
		if ( ! fields ) { return null; }
		for ( var i = 0; i < fields.length; i++ ) {
			if ( fields[ i ].key === key ) { return fields[ i ]; }
		}
		return null;
	}

	function fieldExists( sectionId, key ) {
		return !! findField( sectionId, key );
	}

	function refreshFieldPreview( sectionId, key ) {
		var card = el.canvas.querySelector( '[data-key="' + key + '"][data-section="' + sectionId + '"]' );
		if ( ! card ) { return; }
		var field = findField( sectionId, key );
		if ( ! field ) { return; }

		var newHtml = renderFieldPreview( field, sectionId );
		var tmp = document.createElement( 'div' );
		tmp.innerHTML = newHtml;
		var newCard = tmp.firstElementChild;
		card.parentNode.replaceChild( newCard, card );
	}

	function updateFieldCount() {
		if ( ! el.fieldCount ) { return; }
		var section = state.sections[ state.selectedSection ] || [];
		var enabled = section.filter( function ( f ) { return f.enabled; } ).length;
		el.fieldCount.textContent = enabled + ' of ' + section.length + ' fields';
	}

	/* =========================================================================
	   Toast notifications
	   ====================================================================== */

	function showToast( message, type ) {
		var existing = document.querySelector( '.kramar-fb-notice-toast' );
		if ( existing ) { existing.remove(); }

		var toast = document.createElement( 'div' );
		toast.className = 'kramar-fb-notice-toast kramar-fb-notice-toast--' + ( type || 'success' );
		var icon = type === 'error' ? 'dashicons-warning' : 'dashicons-yes-alt';
		toast.innerHTML = '<span class="dashicons ' + icon + '"></span>' + esc( message );
		document.body.appendChild( toast );

		setTimeout( function () {
			toast.classList.add( 'kramar-fb-notice-toast--fade' );
			setTimeout( function () { if ( toast.parentNode ) { toast.remove(); } }, 300 );
		}, 2500 );
	}

	/* =========================================================================
	   Drag-and-drop reordering (mouse-based)
	   ====================================================================== */

	function initDragDrop() {
		var draggedCard  = null;
		var draggedKey   = null;
		var dragSection  = null;
		var placeholder  = null;

		el.canvas.addEventListener( 'mousedown', function ( e ) {
			var handle = e.target.closest( '.kramar-fb-form-field__handle' );
			if ( ! handle ) { return; }
			var card = handle.closest( '.kramar-fb-form-field' );
			if ( ! card ) { return; }

			e.preventDefault();

			var container = document.getElementById( 'kramar-fb-form-preview' );
			if ( ! container ) { return; }

			draggedCard = card;
			draggedKey  = card.dataset.key;
			dragSection = card.dataset.section;

			placeholder = document.createElement( 'div' );
			placeholder.className = 'kramar-fb-drop-placeholder';
			placeholder.style.height = card.offsetHeight + 'px';

			card.classList.add( 'kramar-fb-form-field--dragging' );
			card.style.position      = 'fixed';
			card.style.width         = card.offsetWidth + 'px';
			card.style.zIndex        = '10000';
			card.style.pointerEvents = 'none';
			card.style.left          = card.getBoundingClientRect().left + 'px';
			card.style.top           = card.getBoundingClientRect().top + 'px';

			card.parentNode.insertBefore( placeholder, card );
			document.body.appendChild( card );

			document.addEventListener( 'mousemove', onMove );
			document.addEventListener( 'mouseup', onUp );
		} );

		function onMove( e ) {
			if ( ! draggedCard ) { return; }
			draggedCard.style.top = ( e.clientY - 20 ) + 'px';

			var container = document.getElementById( 'kramar-fb-form-preview' );
			if ( ! container ) { return; }

			var cards    = container.querySelectorAll( '.kramar-fb-form-field:not(.kramar-fb-form-field--dragging)' );
			var inserted = false;

			for ( var i = 0; i < cards.length; i++ ) {
				var rect = cards[ i ].getBoundingClientRect();
				if ( e.clientY < rect.top + rect.height / 2 ) {
					container.insertBefore( placeholder, cards[ i ] );
					inserted = true;
					break;
				}
			}
			if ( ! inserted ) {
				container.appendChild( placeholder );
			}
		}

		function onUp() {
			if ( ! draggedCard || ! draggedKey ) { return; }
			document.removeEventListener( 'mousemove', onMove );
			document.removeEventListener( 'mouseup', onUp );

			var container = document.getElementById( 'kramar-fb-form-preview' );

			var newOrder = [];
			if ( container ) {
				var children = container.querySelectorAll(
					'.kramar-fb-drop-placeholder, .kramar-fb-form-field:not(.kramar-fb-form-field--dragging)'
				);
				for ( var i = 0; i < children.length; i++ ) {
					if ( children[ i ] === placeholder ) {
						newOrder.push( draggedKey );
					} else if ( children[ i ].dataset && children[ i ].dataset.key ) {
						newOrder.push( children[ i ].dataset.key );
					}
				}
			}

			if ( placeholder && placeholder.parentNode ) {
				placeholder.parentNode.removeChild( placeholder );
			}
			if ( draggedCard.parentNode === document.body ) {
				document.body.removeChild( draggedCard );
			}

			var fields  = state.sections[ dragSection ];
			var indexed = {};
			fields.forEach( function ( f ) { indexed[ f.key ] = f; } );

			var reordered = [];
			newOrder.forEach( function ( k, idx ) {
				if ( indexed[ k ] ) {
					indexed[ k ].priority = ( idx + 1 ) * 10;
					reordered.push( indexed[ k ] );
				}
			} );
			fields.forEach( function ( f ) {
				if ( newOrder.indexOf( f.key ) === -1 ) {
					reordered.push( f );
				}
			} );

			state.sections[ dragSection ] = reordered;
			state.dirty = true;

			draggedCard = null;
			draggedKey  = null;
			dragSection = null;
			placeholder = null;

			renderCanvas();
		}
	}

	/* =========================================================================
	   Save
	   ====================================================================== */

	function bindGlobalEvents() {
		if ( ! el.saveBtn ) { return; }

		el.saveBtn.addEventListener( 'click', function () {
			saveFields();
		} );
	}

	function saveFields() {
		var btn    = el.saveBtn;
		var status = el.saveStat;
		var i18n   = kramarFieldBuilder.i18n;

		btn.disabled       = true;
		status.textContent = i18n.saving;
		status.className   = '';

		var body = new URLSearchParams( {
			action:   'kramar_save_checkout_fields',
			_wpnonce: kramarFieldBuilder.nonce,
			fields:   JSON.stringify( state.sections ),
		} );

		fetch( kramarFieldBuilder.ajaxUrl, {
			method:  'POST',
			headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
			body:    body.toString(),
		} )
		.then( function ( response ) { return response.json(); } )
		.then( function ( data ) {
			btn.disabled       = false;
			status.textContent = '';
			status.className   = '';
			if ( data.success ) {
				state.dirty = false;
				showToast( i18n.saved, 'success' );
			} else {
				showToast( data.data || 'Error', 'error' );
			}
		} )
		.catch( function () {
			btn.disabled       = false;
			status.textContent = '';
			status.className   = '';
			showToast( 'Save failed.', 'error' );
		} );
	}

	/* =========================================================================
	   Utility
	   ====================================================================== */

	function esc( val ) {
		var div = document.createElement( 'div' );
		div.textContent = String( val == null ? '' : val );
		return div.innerHTML;
	}

	/* =========================================================================
	   Bootstrap
	   ====================================================================== */

	if ( document.readyState === 'loading' ) {
		document.addEventListener( 'DOMContentLoaded', init );
	} else {
		init();
	}

} )();
