/* Pay4All Form — ticket validation page client script.
 *
 * Loaded by Frontend\Endpoints::render_ticket_page() via a raw <script> tag
 * (the page is a standalone HTML response, not built through wp_head() /
 * wp_footer(), so wp_enqueue_script() would never run).
 *
 * Behaviour: tapping "Valider une autre place" opens a full-screen camera
 * viewfinder overlaid on the page. We stream the rear camera into a
 * <video> element and run the browser-native BarcodeDetector API every
 * animation frame. The moment a QR code is detected that resolves to a
 * /p4all-ticket/… URL on this same origin, we cleanly stop the camera and
 * redirect — no shutter tap, no "take photo" step, no second-guessing.
 *
 * Requirements for live scanning:
 *   - HTTPS (or localhost) — getUserMedia is gated by secure context
 *   - navigator.mediaDevices.getUserMedia present
 *   - window.BarcodeDetector present and reporting 'qr_code' support
 *     (Safari 17.4+ on iOS, Chrome on Android/desktop)
 *
 * If any of the above is missing we tell the user to fall back to their
 * native camera app (iOS detects QR codes natively in the Camera viewfinder
 * and shows the URL banner).
 *
 * Security: the decoded QR is only followed when its URL is on the same
 * origin AND its path starts with /p4all-ticket/ — stops a malicious printed
 * QR from redirecting the gatekeeper's phone off to an attacker domain.
 */
(function () {
	'use strict';

	var trigger = document.getElementById('p4all-next-scan');
	if (!trigger) { return; }

	function msg(key) { return trigger.getAttribute('data-msg-' + key) || ''; }

	function isSecure() {
		return window.isSecureContext === true
			|| window.location.protocol === 'https:'
			|| window.location.hostname === 'localhost'
			|| window.location.hostname === '127.0.0.1';
	}

	trigger.addEventListener('click', function (e) {
		e.preventDefault();
		startLiveScan();
	});

	function startLiveScan() {
		if (!isSecure()
			|| !navigator.mediaDevices
			|| !navigator.mediaDevices.getUserMedia
			|| typeof window.BarcodeDetector === 'undefined') {
			window.alert(msg('unsupported'));
			return;
		}

		// Verify QR is in the supported formats list — some browsers ship
		// the class as a stub but report no formats, in which case `detect()`
		// would just silently fail forever.
		var ready = (typeof window.BarcodeDetector.getSupportedFormats === 'function')
			? window.BarcodeDetector.getSupportedFormats()
			: Promise.resolve(['qr_code']);

		ready.then(function (formats) {
			if (formats.indexOf('qr_code') === -1) {
				window.alert(msg('unsupported'));
				return;
			}
			openOverlay();
		}).catch(function () {
			window.alert(msg('unsupported'));
		});
	}

	function openOverlay() {
		var detector;
		try { detector = new window.BarcodeDetector({ formats: ['qr_code'] }); }
		catch (e) { window.alert(msg('error')); return; }

		var overlay = document.createElement('div');
		overlay.id = 'p4all-scan-overlay';

		var video = document.createElement('video');
		video.setAttribute('playsinline', '');
		video.setAttribute('autoplay', '');
		video.muted = true;

		var hint = document.createElement('div');
		hint.className = 'p4all-scan-hint';
		hint.textContent = msg('hint');

		var closeBtn = document.createElement('button');
		closeBtn.type = 'button';
		closeBtn.className = 'p4all-scan-close';
		closeBtn.textContent = '✕';
		closeBtn.setAttribute('aria-label', msg('close'));

		overlay.appendChild(video);
		overlay.appendChild(hint);
		overlay.appendChild(closeBtn);
		document.body.appendChild(overlay);

		var stream = null;
		var rafId  = null;
		var done   = false;

		// Stops the camera and the per-frame detect loop but leaves the
		// overlay DOM in place. Used right before navigating to the next
		// ticket so the previous ticket's page never flashes through during
		// the brief redirect.
		function stopCamera() {
			done = true;
			if (rafId) { cancelAnimationFrame(rafId); rafId = null; }
			if (stream) {
				stream.getTracks().forEach(function (t) { t.stop(); });
				stream = null;
			}
		}

		// Full teardown: stop the camera AND remove the overlay. Used by the
		// close button when the gatekeeper cancels the scan.
		function dismiss() {
			stopCamera();
			if (overlay.parentNode) { overlay.parentNode.removeChild(overlay); }
		}

		closeBtn.addEventListener('click', dismiss);

		navigator.mediaDevices.getUserMedia({
			video: { facingMode: { ideal: 'environment' } },
			audio: false
		}).then(function (s) {
			stream = s;
			video.srcObject = s;
			return video.play();
		}).then(function () {
			rafId = requestAnimationFrame(tick);
		}).catch(function (err) {
			dismiss();
			// NotAllowedError = user denied; everything else = device/init issue.
			var key = (err && (err.name === 'NotAllowedError' || err.name === 'SecurityError'))
				? 'permission'
				: 'error';
			window.alert(msg(key));
		});

		function tick() {
			if (done) { return; }
			if (video.readyState < 2 /* HAVE_CURRENT_DATA */) {
				rafId = requestAnimationFrame(tick);
				return;
			}
			detector.detect(video).then(function (codes) {
				if (done) { return; }
				if (codes && codes.length > 0) {
					var rawUrl = codes[0].rawValue || '';
					var parsed = null;
					try { parsed = new URL(rawUrl, window.location.origin); } catch (e) {}
					if (parsed
						&& parsed.origin === window.location.origin
						&& parsed.pathname.indexOf('/p4all-ticket/') === 0) {
						// Stop the camera but keep the dark overlay in place,
						// swapping the viewfinder for a loading spinner so the
						// previous ticket's "OK" page never flashes through
						// while the next ticket's page is loading.
						stopCamera();
						overlay.innerHTML = '<div class="p4all-scan-loading" role="status" aria-label="' + (msg('loading') || '') + '"></div>';
						window.location.assign(parsed.href);
						return;
					}
					// Foreign QR — keep scanning rather than alerting, so the
					// gatekeeper can just sweep past unrelated codes.
				}
				rafId = requestAnimationFrame(tick);
			}).catch(function () {
				rafId = requestAnimationFrame(tick);
			});
		}
	}
})();
