/**
 * WDS Biometric 2FA — Registrazione dispositivo (Fase 2).
 * Dipende dai dati passati via wp_localize_script in spidyBiometricReg.
 */
( function () {
	'use strict';

	if ( typeof spidyBiometricReg === 'undefined' ) {
		return;
	}

	var button   = document.getElementById( 'spidy-wp-register-button' );
	var status   = document.getElementById( 'spidy-wp-status' );
	var list     = document.getElementById( 'spidy-wp-devices-list' );
	var noDevices = document.getElementById( 'spidy-wp-no-devices' );

	if ( ! button ) {
		return;
	}

	/**
	 * Converte una stringa base64url in ArrayBuffer, come richiesto
	 * dall'API navigator.credentials.create().
	 */
	function base64urlToBuffer( base64url ) {
		var padding = '='.repeat( ( 4 - ( base64url.length % 4 ) ) % 4 );
		var base64  = ( base64url + padding ).replace( /-/g, '+' ).replace( /_/g, '/' );
		var raw     = atob( base64 );
		var buffer  = new ArrayBuffer( raw.length );
		var array   = new Uint8Array( buffer );
		for ( var i = 0; i < raw.length; i++ ) {
			array[ i ] = raw.charCodeAt( i );
		}
		return buffer;
	}

	/**
	 * Converte un ArrayBuffer in stringa base64url, per rispedirlo al
	 * server via AJAX in un formato testuale sicuro.
	 */
	function bufferToBase64url( buffer ) {
		var bytes  = new Uint8Array( buffer );
		var binary = '';
		for ( var i = 0; i < bytes.byteLength; i++ ) {
			binary += String.fromCharCode( bytes[ i ] );
		}
		return btoa( binary ).replace( /\+/g, '-' ).replace( /\//g, '_' ).replace( /=+$/, '' );
	}

	function setStatus( message, isError ) {
		if ( ! status ) {
			return;
		}
		status.textContent = message;
		status.style.color = isError ? '#b32d2e' : '#1a7f37';
	}

	function addDeviceToList( credentialId, label ) {
		if ( noDevices ) {
			noDevices.style.display = 'none';
		}
		if ( ! list ) {
			list = document.createElement( 'ul' );
			list.id = 'spidy-wp-devices-list';
			list.style.margin = '0';
			var table = document.getElementById( 'spidy-wp-devices-table' );
			if ( table ) {
				var cell = table.querySelector( 'td' );
				cell.insertBefore( list, cell.firstChild );
			}
		}
		var li = document.createElement( 'li' );
		li.setAttribute( 'data-credential-id', credentialId );
		li.innerHTML =
			'<strong></strong> — <span class="description"></span> ' +
			'<button type="button" class="button-link spidy-wp-remove-device" data-credential-id="' +
			credentialId +
			'"></button>';
		li.querySelector( 'strong' ).textContent = label;
		li.querySelector( '.description' ).textContent = 'associato ora';
		li.querySelector( 'button' ).textContent = 'Rimuovi';
		list.appendChild( li );
		bindRemoveButton( li.querySelector( 'button' ) );
	}

	function bindRemoveButton( btn ) {
		btn.addEventListener( 'click', function () {
			if ( ! window.confirm( spidyBiometricReg.i18n.confirmRemove ) ) {
				return;
			}

			var credentialId = btn.getAttribute( 'data-credential-id' );
			var formData      = new FormData();
			formData.append( 'action', 'spidy_wp_remove_credential' );
			formData.append( 'nonce', spidyBiometricReg.nonce );
			formData.append( 'credential_id', credentialId );

			fetch( spidyBiometricReg.ajaxUrl, { method: 'POST', body: formData, credentials: 'same-origin' } )
				.then( function ( response ) { return response.json(); } )
				.then( function ( data ) {
					if ( data.success ) {
						btn.closest( 'li' ).remove();
					} else {
						window.alert( data.data && data.data.message ? data.data.message : spidyBiometricReg.i18n.genericError );
					}
				} )
				.catch( function () {
					window.alert( spidyBiometricReg.i18n.genericError );
				} );
		} );
	}

	// Aggancia i pulsanti "Rimuovi" già presenti nel markup PHP iniziale.
	document.querySelectorAll( '.spidy-wp-remove-device' ).forEach( bindRemoveButton );

	button.addEventListener( 'click', function () {
		if ( ! window.PublicKeyCredential ) {
			setStatus( spidyBiometricReg.i18n.notSupported, true );
			return;
		}

		setStatus( '…', false );
		button.disabled = true;

		// Challenge fresca proprio ora, non quella (potenzialmente vecchia
		// di minuti) generata al caricamento della pagina: evita "La
		// richiesta è scaduta" se l'utente impiega un po' a completare la
		// procedura (es. Chrome che offre più opzioni, account picker...).
		var freshFormData = new FormData();
		freshFormData.append( 'action', 'spidy_wp_get_fresh_challenge' );
		freshFormData.append( 'nonce', spidyBiometricReg.nonce );

		fetch( spidyBiometricReg.ajaxUrl, { method: 'POST', body: freshFormData, credentials: 'same-origin' } )
			.then( function ( res ) { return res.json(); } )
			.then( function ( data ) {
				if ( data.success && data.data && data.data.options ) {
					startCreate( data.data.options );
				} else {
					// Niente fallback silenzioso su una challenge vecchia:
					// mostriamo l'errore reale, altrimenti si ripresenta lo
					// stesso "richiesta scaduta" senza dire perché.
					button.disabled = false;
					setStatus( ( data.data && data.data.message ) ? data.data.message : spidyBiometricReg.i18n.genericError, true );
				}
			} )
			.catch( function () {
				button.disabled = false;
				setStatus( spidyBiometricReg.i18n.genericError, true );
			} );
	} );

	function startCreate( opts ) {
		var publicKey = {
			challenge: base64urlToBuffer( opts.challenge ),
			rp: opts.rp,
			user: {
				id: base64urlToBuffer( opts.user.id ),
				name: opts.user.name,
				displayName: opts.user.displayName,
			},
			pubKeyCredParams: opts.pubKeyCredParams,
			authenticatorSelection: opts.authenticatorSelection,
			timeout: opts.timeout,
			attestation: opts.attestation,
			excludeCredentials: ( opts.excludeCredentials || [] ).map( function ( cred ) {
				return {
					id: base64urlToBuffer( cred.id ),
					type: cred.type,
				};
			} ),
		};

		navigator.credentials
			.create( { publicKey: publicKey } )
			.then( function ( credential ) {
				askForLabel( function ( label ) {
					if ( label === null ) {
						setStatus( spidyBiometricReg.i18n.cancelled, true );
						button.disabled = false;
						return;
					}

					var response = credential.response;

					var formData = new FormData();
					formData.append( 'action', 'spidy_wp_save_credential' );
					formData.append( 'nonce', spidyBiometricReg.nonce );
					formData.append( 'credential_id', bufferToBase64url( credential.rawId ) );
					formData.append( 'public_key', bufferToBase64url( response.getPublicKey ? ( response.getPublicKey() || new ArrayBuffer( 0 ) ) : new ArrayBuffer( 0 ) ) );
					formData.append( 'attestation_object', bufferToBase64url( response.attestationObject ) );
					formData.append( 'client_data_json', bufferToBase64url( response.clientDataJSON ) );
					formData.append( 'label', label || 'Dispositivo senza nome' );
					formData.append(
						'transports',
						JSON.stringify( response.getTransports ? response.getTransports() : [] )
					);

					fetch( spidyBiometricReg.ajaxUrl, { method: 'POST', body: formData, credentials: 'same-origin' } )
						.then( function ( res ) { return res.json(); } )
						.then( function ( data ) {
							button.disabled = false;
							if ( data.success ) {
								setStatus( spidyBiometricReg.i18n.success, false );
								addDeviceToList( bufferToBase64url( credential.rawId ), label );
							} else {
								setStatus( data.data && data.data.message ? data.data.message : spidyBiometricReg.i18n.genericError, true );
							}
						} )
						.catch( function () {
							button.disabled = false;
							setStatus( spidyBiometricReg.i18n.genericError, true );
						} );
				} );
			} )
			.catch( function ( err ) {
				button.disabled = false;
				if ( err && err.name === 'NotAllowedError' ) {
					setStatus( spidyBiometricReg.i18n.cancelled, true );
				} else {
					setStatus( spidyBiometricReg.i18n.genericError, true );
				}
			} );
	}

	/**
	 * Sostituisce window.prompt() con un piccolo modulo inline nella
	 * pagina. window.prompt() qui era inaffidabile: molti browser (Chrome
	 * ed Edge in testa) bloccano o auto-annullano i dialoghi nativi
	 * (prompt/alert/confirm) quando arrivano subito dopo un'azione
	 * asincrona come la cerimonia WebAuthn — il "gesto utente" si
	 * considera già consumato da Windows Hello/Face ID/impronta. Un
	 * elemento DOM normale non ha questa limitazione.
	 */
	function askForLabel( callback ) {
		var overlay = document.createElement( 'div' );
		overlay.setAttribute(
			'style',
			'position:fixed;inset:0;background:rgba(0,0,0,.45);z-index:100000;' +
			'display:flex;align-items:center;justify-content:center;'
		);

		var box = document.createElement( 'div' );
		box.setAttribute(
			'style',
			'background:#fff;border-radius:6px;padding:24px;max-width:360px;' +
			'width:90%;box-shadow:0 4px 20px rgba(0,0,0,.25);font-family:inherit;'
		);

		var label = document.createElement( 'p' );
		label.textContent = spidyBiometricReg.i18n.labelPrompt;
		label.style.marginTop = '0';

		var input = document.createElement( 'input' );
		input.type = 'text';
		input.value = spidyBiometricReg.defaultLabel || 'Il mio dispositivo';
		input.style.width = '100%';
		input.style.marginBottom = '16px';

		var actions = document.createElement( 'div' );
		actions.style.textAlign = 'right';

		var cancelBtn = document.createElement( 'button' );
		cancelBtn.type = 'button';
		cancelBtn.className = 'button';
		cancelBtn.textContent = spidyBiometricReg.i18n.cancelButton || 'Annulla';
		cancelBtn.style.marginRight = '8px';

		var saveBtn = document.createElement( 'button' );
		saveBtn.type = 'button';
		saveBtn.className = 'button button-primary';
		saveBtn.textContent = spidyBiometricReg.i18n.saveButton || 'Salva';

		function close( result ) {
			document.body.removeChild( overlay );
			callback( result );
		}

		cancelBtn.addEventListener( 'click', function () {
			close( null );
		} );
		saveBtn.addEventListener( 'click', function () {
			close( input.value );
		} );
		input.addEventListener( 'keydown', function ( e ) {
			if ( e.key === 'Enter' ) {
				close( input.value );
			} else if ( e.key === 'Escape' ) {
				close( null );
			}
		} );

		actions.appendChild( cancelBtn );
		actions.appendChild( saveBtn );
		box.appendChild( label );
		box.appendChild( input );
		box.appendChild( actions );
		overlay.appendChild( box );
		document.body.appendChild( overlay );

		input.focus();
		input.select();
	}
} )();
