/**
 * Create Fundraiser Form — frontend script.
 *
 * Handles three creation flows:
 *   individual  — personal fundraising page (1 step)
 *   team        — team creation + optional personal join (2 steps)
 *   fundraiser  — sub-cause container (1 step)
 *
 * Globals injected by Block::enqueue_frontend():
 *   window.ambhUserId      — current user ID
 *   window.ambhApiBase     — REST base URL (no trailing slash)
 *   window.ambhFundraiserFormConfig.createFields
 *   window.ambhFundraiserFormConfig.editFields
 *
 * @package AmbassadorHub\Fundraisers\Blocks\CreateFundraiserForm
 */

( function () {
	'use strict';

	document.addEventListener( 'DOMContentLoaded', function () {
		document.querySelectorAll( '.ambh-create-fundraiser-form' ).forEach( initForm );
	} );

	// -------------------------------------------------------------------------
	// Per-instance setup
	// -------------------------------------------------------------------------

	function initForm( wrapper ) {
		var rootId           = parseInt( wrapper.dataset.rootId   || '0', 10 );
		var parentId         = parseInt( wrapper.dataset.parentId || '0', 10 );
		var nestingEnabled   = wrapper.dataset.nestingEnabled === '1';
		var forcedType       = wrapper.dataset.forcedType || '';
		var nonce            = wrapper.dataset.nonce;
		var userDisplayName  = wrapper.dataset.userDisplayName || '';

		var editMode      = wrapper.dataset.editMode === '1';
		var editId        = parseInt( wrapper.dataset.editFundraiserId || '0', 10 );
		var editPermalink = wrapper.dataset.editPermalink || '';

		var rootStep          = wrapper.querySelector( '[data-step="select-root"]' );
		var typeStep          = wrapper.querySelector( '[data-step="select-type"]' );
		var dataStep          = wrapper.querySelector( '[data-step="fundraiser-data"]' );
		var postFundraiserStep = wrapper.querySelector( '[data-step="post-fundraiser"]' );
		var personalJoinStep  = wrapper.querySelector( '[data-step="personal-join"]' );

		var contextInfo      = wrapper.querySelector( '[data-context-info]' );
		var contextRootEl    = contextInfo ? contextInfo.querySelector( '[data-context-root-text]' ) : null;
		var contextParentEl  = contextInfo ? contextInfo.querySelector( '[data-context-parent-text]' ) : null;
		var contextParentWrap = contextInfo ? contextInfo.querySelector( '[data-context-parent]' ) : null;

		// Active type selected in the type-selection step (empty when pre-forced).
		var selectedType = forcedType;

		// State set after team is successfully created.
		var createdTeamId  = 0;
		var createdTeamUrl = '';

		// State set after a sub-fundraiser is created (post-fundraiser flow).
		var createdFundraiserId  = 0;
		var createdFundraiserUrl = '';
		var inPostFundraiserFlow = false;

		// Image picker instances (one per form with an image dropzone).
		var mainImagePicker     = initImagePicker( dataStep );
		var hasExistingImage    = mainImagePicker ? mainImagePicker.hasExistingImage : false;

		// -------------------------------------------------------------------------
		// Root fundraiser selection
		// -------------------------------------------------------------------------

		if ( rootStep ) {
			rootStep.querySelectorAll( '.ambh-cff-root-option' ).forEach( function ( btn ) {
				btn.addEventListener( 'click', function () {
					rootId   = parseInt( btn.dataset.rootId || '0', 10 );
					parentId = rootId;

					wrapper.dataset.rootId         = String( rootId );
					wrapper.dataset.parentId       = String( parentId );
					wrapper.dataset.nestingEnabled = btn.dataset.nestingEnabled || '0';
					nestingEnabled = wrapper.dataset.nestingEnabled === '1';

					updateContextInfo( btn.dataset.rootTitle || '', '' );

					// When the card button carries a pre-set action (individual / team),
					// skip the type step and go straight to the data step.
					var action = btn.dataset.action || '';
					if ( action && dataStep ) {
						selectedType = action;
						setIsGroup( action === 'team' );
						syncAllowNested( action === 'team' );
						applyTypeLabels( action );
						if ( action === 'individual' ) prefillDisplayName( dataStep );
						restoreDraft();
						showStep( dataStep );
						loadFormFieldsFragment( action );
						return;
					}

					if ( typeStep ) {
						// A depth-1 root always allows individual and sub-fundraiser children;
						// show those items unconditionally, team depends on nesting setting.
						var typeItems = {
							individual: typeStep.querySelector( '[data-type-item="individual"]' ),
							team:       typeStep.querySelector( '[data-type-item="team"]' ),
							fundraiser: typeStep.querySelector( '[data-type-item="fundraiser"]' ),
						};
						if ( typeItems.individual ) typeItems.individual.hidden = false;
						if ( typeItems.fundraiser ) typeItems.fundraiser.hidden = false;
						if ( typeItems.team )       typeItems.team.hidden       = ! nestingEnabled;
						showStep( typeStep );
					} else {
						selectedType = nestingEnabled ? '' : 'individual';
						setIsGroup( false );
						applyTypeLabels( 'fundraiser' );
						restoreDraft();
						showStep( dataStep );
						loadFormFieldsFragment( rootId > 0 ? 'individual' : '' );
					}
				} );
			} );
		}

		// -------------------------------------------------------------------------
		// Type-selection step
		// -------------------------------------------------------------------------

		if ( typeStep ) {
			typeStep.querySelectorAll( '.ambh-cff-type-option' ).forEach( function ( btn ) {
				btn.addEventListener( 'click', function () {
					typeStep.querySelectorAll( '.ambh-cff-type-option' ).forEach( function ( b ) {
						b.classList.remove( 'is-active' );
					} );
					btn.classList.add( 'is-active' );

					selectedType = btn.dataset.type || 'individual';
					var isTeam   = selectedType === 'team';

					setIsGroup( isTeam );
					syncAllowNested( isTeam );
					applyTypeLabels( selectedType );

					// Pre-fill display name when joining as individual.
					if ( selectedType === 'individual' ) {
						prefillDisplayName( dataStep );
					}

					restoreDraft();
					showStep( dataStep );
					loadFormFieldsFragment( selectedType );
				} );
			} );

			var typeBackBtn = typeStep.querySelector( '.ambh-cff-back--type' );
			if ( typeBackBtn ) {
				typeBackBtn.addEventListener( 'click', function () {
					selectedType = '';
					updateContextInfo( '', '' );
					showStep( rootStep );
				} );
			}
		}

		// Pre-fill display name immediately when type is already forced to individual.
		if ( forcedType === 'individual' && dataStep ) {
			prefillDisplayName( dataStep );
		}

		// Back from data step.
		var dataStepEl  = dataStep ? dataStep.querySelector( '.ambh-cff-step-inner' ) : null;
		var dataBackBtn = dataStep ? dataStep.querySelector( '.ambh-cff-back' ) : null;
		if ( dataBackBtn ) {
			dataBackBtn.addEventListener( 'click', function () {
				if ( inPostFundraiserFlow && postFundraiserStep ) {
					showStep( postFundraiserStep );
				} else if ( typeStep ) {
					showStep( typeStep );
				} else if ( rootStep ) {
					updateContextInfo( '', '' );
					showStep( rootStep );
				}
			} );
		}

		// -------------------------------------------------------------------------
		// Label swapping (when type is chosen in the type-selection step)
		// -------------------------------------------------------------------------

		function applyTypeLabels( type ) {
			if ( ! dataStep ) return;
			var inner  = dataStep.querySelector( '.ambh-cff-step-inner' );
			if ( ! inner ) return;

			var labelsRaw = inner.dataset.labels;
			if ( ! labelsRaw ) return;

			var allLabels;
			try { allLabels = JSON.parse( labelsRaw ); } catch ( e ) { return; }

			var labels = allLabels[ type ] || allLabels['fundraiser'];
			if ( ! labels ) return;

			// Heading.
			var headingEl = inner.querySelector( '[data-field="heading"]' );
			if ( headingEl && labels.heading ) headingEl.textContent = labels.heading;

			// Title label + hint.
			var titleLabelEl = inner.querySelector( '[data-field="title-label"]' );
			if ( titleLabelEl && labels.title ) titleLabelEl.firstChild.textContent = labels.title + ' ';
			var titleHintEl = inner.querySelector( '[data-field="title-hint"]' );
			if ( titleHintEl ) {
				titleHintEl.textContent = labels.titleHint || '';
				titleHintEl.hidden = ! labels.titleHint;
			}

			// Content label + hint.
			var contentLabelEl = inner.querySelector( '[data-field="content-label"]' );
			if ( contentLabelEl && labels.content ) contentLabelEl.firstChild.textContent = labels.content + ' ';
			var contentHintEl = inner.querySelector( '[data-field="content-hint"]' );
			if ( contentHintEl ) {
				contentHintEl.textContent = labels.contentHint || '';
				contentHintEl.hidden = ! labels.contentHint;
			}

			// Image label.
			var imageLabelEl = inner.querySelector( '[data-field="image-label"]' );
			if ( imageLabelEl && labels.image ) imageLabelEl.textContent = labels.image;

			// Target label + optional marker.
			var targetWrap  = inner.querySelector( '[data-field-target-wrap]' );
			var targetInput = inner.querySelector( '[name="target_amount"]' );
			if ( targetWrap ) {
				targetWrap.classList.toggle( 'ambh-cff-field--optional', !! labels.targetOptional );
			}
			if ( targetInput ) {
				if ( labels.targetOptional ) {
					targetInput.removeAttribute( 'required' );
				} else {
					targetInput.setAttribute( 'required', '' );
				}
			}

			// Submit button.
			var submitEl = inner.querySelector( '[data-field="submit-label"]' );
			if ( submitEl && labels.submit ) submitEl.textContent = labels.submit;

			// Allow-nested checkbox (team sub-team setting).
			var allowNestedEl = inner.querySelector( '.ambh-cff-field--allow-nested' );
			if ( allowNestedEl ) {
				allowNestedEl.style.display = ( type === 'team' && labels.allowNested ) ? '' : 'none';
			}

			// Teams-enabled checkbox (root fundraiser setting — only shown when creating a new root).
			var teamsEnabledEl = inner.querySelector( '.ambh-cff-field--teams-enabled' );
			if ( teamsEnabledEl ) {
				teamsEnabledEl.style.display = ( type === 'fundraiser' && rootId === 0 && labels.teamsEnabled ) ? '' : 'none';
			}

			// is_group hidden input.
			setIsGroup( type === 'team' );
		}

		// -------------------------------------------------------------------------
		// Pre-fill user display name
		// -------------------------------------------------------------------------

		function prefillDisplayName( stepEl ) {
			if ( ! stepEl || ! userDisplayName ) return;
			var titleInput = stepEl.querySelector( '[name="title"]' );
			if ( titleInput && ! titleInput.value ) {
				titleInput.value = userDisplayName;
			}
		}

		// -------------------------------------------------------------------------
		// Form-fields fragment loader
		// -------------------------------------------------------------------------

		var fieldsContainer = dataStep ? dataStep.querySelector( '[data-ambh-form-fields-container]' ) : null;
		var fieldsFetchSeq  = 0;

		function loadFormFieldsFragment( type ) {
			if ( editMode || ! fieldsContainer || ! window.ambhApiBase ) return;

			var seq    = ++fieldsFetchSeq;
			var effectiveParentId = parentId > 0 ? parentId : rootId || 0;
			var params = new URLSearchParams( {
				mode:          'create',
				fundraiser_id: '0',
				root_id:       String( rootId || 0 ),
				type:          type || '',
				parent_id:     String( effectiveParentId ),
			} );

			fetch(
				window.ambhApiBase + '/ambassadorhub/v1/fundraiser-form-fields?' + params.toString(),
				{ credentials: 'same-origin', headers: { 'X-WP-Nonce': nonce || '' } }
			)
				.then( function ( r ) { return r.ok ? r.json() : Promise.reject(); } )
				.then( function ( payload ) {
					if ( seq !== fieldsFetchSeq ) return;
					fieldsContainer.innerHTML = ( payload && payload.html ) || '';
					var event = new CustomEvent( 'ambh:fundraiser-form-fields-updated', {
						bubbles: true,
						detail: {
							container:   fieldsContainer,
							form:        dataStep ? dataStep.querySelector( '.ambh-cff-form' ) : null,
							formContext: { mode: 'create', fundraiser_id: 0, root_id: rootId || 0, type: type || '', parent_id: effectiveParentId },
							fieldConfig: ( payload && payload.fieldConfig ) || [],
						},
					} );
					wrapper.dispatchEvent( event );
				} )
				.catch( function () {} );
		}

		// -------------------------------------------------------------------------
		// Draft persistence
		// -------------------------------------------------------------------------

		function draftKey() {
			return 'ambh_cff_draft_' + ( window.ambhUserId || 0 ) + '_' + rootId + '_' + parentId;
		}

		function saveDraft() {
			if ( ! rootId || ! dataStep ) return;
			var form = dataStep.querySelector( '.ambh-cff-form' );
			if ( ! form ) return;
			var data = {};
			try {
				new FormData( form ).forEach( function ( value, key ) { data[ key ] = value; } );
				localStorage.setItem( draftKey(), JSON.stringify( data ) );
			} catch ( e ) {}
		}

		function restoreDraft() {
			var form = dataStep ? dataStep.querySelector( '.ambh-cff-form' ) : null;
			if ( ! form ) return;
			try {
				var raw = localStorage.getItem( draftKey() );
				if ( ! raw ) return;
				var data = JSON.parse( raw );
				Object.keys( data ).forEach( function ( key ) {
					var el = form.querySelector( '[name="' + key + '"]' );
					if ( el ) el.value = data[ key ];
				} );
			} catch ( e ) {}
		}

		function clearDraft() {
			try { localStorage.removeItem( draftKey() ); } catch ( e ) {}
		}

		if ( dataStep ) {
			var mainForm = dataStep.querySelector( '.ambh-cff-form' );
			if ( mainForm ) {
				mainForm.addEventListener( 'input', saveDraft );
				if ( rootId > 0 ) restoreDraft();
			}
		}

		// -------------------------------------------------------------------------
		// Helpers
		// -------------------------------------------------------------------------

		function setIsGroup( isTeam ) {
			var form = dataStep ? dataStep.querySelector( '.ambh-cff-form' ) : null;
			var el   = form ? form.querySelector( '[name="is_group"]' ) : null;
			if ( el ) el.value = isTeam ? '1' : '0';
		}

		function syncAllowNested( isTeam ) {
			var form = dataStep ? dataStep.querySelector( '.ambh-cff-form' ) : null;
			var el   = form ? form.querySelector( '.ambh-cff-field--allow-nested' ) : null;
			if ( ! el ) return;
			el.style.display = isTeam ? '' : 'none';
		}

		function updateContextInfo( rootTitle, parentTitle ) {
			if ( ! contextInfo ) return;
			if ( contextRootEl )   contextRootEl.textContent   = rootTitle;
			if ( contextParentEl ) contextParentEl.textContent = parentTitle;
			if ( contextParentWrap ) {
				contextParentWrap.style.display = parentTitle ? '' : 'none';
			}
			if ( rootTitle ) {
				contextInfo.classList.add( 'is-visible' );
			} else {
				contextInfo.classList.remove( 'is-visible' );
			}
		}

		function showStep( stepEl ) {
			wrapper.querySelectorAll( '.ambh-cff-step' ).forEach( function ( s ) {
				s.classList.remove( 'is-active' );
			} );
			if ( stepEl ) stepEl.classList.add( 'is-active' );
			wrapper.scrollIntoView( { behavior: 'smooth', block: 'start' } );
		}

		function setStatus( containerEl, message ) {
			var el = containerEl ? containerEl.querySelector( '.ambh-cff-status' ) : null;
			if ( el ) el.textContent = message;
		}

		function pickFields( obj, keys ) {
			var result = {};
			keys.forEach( function ( key ) { result[ key ] = obj[ key ]; } );
			return result;
		}

		function apiBase() {
			return ( window.ambhApiBase || '' ).replace( /\/$/, '' );
		}

		// -------------------------------------------------------------------------
		// Image picker (reusable per step container)
		// -------------------------------------------------------------------------

		function initImagePicker( stepEl ) {
			if ( ! stepEl ) return null;

			var imageDrop     = stepEl.querySelector( '.ambh-cff-image-drop' );
			var imageInput    = stepEl.querySelector( '.ambh-cff-image-input' );
			var imageCrop     = stepEl.querySelector( '.ambh-cff-image-crop' );
			var cropImgEl     = stepEl.querySelector( '.ambh-cff-crop-img' );
			var imageViewport = stepEl.querySelector( '.ambh-cff-crop-viewport' );
			var cropCanvas    = stepEl.querySelector( '.ambh-cff-crop-canvas' );
			var imageChange   = stepEl.querySelector( '.ambh-cff-image-change' );
			var currentImagePanel  = stepEl.querySelector( '.ambh-cff-current-image' );
			var currentImageChange = currentImagePanel ? currentImagePanel.querySelector( '.ambh-cff-current-image__change' ) : null;

			var hasExistingImage = currentImagePanel !== null;

			if ( ! imageDrop ) return { getBlob: function () { return null; }, hasExistingImage: false };

			var cropOffsetX = 0, cropOffsetY = 0, cropNatW = 0, cropNatH = 0;
			var cropRenderW = 0, cropRenderH = 0;
			var baseScale = 1, cropScale = 1;
			var isDragging = false, dragStartX = 0, dragStartY = 0;
			var hasCropImage = false, lastPinchDist = 0;

			imageDrop.addEventListener( 'click', function () { imageInput.click(); } );
			imageDrop.addEventListener( 'keydown', function ( e ) {
				if ( e.key === 'Enter' || e.key === ' ' ) { e.preventDefault(); imageInput.click(); }
			} );
			imageDrop.addEventListener( 'dragover', function ( e ) {
				e.preventDefault(); imageDrop.classList.add( 'is-drag-over' );
			} );
			imageDrop.addEventListener( 'dragleave', function () {
				imageDrop.classList.remove( 'is-drag-over' );
			} );
			imageDrop.addEventListener( 'drop', function ( e ) {
				e.preventDefault(); imageDrop.classList.remove( 'is-drag-over' );
				var file = e.dataTransfer && e.dataTransfer.files[0];
				if ( file && file.type.startsWith( 'image/' ) ) openCropper( file );
			} );
			imageInput.addEventListener( 'change', function () {
				var file = imageInput.files[0];
				if ( file ) { openCropper( file ); imageInput.value = ''; }
			} );

			function openCropper( file ) {
				var reader = new FileReader();
				reader.onload = function ( ev ) {
					cropImgEl.onload = function () {
						cropNatW = cropImgEl.naturalWidth;
						cropNatH = cropImgEl.naturalHeight;
						var viewW = imageViewport.clientWidth;
						var viewH = imageViewport.clientHeight;
						baseScale   = Math.max( viewW / cropNatW, viewH / cropNatH );
						cropScale   = 1;
						cropRenderW = Math.round( cropNatW * baseScale );
						cropRenderH = Math.round( cropNatH * baseScale );
						cropOffsetX = Math.round( ( viewW - cropRenderW ) / 2 );
						cropOffsetY = Math.round( ( viewH - cropRenderH ) / 2 );
						cropImgEl.style.width  = cropRenderW + 'px';
						cropImgEl.style.height = cropRenderH + 'px';
						placeCropImg();
						hasCropImage = true;
					};
					cropImgEl.src    = ev.target.result;
					imageDrop.hidden = true;
					imageCrop.hidden = false;
					hasCropImage     = false;
				};
				reader.readAsDataURL( file );
			}

			function clampOffset() {
				var viewW = imageViewport.clientWidth;
				var viewH = imageViewport.clientHeight;
				cropOffsetX = Math.min( 0, Math.max( viewW - cropRenderW, cropOffsetX ) );
				cropOffsetY = Math.min( 0, Math.max( viewH - cropRenderH, cropOffsetY ) );
			}

			function placeCropImg() {
				cropImgEl.style.left = cropOffsetX + 'px';
				cropImgEl.style.top  = cropOffsetY + 'px';
			}

			imageViewport.addEventListener( 'mousedown', startDrag );
			imageViewport.addEventListener( 'touchstart', function ( e ) {
				if ( e.touches.length === 2 ) { lastPinchDist = getPinchDist( e ); e.preventDefault(); }
				else startDrag( e );
			}, { passive: false } );
			imageViewport.addEventListener( 'touchmove', function ( e ) {
				if ( e.touches.length === 2 ) {
					var dist = getPinchDist( e );
					var mid  = getPinchMid( e );
					var rect = imageViewport.getBoundingClientRect();
					applyZoom( cropScale * ( dist / lastPinchDist ), mid.x - rect.left, mid.y - rect.top );
					lastPinchDist = dist;
					e.preventDefault();
				} else { onDrag( e ); }
			}, { passive: false } );
			imageViewport.addEventListener( 'wheel', function ( e ) {
				e.preventDefault();
				var rect  = imageViewport.getBoundingClientRect();
				var delta = e.deltaY > 0 ? -0.15 : 0.15;
				applyZoom( cropScale + delta, e.clientX - rect.left, e.clientY - rect.top );
			}, { passive: false } );

			function startDrag( e ) {
				isDragging = true;
				var p = e.touches ? e.touches[0] : e;
				dragStartX = p.clientX - cropOffsetX;
				dragStartY = p.clientY - cropOffsetY;
				imageViewport.classList.add( 'is-dragging' );
				e.preventDefault();
				document.addEventListener( 'mousemove', onDrag );
				document.addEventListener( 'mouseup',   endDrag );
				document.addEventListener( 'touchend',  endDrag );
			}
			function onDrag( e ) {
				if ( ! isDragging ) return;
				var p = e.touches ? e.touches[0] : e;
				cropOffsetX = p.clientX - dragStartX;
				cropOffsetY = p.clientY - dragStartY;
				clampOffset(); placeCropImg(); e.preventDefault();
			}
			function endDrag() {
				isDragging = false;
				imageViewport.classList.remove( 'is-dragging' );
				document.removeEventListener( 'mousemove', onDrag );
				document.removeEventListener( 'mouseup',   endDrag );
				document.removeEventListener( 'touchend',  endDrag );
			}
			function applyZoom( newScale, vx, vy ) {
				newScale    = Math.max( 1, Math.min( 4, newScale ) );
				var ratio   = newScale / cropScale;
				cropScale   = newScale;
				cropRenderW = Math.round( cropNatW * baseScale * cropScale );
				cropRenderH = Math.round( cropNatH * baseScale * cropScale );
				cropImgEl.style.width  = cropRenderW + 'px';
				cropImgEl.style.height = cropRenderH + 'px';
				cropOffsetX = vx - ( vx - cropOffsetX ) * ratio;
				cropOffsetY = vy - ( vy - cropOffsetY ) * ratio;
				clampOffset(); placeCropImg();
			}
			function getPinchDist( e ) {
				var dx = e.touches[0].clientX - e.touches[1].clientX;
				var dy = e.touches[0].clientY - e.touches[1].clientY;
				return Math.sqrt( dx * dx + dy * dy );
			}
			function getPinchMid( e ) {
				return { x: ( e.touches[0].clientX + e.touches[1].clientX ) / 2, y: ( e.touches[0].clientY + e.touches[1].clientY ) / 2 };
			}

			if ( imageChange ) {
				imageChange.addEventListener( 'click', function () {
					cropImgEl.src = ''; hasCropImage = false;
					imageDrop.hidden = false; imageCrop.hidden = true;
					imageInput.click();
				} );
			}
			if ( currentImageChange ) {
				currentImageChange.addEventListener( 'click', function () {
					hasExistingImage = false;
					currentImagePanel.hidden = true;
					imageDrop.hidden = false;
					imageInput.click();
				} );
			}

			function getBlob() {
				if ( ! hasCropImage ) return null;
				var viewW = imageViewport.clientWidth;
				var viewH = imageViewport.clientHeight;
				var scale = cropRenderW / cropNatW;
				var sx = -cropOffsetX / scale, sy = -cropOffsetY / scale;
				var sw =  viewW       / scale, sh =  viewH       / scale;
				cropCanvas.width  = viewW;
				cropCanvas.height = viewH;
				cropCanvas.getContext( '2d' ).drawImage( cropImgEl, sx, sy, sw, sh, 0, 0, viewW, viewH );
				var dataURL = cropCanvas.toDataURL( 'image/jpeg', 0.85 );
				var parts   = dataURL.split( ',' );
				var binary  = atob( parts[1] );
				var bytes   = new Uint8Array( binary.length );
				for ( var i = 0; i < binary.length; i++ ) bytes[ i ] = binary.charCodeAt( i );
				return new Blob( [ bytes ], { type: 'image/jpeg' } );
			}

			return { getBlob: getBlob, hasExistingImage: hasExistingImage };
		}

		// -------------------------------------------------------------------------
		// Upload image to a fundraiser
		// -------------------------------------------------------------------------

		function uploadImage( fundraiserId, blob ) {
			if ( ! blob || ! fundraiserId ) return Promise.resolve();
			var fd = new FormData();
			fd.append( 'file', blob, 'fundraiser-image.jpg' );
			return fetch( apiBase() + '/ambassadorhub/v1/fundraisers/' + fundraiserId + '/image', {
				method:  'POST',
				headers: { 'X-WP-Nonce': nonce },
				body:    fd,
			} ).catch( function () {} );
		}

		// -------------------------------------------------------------------------
		// Main form submission (team / individual / fundraiser / edit)
		// -------------------------------------------------------------------------

		var mainForm = dataStep ? dataStep.querySelector( '.ambh-cff-form' ) : null;
		if ( mainForm ) {
			mainForm.addEventListener( 'submit', function ( event ) {
				event.preventDefault();

				var titleInput   = mainForm.querySelector( '[name="title"]' );
				var contentInput = mainForm.querySelector( '[name="content"]' );
				var targetInput  = mainForm.querySelector( '[name="target_amount"]' );
				var isTargetRequired = targetInput && targetInput.hasAttribute( 'required' );

				// Validate text fields.
				var hasError = false;
				hasError = validateRequired( titleInput,   wrapper.dataset.i18nTitleRequired   || 'Please enter a title.' )       || hasError;
				hasError = validateRequired( contentInput, wrapper.dataset.i18nContentRequired || 'Please enter a description.' ) || hasError;
				if ( hasError ) {
					( mainForm.querySelector( '[name="title"]' ) || mainForm ).focus();
					return;
				}

				// Validate target amount (only when required).
				if ( isTargetRequired ) {
					var parseAmt  = window.ambhCurrencyInput && window.ambhCurrencyInput.parseAmount ? window.ambhCurrencyInput.parseAmount : parseFloat;
					var targetVal = parseAmt( targetInput.value );
					if ( isNaN( targetVal ) || targetVal <= 0 ) {
						fieldError( targetInput, wrapper.dataset.i18nTargetRequired || 'Please enter a target amount.' );
						targetInput.focus();
						return;
					}
					if ( targetVal > 10000000 ) {
						fieldError( targetInput, wrapper.dataset.i18nTargetMax || 'Target amount must be less than 10,000,000.' );
						targetInput.focus();
						return;
					}
				}

				// Image required for team and fundraiser types; optional for individual.
				var imageBlob       = mainImagePicker ? mainImagePicker.getBlob() : null;
				var imageIsRequired = selectedType !== 'individual';
				if ( imageIsRequired && ! imageBlob && ! hasExistingImage ) {
					setStatus( dataStep, wrapper.dataset.i18nImageRequired || 'Please select an image.' );
					return;
				}

				var submitBtn = mainForm.querySelector( '.ambh-cff-submit' );
				if ( submitBtn ) submitBtn.disabled = true;
				clearFieldErrors( mainForm );
				setStatus( dataStep, '' );

				var data = {};
				new FormData( mainForm ).forEach( function ( value, key ) { data[ key ] = value; } );
				data = wp.hooks.applyFilters( 'ambh.fundraiser.submitData', data, mainForm );

				if ( editMode ) {
					// ── Edit: PATCH /fundraisers/{id} ────────────────────────
					var editFields = ( window.ambhFundraiserFormConfig && window.ambhFundraiserFormConfig.editFields ) || [];
					fetch( apiBase() + '/ambassadorhub/v1/fundraisers/' + editId, {
						method:  'PATCH',
						headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': nonce },
						body:    JSON.stringify( pickFields( data, editFields ) ),
					} )
					.then( jsonResult )
					.then( function ( result ) {
						if ( ! result.ok ) {
							setStatus( dataStep, ( result.body && result.body.message ) || 'An error occurred.' );
							if ( submitBtn ) submitBtn.disabled = false;
							return;
						}
						uploadImage( editId, imageBlob ).finally( function () {
							if ( editPermalink ) window.location.href = editPermalink;
						} );
					} )
					.catch( function () {
						setStatus( dataStep, 'Network error. Please try again.' );
						if ( submitBtn ) submitBtn.disabled = false;
					} );

				} else {
					// ── Create ───────────────────────────────────────────────
					// Individuals and teams are always nested; fall back to rootId when
				// no explicit parentId is set (e.g. form placed on a root-team page).
				data.parent_id         = parentId > 0 ? parentId : rootId || 0;
					data.is_group          = data['is_group'] === '1' || data['is_group'] === 1;
					data.allow_nested_team = data['allow_nested_team'] === '1' || data['allow_nested_team'] === 1;

					// Ensure target_amount is numeric (0 = no goal).
					var parseAmt2   = window.ambhCurrencyInput && window.ambhCurrencyInput.parseAmount ? window.ambhCurrencyInput.parseAmount : parseFloat;
					data.target_amount = parseAmt2( data.target_amount || '0' ) || 0;

					var createFields = ( window.ambhFundraiserFormConfig && window.ambhFundraiserFormConfig.createFields ) || [];
					fetch( apiBase() + '/ambassadorhub/v1/fundraisers', {
						method:  'POST',
						headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': nonce },
						body:    JSON.stringify( pickFields( data, createFields ) ),
					} )
					.then( jsonResult )
					.then( function ( result ) {
						if ( ! result.ok ) {
							setStatus( dataStep, ( result.body && result.body.message ) || 'An error occurred. Please try again.' );
							if ( submitBtn ) submitBtn.disabled = false;
							return;
						}

						clearDraft();
						var redirectUrl  = result.body.url || '';
						var fundraiserId = result.body.id  || 0;

						uploadImage( fundraiserId, imageBlob ).finally( function () {
							if ( selectedType === 'team' && personalJoinStep ) {
								// Team created — show personal-join step.
								createdTeamId  = fundraiserId;
								createdTeamUrl = redirectUrl;
								showPersonalJoinStep( result.body.title || '', 'team' );
							} else if ( selectedType === 'fundraiser' && postFundraiserStep ) {
								// Fundraiser created — ask what to do next.
								createdFundraiserId  = fundraiserId;
								createdFundraiserUrl = redirectUrl;
								// Children of the new fundraiser use it as their parent.
								parentId = fundraiserId;
								wrapper.dataset.parentId = String( parentId );
								// When a new root was just created, update rootId so subsequent
								// children (team / individual) reference it correctly.
								if ( rootId === 0 ) {
									rootId = fundraiserId;
									wrapper.dataset.rootId = String( rootId );
								}
								// Reflect the actual teams setting (may differ from initial context).
								if ( typeof result.body.teams_enabled !== 'undefined' ) {
									nestingEnabled = !! result.body.teams_enabled;
									wrapper.dataset.nestingEnabled = nestingEnabled ? '1' : '0';
								}
								showPostFundraiserStep( result.body.title || '' );
							} else {
								if ( redirectUrl ) window.location.href = redirectUrl;
							}
						} );
					} )
					.catch( function () {
						setStatus( dataStep, 'Network error. Please try again.' );
						if ( submitBtn ) submitBtn.disabled = false;
					} );
				}
			} );
		}

		// -------------------------------------------------------------------------
		// Personal-join step (second step of team flow)
		// -------------------------------------------------------------------------

		// mode: 'team' (default) or 'fundraiser'
		function showPersonalJoinStep( parentName, mode ) {
			if ( ! personalJoinStep ) return;

			var isTeamMode = mode !== 'fundraiser';

			// Show/hide the team-created banner (not needed when coming from post-fundraiser step).
			var banner = personalJoinStep.querySelector( '[data-join-banner]' );
			if ( banner ) {
				banner.hidden = ! isTeamMode;
				if ( isTeamMode ) {
					var nameEl = banner.querySelector( '[data-created-team-name]' );
					if ( nameEl ) nameEl.textContent = parentName ? ' ' + parentName : '';
				}
			}

			// Swap submit/skip labels based on mode.
			var submitBtn = personalJoinStep.querySelector( '[data-join-submit]' );
			var skipBtnEl = personalJoinStep.querySelector( '[data-join-skip]' );
			if ( submitBtn ) submitBtn.textContent = isTeamMode
				? ( wrapper.dataset.i18nJoinTeam || 'Join the team' )
				: ( wrapper.dataset.i18nJoinFundraiser || 'Join this fundraiser' );
			if ( skipBtnEl ) skipBtnEl.textContent = isTeamMode
				? ( wrapper.dataset.i18nSkipTeam || 'Skip — go to team page' )
				: ( wrapper.dataset.i18nSkipFundraiser || 'Skip — go to fundraiser page' );

			// Pre-fill display name.
			var titleInput = personalJoinStep.querySelector( '[name="title"]' );
			if ( titleInput && userDisplayName ) titleInput.value = userDisplayName;
			// Reset motivation field.
			var contentInput = personalJoinStep.querySelector( '[name="content"]' );
			if ( contentInput ) contentInput.value = '';

			showStep( personalJoinStep );
		}

		// -------------------------------------------------------------------------
		// Post-fundraiser step (shown after sub-fundraiser creation)
		// -------------------------------------------------------------------------

		function showPostFundraiserStep( fundraiserName ) {
			if ( ! postFundraiserStep ) return;
			inPostFundraiserFlow = true;

			var nameEl = postFundraiserStep.querySelector( '[data-created-fundraiser-name]' );
			if ( nameEl ) nameEl.textContent = fundraiserName ? ' ' + fundraiserName : '';

			// Show "Create a group" only when root allows teams.
			var teamAfterItem = postFundraiserStep.querySelector( '.ambh-cff-type-item--create-team-after' );
			if ( teamAfterItem ) teamAfterItem.hidden = ! nestingEnabled;

			showStep( postFundraiserStep );
		}

		if ( postFundraiserStep ) {
			// "Join this fundraiser" — create personal page under the new fundraiser.
			var joinFundraiserBtn = postFundraiserStep.querySelector( '.ambh-cff-join-fundraiser' );
			if ( joinFundraiserBtn ) {
				joinFundraiserBtn.addEventListener( 'click', function () {
					// createdTeamId / createdTeamUrl used by personalJoinStep submit + skip.
					createdTeamId  = createdFundraiserId;
					createdTeamUrl = createdFundraiserUrl;
					showPersonalJoinStep( null, 'fundraiser' );
				} );
			}

			// "Create a group" — open team form under the new fundraiser.
			var createTeamAfterBtn = postFundraiserStep.querySelector( '.ambh-cff-create-team-after' );
			if ( createTeamAfterBtn ) {
				createTeamAfterBtn.addEventListener( 'click', function () {
					selectedType = 'team';
					setIsGroup( true );
					syncAllowNested( true );
					applyTypeLabels( 'team' );
					// Clear the main form so the team fields start fresh.
					var mainFormEl = dataStep ? dataStep.querySelector( '.ambh-cff-form' ) : null;
					if ( mainFormEl ) mainFormEl.reset();
					showStep( dataStep );
					loadFormFieldsFragment( 'team' );
				} );
			}

			// "Skip" — go straight to the fundraiser page.
			var skipFundraiserBtn = postFundraiserStep.querySelector( '.ambh-cff-skip-fundraiser' );
			if ( skipFundraiserBtn ) {
				skipFundraiserBtn.addEventListener( 'click', function () {
					if ( createdFundraiserUrl ) window.location.href = createdFundraiserUrl;
				} );
			}
		}

		var personalForm = personalJoinStep ? personalJoinStep.querySelector( '.ambh-cff-personal-form' ) : null;

		// Skip — go directly to the team / fundraiser page.
		var skipBtn = personalJoinStep ? personalJoinStep.querySelector( '.ambh-cff-skip-personal' ) : null;
		if ( skipBtn ) {
			skipBtn.addEventListener( 'click', function () {
				if ( createdTeamUrl ) window.location.href = createdTeamUrl;
			} );
		}

		if ( personalForm ) {
			personalForm.addEventListener( 'submit', function ( event ) {
				event.preventDefault();

				var titleInput   = personalForm.querySelector( '[name="title"]' );
				var contentInput = personalForm.querySelector( '[name="content"]' );
				var targetInput  = personalForm.querySelector( '[name="target_amount"]' );

				var hasError = false;
				hasError = validateRequired( titleInput,   wrapper.dataset.i18nTitleRequired   || 'Please enter a title.' )       || hasError;
				hasError = validateRequired( contentInput, wrapper.dataset.i18nContentRequired || 'Please enter a description.' ) || hasError;
				if ( hasError ) return;

				// Target amount is optional here; validate range if provided.
				var parseAmt3 = window.ambhCurrencyInput && window.ambhCurrencyInput.parseAmount ? window.ambhCurrencyInput.parseAmount : parseFloat;
				var targetVal = targetInput && targetInput.value ? parseAmt3( targetInput.value ) : 0;
				if ( targetInput && targetInput.value && ( isNaN( targetVal ) || targetVal < 0 ) ) {
					fieldError( targetInput, 'Please enter a valid amount.' );
					targetInput.focus();
					return;
				}

				var submitBtn = personalForm.querySelector( '.ambh-cff-submit-personal' );
				if ( submitBtn ) submitBtn.disabled = true;
				clearFieldErrors( personalForm );
				setStatus( personalJoinStep, '' );

				var data = {
					title:         ( titleInput   && titleInput.value.trim() )   || '',
					content:       ( contentInput && contentInput.value.trim() ) || '',
					target_amount: targetVal || 0,
					parent_id:     createdTeamId,
					is_group:      false,
					allow_nested_team: false,
				};
				data = wp.hooks.applyFilters( 'ambh.fundraiser.submitData', data, personalForm );

				var createFields = ( window.ambhFundraiserFormConfig && window.ambhFundraiserFormConfig.createFields ) || [];
				fetch( apiBase() + '/ambassadorhub/v1/fundraisers', {
					method:  'POST',
					headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': nonce },
					body:    JSON.stringify( pickFields( data, createFields ) ),
				} )
				.then( jsonResult )
				.then( function ( result ) {
					if ( ! result.ok ) {
						setStatus( personalJoinStep, ( result.body && result.body.message ) || 'An error occurred. Please try again.' );
						if ( submitBtn ) submitBtn.disabled = false;
						return;
					}
					var redirectUrl = result.body.url || createdTeamUrl || '';
					if ( redirectUrl ) window.location.href = redirectUrl;
				} )
				.catch( function () {
					setStatus( personalJoinStep, 'Network error. Please try again.' );
					if ( submitBtn ) submitBtn.disabled = false;
				} );
			} );
		}

		// -------------------------------------------------------------------------
		// Validation helpers
		// -------------------------------------------------------------------------

		function validateRequired( input, message ) {
			if ( ! input ) return false;
			if ( ! input.value.trim() ) {
				fieldError( input, message );
				return true;
			}
			fieldError( input, '' );
			return false;
		}

		function fieldError( input, message ) {
			var errorEl = input ? input.closest( '.ambh-cff-field' ) : null;
			errorEl = errorEl ? errorEl.querySelector( '.ambh-cff-field__error' ) : null;
			if ( errorEl ) errorEl.textContent = message;
		}

		function clearFieldErrors( form ) {
			if ( ! form ) return;
			form.querySelectorAll( '.ambh-cff-field__error' ).forEach( function ( el ) {
				el.textContent = '';
			} );
		}

		// -------------------------------------------------------------------------
		// Fetch helper
		// -------------------------------------------------------------------------

		function jsonResult( response ) {
			return response.json().then( function ( body ) {
				return { ok: response.ok, body: body };
			} );
		}
	}

} )();
