/**
 * GoValid Form — public form handler.
 *
 * Vanilla JS (no jQuery). Handles validation, conditional logic,
 * drag-drop file uploads, camera/selfie capture, and AJAX submission.
 *
 * @package GoValid_QR
 */

/* global govalidForm */
(function () {
	'use strict';

	document.addEventListener('DOMContentLoaded', function () {
		var wraps = document.querySelectorAll('.govalid-pf-wrap');
		wraps.forEach(function (wrap) {
			initForm(wrap);
		});
	});

	function initForm(wrap) {
		var formId  = wrap.getAttribute('data-form-id');
		var successMsg = wrap.getAttribute('data-success-msg') || 'Thank you!';
		var form    = wrap.querySelector('.govalid-pf-form');
		var submit  = wrap.querySelector('.govalid-pf-submit');
		var msgEl   = wrap.querySelector('.govalid-pf-message');

		if (!form || !formId) return;

		// Apply conditional logic.
		applyConditionalLogic(wrap);

		// Listen for changes to re-evaluate conditional logic.
		form.addEventListener('input', function () {
			applyConditionalLogic(wrap);
		});
		form.addEventListener('change', function () {
			applyConditionalLogic(wrap);
		});

		// Initialize camera/selfie fields.
		initCameraFields(wrap);

		// Initialize drag-drop file zones.
		initDropzones(wrap);

		// Initialize rating/star fields.
		initRatingFields(wrap);

		// Initialize signature fields.
		initSignatureFields(wrap);

		// Initialize multi-step navigation.
		initMultiStep(wrap);

		// Submit handler.
		form.addEventListener('submit', function (e) {
			e.preventDefault();

			// Clear previous errors.
			wrap.querySelectorAll('.govalid-pf-field-error').forEach(function (el) {
				el.style.display = 'none';
				el.textContent = '';
			});
			wrap.querySelectorAll('.govalid-pf-input--error').forEach(function (el) {
				el.classList.remove('govalid-pf-input--error');
			});
			msgEl.style.display = 'none';

			// Collect values.
			var fields = wrap.querySelectorAll('.govalid-pf-field');
			var values = [];
			var valid  = true;

			fields.forEach(function (fieldEl) {
				var fieldId = fieldEl.getAttribute('data-field-id');
				if (!fieldId) return;

				// Skip hidden (conditional) fields.
				if (fieldEl.classList.contains('govalid-pf-field--hidden')) return;

				var type  = fieldEl.className.match(/govalid-pf-field--(\w+)/);
				type = type ? type[1] : 'text';

				if (type === 'heading' || type === 'separator' || type === 'image_banner' || type === 'page_break') return;

				var value = getFieldValue(fieldEl, fieldId, type);

				// Validate required.
				var input = fieldEl.querySelector('.govalid-pf-input, input[type="radio"], input[type="checkbox"], .govalid-pf-camera-file, .govalid-pf-signature-file');
				var isRequired = false;
				if (input) {
					isRequired = input.hasAttribute('required');
				}

				if (isRequired && (!value || !value.toString().trim())) {
					showFieldError(fieldEl, 'This field is required.');
					valid = false;
					return;
				}

				// Type validation.
				if (value && type === 'email') {
					var emailRe = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
					if (!emailRe.test(value)) {
						showFieldError(fieldEl, 'Please enter a valid email address.');
						valid = false;
						return;
					}
				}

				if (value && type === 'number') {
					if (isNaN(Number(value))) {
						showFieldError(fieldEl, 'Please enter a valid number.');
						valid = false;
						return;
					}
				}

				if (value && type === 'phone') {
					var phoneRe = /^[\d+\-() ]{3,20}$/;
					if (!phoneRe.test(value)) {
						showFieldError(fieldEl, 'Please enter a valid phone number.');
						valid = false;
						return;
					}
				}

				if (value && type === 'url') {
					try {
						new URL(value);
					} catch (_) {
						showFieldError(fieldEl, 'Please enter a valid URL.');
						valid = false;
						return;
					}
				}

				// Enforce max length to prevent abuse.
				if (value && typeof value === 'string') {
					var maxLen = (type === 'textarea') ? 5000 : 1000;
					if (value.length > maxLen) {
						showFieldError(fieldEl, 'Input is too long (max ' + maxLen + ' characters).');
						valid = false;
						return;
					}
				}

				// Sanitize: strip HTML tags from text fields (basic XSS prevention).
				if (value && typeof value === 'string' && type !== 'file' && type !== 'camera') {
					value = value.replace(/<[^>]*>/g, '');
				}

				values.push({
					field_id: parseInt(fieldId, 10),
					value:    value
				});
			});

			if (!valid) return;

			// Get honeypot.
			var hp = form.querySelector('input[name="_govalid_hp"]');

			// Collect file inputs that have files selected.
			var fileFields = [];
			wrap.querySelectorAll('.govalid-pf-field').forEach(function (fieldEl) {
				var fieldId = fieldEl.getAttribute('data-field-id');
				if (!fieldId) return;
				if (fieldEl.classList.contains('govalid-pf-field--hidden')) return;
				var fileInput = fieldEl.querySelector('input[type="file"]');
				if (fileInput && fileInput.files.length) {
					fileFields.push({ fieldId: fieldId, file: fileInput.files[0] });
				} else if (fileInput && fileInput._cameraBlob) {
					// Fallback for browsers that don't support DataTransfer.
					var blobFile = new File(
						[fileInput._cameraBlob],
						fileInput._cameraFileName || 'selfie.jpg',
						{ type: 'image/jpeg' }
					);
					fileFields.push({ fieldId: fieldId, file: blobFile });
				}
			});

			// Build request body — use FormData when files are present.
			var fetchOptions = {
				method: 'POST',
				headers: { 'X-WP-Nonce': govalidForm.nonce }
			};

			if (fileFields.length) {
				var fd = new FormData();
				fd.append('values', JSON.stringify(values));
				fd.append('_govalid_hp', hp ? hp.value : '');
				fileFields.forEach(function (ff) {
					fd.append('file_' + ff.fieldId, ff.file);
				});
				fetchOptions.body = fd;
				// Do NOT set Content-Type — browser auto-sets multipart boundary.
			} else {
				fetchOptions.headers['Content-Type'] = 'application/json';
				fetchOptions.body = JSON.stringify({
					values: values,
					_govalid_hp: hp ? hp.value : ''
				});
			}

			// Disable submit.
			submit.disabled = true;
			var origHTML = submit.innerHTML;
			submit.innerHTML = '<span>Submitting\u2026</span>';

			fetch(govalidForm.restUrl + 'public/forms/' + formId + '/submit', fetchOptions)
			.then(function (resp) { return resp.json(); })
			.then(function (data) {
				if (data.success) {
					msgEl.className = 'govalid-pf-message govalid-pf-message--success';
					msgEl.textContent = successMsg;
					msgEl.style.display = 'flex';
					form.reset();
					// Reset any dropzone previews.
					wrap.querySelectorAll('.govalid-pf-dropzone').forEach(function (dz) {
						resetDropzone(dz);
					});
					// Hide form fields after success.
					wrap.querySelectorAll('.govalid-pf-field, .govalid-pf-submit-wrap').forEach(function (el) {
						el.style.display = 'none';
					});
				} else {
					var errMsg = data.message || 'An error occurred.';
					// Try to highlight specific field.
					if (data.data && data.data.field_id) {
						var errField = wrap.querySelector('[data-field-id="' + data.data.field_id + '"]');
						if (errField) {
							showFieldError(errField, errMsg);
							submit.disabled = false;
							submit.innerHTML = origHTML;
							return;
						}
					}
					msgEl.className = 'govalid-pf-message govalid-pf-message--error';
					msgEl.textContent = errMsg;
					msgEl.style.display = 'flex';
				}
				submit.disabled = false;
				submit.innerHTML = origHTML;
			})
			.catch(function () {
				msgEl.className = 'govalid-pf-message govalid-pf-message--error';
				msgEl.textContent = 'Network error. Please try again.';
				msgEl.style.display = 'flex';
				submit.disabled = false;
				submit.innerHTML = origHTML;
			});
		});
	}

	/* ------------------------------------------------------------------
	 * Get field value
	 * ----------------------------------------------------------------*/

	function getFieldValue(fieldEl, fieldId, type) {
		if (type === 'radio' || type === 'gender') {
			var checked = fieldEl.querySelector('input[type="radio"]:checked');
			return checked ? checked.value : '';
		}
		if (type === 'checkbox') {
			var checked = fieldEl.querySelectorAll('input[type="checkbox"]:checked');
			var vals = [];
			checked.forEach(function (cb) { vals.push(cb.value); });
			return vals.join(', ');
		}
		if (type === 'file') {
			var fileInput = fieldEl.querySelector('input[type="file"]');
			return fileInput && fileInput.files.length ? fileInput.files[0].name : '';
		}
		if (type === 'camera') {
			var camFile = fieldEl.querySelector('.govalid-pf-camera-file');
			if (camFile && camFile.files.length) return camFile.files[0].name;
			if (camFile && camFile._cameraFileName) return camFile._cameraFileName;
			return '';
		}
		if (type === 'rating') {
			var ratingInput = fieldEl.querySelector('.govalid-pf-rating-value');
			return ratingInput ? ratingInput.value : '';
		}
		if (type === 'signature') {
			var sigFile = fieldEl.querySelector('.govalid-pf-signature-file');
			if (sigFile && sigFile.files.length) return sigFile.files[0].name;
			if (sigFile && sigFile._cameraFileName) return sigFile._cameraFileName;
			return '';
		}
		var input = fieldEl.querySelector('.govalid-pf-input');
		return input ? input.value : '';
	}

	/* ------------------------------------------------------------------
	 * Validation error display
	 * ----------------------------------------------------------------*/

	function showFieldError(fieldEl, msg) {
		var errEl = fieldEl.querySelector('.govalid-pf-field-error');
		if (errEl) {
			errEl.textContent = msg;
			errEl.style.display = 'flex';
		}
		var input = fieldEl.querySelector('.govalid-pf-input');
		if (input) {
			input.classList.add('govalid-pf-input--error');
		}
	}

	/* ------------------------------------------------------------------
	 * Rating / Star fields
	 * ----------------------------------------------------------------*/

	function initRatingFields(wrap) {
		var ratings = wrap.querySelectorAll('.govalid-pf-rating');
		ratings.forEach(function (ratingEl) {
			var stars = ratingEl.querySelectorAll('.govalid-pf-star');
			var hiddenInput = ratingEl.querySelector('.govalid-pf-rating-value');

			stars.forEach(function (star) {
				star.addEventListener('click', function () {
					var val = parseInt(star.getAttribute('data-value'), 10);
					hiddenInput.value = val;
					updateStars(ratingEl, val);
				});
				star.addEventListener('mouseenter', function () {
					var val = parseInt(star.getAttribute('data-value'), 10);
					updateStars(ratingEl, val, true);
				});
			});

			ratingEl.addEventListener('mouseleave', function () {
				var current = parseInt(hiddenInput.value, 10) || 0;
				updateStars(ratingEl, current);
			});
		});
	}

	function updateStars(ratingEl, value, isHover) {
		var stars = ratingEl.querySelectorAll('.govalid-pf-star');
		stars.forEach(function (star) {
			var starVal = parseInt(star.getAttribute('data-value'), 10);
			if (starVal <= value) {
				star.classList.add('govalid-pf-star--active');
				if (isHover) star.classList.add('govalid-pf-star--hover');
				else star.classList.remove('govalid-pf-star--hover');
			} else {
				star.classList.remove('govalid-pf-star--active');
				star.classList.remove('govalid-pf-star--hover');
			}
		});
	}

	/* ------------------------------------------------------------------
	 * Signature Canvas fields
	 * ----------------------------------------------------------------*/

	function initSignatureFields(wrap) {
		var sigs = wrap.querySelectorAll('.govalid-pf-signature');
		sigs.forEach(function (sigEl) {
			var canvas    = sigEl.querySelector('.govalid-pf-signature-canvas');
			var clearBtn  = sigEl.querySelector('.govalid-pf-signature-clear');
			var fileInput = sigEl.querySelector('.govalid-pf-signature-file');
			if (!canvas || !fileInput) return;

			var ctx       = canvas.getContext('2d');
			var drawing   = false;
			var hasDrawn  = false;

			function resizeCanvas() {
				var rect = canvas.parentElement.getBoundingClientRect();
				var h = canvas.height;
				canvas.width = rect.width;
				canvas.height = h;
				ctx.strokeStyle = '#1e293b';
				ctx.lineWidth = 2.5;
				ctx.lineCap = 'round';
				ctx.lineJoin = 'round';
			}
			resizeCanvas();

			function getPos(e) {
				var rect = canvas.getBoundingClientRect();
				var touch = e.touches ? e.touches[0] : e;
				return {
					x: touch.clientX - rect.left,
					y: touch.clientY - rect.top
				};
			}

			function startDraw(e) {
				e.preventDefault();
				drawing = true;
				var pos = getPos(e);
				ctx.beginPath();
				ctx.moveTo(pos.x, pos.y);
			}

			function draw(e) {
				if (!drawing) return;
				e.preventDefault();
				var pos = getPos(e);
				ctx.lineTo(pos.x, pos.y);
				ctx.stroke();
				hasDrawn = true;
			}

			function endDraw(e) {
				if (!drawing) return;
				e.preventDefault();
				drawing = false;
				if (hasDrawn) {
					canvas.toBlob(function (blob) {
						if (!blob) return;
						var fileName = 'signature_' + Date.now() + '.png';
						var file = new File([blob], fileName, { type: 'image/png' });
						try {
							var dt = new DataTransfer();
							dt.items.add(file);
							fileInput.files = dt.files;
						} catch (err) {
							fileInput._cameraBlob = blob;
							fileInput._cameraFileName = fileName;
						}
					}, 'image/png');
				}
			}

			canvas.addEventListener('mousedown', startDraw);
			canvas.addEventListener('mousemove', draw);
			canvas.addEventListener('mouseup', endDraw);
			canvas.addEventListener('mouseleave', endDraw);

			canvas.addEventListener('touchstart', startDraw, { passive: false });
			canvas.addEventListener('touchmove', draw, { passive: false });
			canvas.addEventListener('touchend', endDraw);

			if (clearBtn) {
				clearBtn.addEventListener('click', function () {
					ctx.clearRect(0, 0, canvas.width, canvas.height);
					hasDrawn = false;
					try {
						var dt = new DataTransfer();
						fileInput.files = dt.files;
					} catch (err) {
						fileInput.value = '';
					}
					fileInput._cameraBlob = null;
					fileInput._cameraFileName = null;
				});
			}
		});
	}

	/* ------------------------------------------------------------------
	 * Multi-Step Form Navigation
	 * ----------------------------------------------------------------*/

	function initMultiStep(wrap) {
		var allSteps = wrap.querySelectorAll('.govalid-pf-step');
		if (allSteps.length <= 1) return; // Not multi-step.

		var currentStep = 0;
		var totalSteps  = allSteps.length;
		var prevBtn     = wrap.querySelector('.govalid-pf-step-prev');
		var nextBtn     = wrap.querySelector('.govalid-pf-step-next');
		var submitWrap  = wrap.querySelector('.govalid-pf-submit-wrap');

		if (nextBtn) {
			nextBtn.addEventListener('click', function () {
				if (!validateStep(wrap, currentStep)) return;
				currentStep++;
				showStep(wrap, currentStep, totalSteps, prevBtn, nextBtn, submitWrap);
			});
		}

		if (prevBtn) {
			prevBtn.addEventListener('click', function () {
				currentStep--;
				showStep(wrap, currentStep, totalSteps, prevBtn, nextBtn, submitWrap);
			});
		}
	}

	function showStep(wrap, step, total, prevBtn, nextBtn, submitWrap) {
		var steps = wrap.querySelectorAll('.govalid-pf-step');
		steps.forEach(function (el, i) {
			el.style.display = (i === step) ? '' : 'none';
		});

		// Update progress indicator.
		var progressSteps = wrap.querySelectorAll('.govalid-pf-progress-step');
		progressSteps.forEach(function (el, i) {
			el.classList.toggle('govalid-pf-progress-step--active', i === step);
			el.classList.toggle('govalid-pf-progress-step--completed', i < step);
		});

		// Show/hide prev/next/submit.
		if (prevBtn) prevBtn.style.display = step > 0 ? '' : 'none';
		if (nextBtn) nextBtn.style.display = step < total - 1 ? '' : 'none';
		if (submitWrap) submitWrap.style.display = step === total - 1 ? '' : 'none';

		// Scroll to top of form.
		wrap.scrollIntoView({ behavior: 'smooth', block: 'start' });
	}

	function validateStep(wrap, step) {
		var stepEl = wrap.querySelectorAll('.govalid-pf-step')[step];
		if (!stepEl) return true;

		var valid = true;

		// Clear previous errors in this step.
		stepEl.querySelectorAll('.govalid-pf-field-error').forEach(function (el) {
			el.style.display = 'none';
			el.textContent = '';
		});
		stepEl.querySelectorAll('.govalid-pf-input--error').forEach(function (el) {
			el.classList.remove('govalid-pf-input--error');
		});

		var fieldEls = stepEl.querySelectorAll('.govalid-pf-field');
		fieldEls.forEach(function (fieldEl) {
			var fieldId = fieldEl.getAttribute('data-field-id');
			if (!fieldId) return;
			if (fieldEl.classList.contains('govalid-pf-field--hidden')) return;

			var type = fieldEl.className.match(/govalid-pf-field--(\w+)/);
			type = type ? type[1] : 'text';
			if (type === 'heading' || type === 'separator' || type === 'image_banner' || type === 'page_break') return;

			var value = getFieldValue(fieldEl, fieldId, type);
			var input = fieldEl.querySelector('.govalid-pf-input, input[type="radio"], input[type="checkbox"], .govalid-pf-camera-file, .govalid-pf-signature-file');
			var isRequired = input && input.hasAttribute('required');

			if (isRequired && (!value || !value.toString().trim())) {
				showFieldError(fieldEl, 'This field is required.');
				valid = false;
			}
		});

		return valid;
	}

	/* ------------------------------------------------------------------
	 * Drag & Drop file zones
	 * ----------------------------------------------------------------*/

	function initDropzones(wrap) {
		var zones = wrap.querySelectorAll('.govalid-pf-dropzone');
		zones.forEach(function (zone) {
			var fileInput   = zone.querySelector('.govalid-pf-file-hidden');
			var innerEl     = zone.querySelector('.govalid-pf-dropzone-inner');
			var previewEl   = zone.querySelector('.govalid-pf-dropzone-preview');
			var filenameEl  = zone.querySelector('.govalid-pf-dropzone-filename');
			var removeBtn   = zone.querySelector('.govalid-pf-dropzone-remove');

			if (!fileInput || !innerEl || !previewEl) return;

			// Drag events.
			var dragCounter = 0;

			zone.addEventListener('dragenter', function (e) {
				e.preventDefault();
				e.stopPropagation();
				dragCounter++;
				zone.classList.add('govalid-pf-dropzone--dragover');
			});

			zone.addEventListener('dragleave', function (e) {
				e.preventDefault();
				e.stopPropagation();
				dragCounter--;
				if (dragCounter <= 0) {
					dragCounter = 0;
					zone.classList.remove('govalid-pf-dropzone--dragover');
				}
			});

			zone.addEventListener('dragover', function (e) {
				e.preventDefault();
				e.stopPropagation();
			});

			zone.addEventListener('drop', function (e) {
				e.preventDefault();
				e.stopPropagation();
				dragCounter = 0;
				zone.classList.remove('govalid-pf-dropzone--dragover');

				var files = e.dataTransfer.files;
				if (files.length) {
					var droppedFile = files[0];
					// Validate file type and size before accepting.
					if (!isAllowedFile(droppedFile)) {
						alert('File type not allowed. Accepted: JPG, PNG, WebP, PDF (max 5 MB).');
						return;
					}
					// Set file on input.
					try {
						var dt = new DataTransfer();
						dt.items.add(droppedFile);
						fileInput.files = dt.files;
					} catch (err) {
						// Fallback: Can't set files via DataTransfer in this browser.
					}
					showFilePreview(zone, droppedFile);
				}
			});

			// File input change.
			fileInput.addEventListener('change', function () {
				if (fileInput.files.length) {
					var selectedFile = fileInput.files[0];
					if (!isAllowedFile(selectedFile)) {
						alert('File type not allowed. Accepted: JPG, PNG, WebP, PDF (max 5 MB).');
						resetDropzone(zone);
						return;
					}
					showFilePreview(zone, selectedFile);
				}
			});

			// Remove button.
			if (removeBtn) {
				removeBtn.addEventListener('click', function (e) {
					e.preventDefault();
					e.stopPropagation();
					resetDropzone(zone);
				});
			}
		});
	}

	function isAllowedFile(file) {
		var allowedTypes = ['image/jpeg', 'image/png', 'image/webp', 'application/pdf'];
		var maxSize      = 5 * 1024 * 1024; // 5 MB.
		if (allowedTypes.indexOf(file.type) === -1) return false;
		if (file.size > maxSize) return false;
		// Check extension as well (belt and suspenders).
		var ext = file.name.split('.').pop().toLowerCase();
		var allowedExts = ['jpg', 'jpeg', 'png', 'webp', 'pdf'];
		if (allowedExts.indexOf(ext) === -1) return false;
		return true;
	}

	function showFilePreview(zone, file) {
		var innerEl    = zone.querySelector('.govalid-pf-dropzone-inner');
		var previewEl  = zone.querySelector('.govalid-pf-dropzone-preview');
		var filenameEl = zone.querySelector('.govalid-pf-dropzone-filename');

		if (filenameEl) filenameEl.textContent = file.name;
		if (innerEl) innerEl.style.display = 'none';
		if (previewEl) previewEl.style.display = 'block';
		zone.classList.add('govalid-pf-dropzone--has-file');
	}

	function resetDropzone(zone) {
		var fileInput  = zone.querySelector('.govalid-pf-file-hidden');
		var innerEl    = zone.querySelector('.govalid-pf-dropzone-inner');
		var previewEl  = zone.querySelector('.govalid-pf-dropzone-preview');

		if (fileInput) {
			try {
				var dt = new DataTransfer();
				fileInput.files = dt.files;
			} catch (e) {
				fileInput.value = '';
			}
		}
		if (innerEl) innerEl.style.display = 'flex';
		if (previewEl) previewEl.style.display = 'none';
		zone.classList.remove('govalid-pf-dropzone--has-file');
	}

	/* ------------------------------------------------------------------
	 * Camera / Selfie fields
	 * ----------------------------------------------------------------*/

	function initCameraFields(wrap) {
		var cameras = wrap.querySelectorAll('.govalid-pf-camera');
		cameras.forEach(function (camEl) {
			var openBtn   = camEl.querySelector('.govalid-pf-camera-open');
			var snapBtn   = camEl.querySelector('.govalid-pf-camera-snap');
			var retakeBtn = camEl.querySelector('.govalid-pf-camera-retake');
			var videoEl   = camEl.querySelector('.govalid-pf-camera-video');
			var canvasEl  = camEl.querySelector('.govalid-pf-camera-canvas');
			var previewEl = camEl.querySelector('.govalid-pf-camera-preview');
			var imgEl     = camEl.querySelector('.govalid-pf-camera-img');
			var fileInput = camEl.querySelector('.govalid-pf-camera-file');
			var stream    = null;

			if (!openBtn || !snapBtn || !videoEl || !canvasEl) return;

			// Open camera.
			openBtn.addEventListener('click', function () {
				if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
					alert('Camera is not supported on this browser or device.');
					return;
				}

				navigator.mediaDevices.getUserMedia({
					video: { facingMode: 'user', width: { ideal: 640 }, height: { ideal: 480 } },
					audio: false
				}).then(function (mediaStream) {
					stream = mediaStream;
					videoEl.srcObject = stream;
					videoEl.style.display = 'block';
					snapBtn.style.display = 'inline-flex';
					openBtn.style.display = 'none';
					previewEl.style.display = 'none';
				}).catch(function (err) {
					var msg = 'Unable to access camera.';
					if (err.name === 'NotAllowedError') {
						msg = 'Camera permission was denied. Please allow camera access and try again.';
					} else if (err.name === 'NotFoundError') {
						msg = 'No camera found on this device.';
					}
					alert(msg);
				});
			});

			// Capture photo.
			snapBtn.addEventListener('click', function () {
				if (!stream) return;

				// Set canvas size to match video.
				canvasEl.width  = videoEl.videoWidth;
				canvasEl.height = videoEl.videoHeight;

				var ctx = canvasEl.getContext('2d');
				// Mirror horizontally to match the mirrored video preview.
				ctx.translate(canvasEl.width, 0);
				ctx.scale(-1, 1);
				ctx.drawImage(videoEl, 0, 0, canvasEl.width, canvasEl.height);
				ctx.setTransform(1, 0, 0, 1, 0, 0);

				// Stop camera stream.
				stopStream();

				// Convert canvas to blob and assign to hidden file input.
				canvasEl.toBlob(function (blob) {
					if (!blob) return;

					var fileName = 'selfie_' + Date.now() + '.jpg';
					var file = new File([blob], fileName, { type: 'image/jpeg' });

					// Use DataTransfer to set file on the hidden input.
					try {
						var dt = new DataTransfer();
						dt.items.add(file);
						fileInput.files = dt.files;
					} catch (e) {
						// Fallback for older browsers: store blob on the element.
						fileInput._cameraBlob = blob;
						fileInput._cameraFileName = fileName;
					}

					// Show preview.
					imgEl.src = URL.createObjectURL(blob);
					previewEl.style.display = 'inline-block';
					videoEl.style.display   = 'none';
					snapBtn.style.display   = 'none';
					openBtn.style.display   = 'none';
				}, 'image/jpeg', 0.85);
			});

			// Retake photo.
			retakeBtn.addEventListener('click', function () {
				previewEl.style.display = 'none';
				imgEl.src = '';
				openBtn.style.display = 'inline-flex';
				snapBtn.style.display = 'none';
				videoEl.style.display = 'none';

				// Clear file input.
				try {
					var dt = new DataTransfer();
					fileInput.files = dt.files;
				} catch (e) {
					fileInput.value = '';
				}
				fileInput._cameraBlob = null;
				fileInput._cameraFileName = null;

				// Stop any lingering stream.
				stopStream();
			});

			function stopStream() {
				if (stream) {
					stream.getTracks().forEach(function (track) { track.stop(); });
					stream = null;
				}
				videoEl.srcObject = null;
			}
		});
	}

	/* ------------------------------------------------------------------
	 * Conditional logic
	 * ----------------------------------------------------------------*/

	function applyConditionalLogic(wrap) {
		var fields = wrap.querySelectorAll('.govalid-pf-field[data-cond]');

		fields.forEach(function (fieldEl) {
			var cond;
			try {
				cond = JSON.parse(fieldEl.getAttribute('data-cond'));
			} catch (e) {
				return;
			}

			if (!cond || !cond.enabled) return;

			var targetIndex = cond.field_index;
			if (targetIndex === undefined || targetIndex === null) return;

			// Find the target field by order in the form.
			var allFields = wrap.querySelectorAll('.govalid-pf-field[data-field-id]');
			var targetEl = allFields[targetIndex];
			if (!targetEl) return;

			var targetValue = getFieldValue(
				targetEl,
				targetEl.getAttribute('data-field-id'),
				getFieldType(targetEl)
			);

			var matches = false;
			switch (cond.operator) {
				case 'equals':
					matches = targetValue === cond.value;
					break;
				case 'not_equals':
					matches = targetValue !== cond.value;
					break;
				case 'contains':
					matches = targetValue.indexOf(cond.value) !== -1;
					break;
				case 'not_empty':
					matches = targetValue !== '' && targetValue !== null && targetValue !== undefined;
					break;
			}

			var shouldShow = (cond.action === 'show') ? matches : !matches;

			if (shouldShow) {
				fieldEl.classList.remove('govalid-pf-field--hidden');
			} else {
				fieldEl.classList.add('govalid-pf-field--hidden');
			}
		});
	}

	function getFieldType(fieldEl) {
		var match = fieldEl.className.match(/govalid-pf-field--(\w+)/);
		return match ? match[1] : 'text';
	}

})();
