/**
 * Sign-in-by-phone form behaviour.
 *
 * Scoped per form instance, so a page may render more than one.
 */
( function () {
	'use strict';

	var config = window.textTangoOtp || {};
	var strings = config.i18n || {};

	/**
	 * POST to admin-ajax and resolve with the parsed payload.
	 *
	 * @param {string} action Ajax action name.
	 * @param {Object} data   Extra fields to send.
	 * @return {Promise<Object>} Resolves on success, rejects with a message.
	 */
	function request( action, data ) {
		var body = new window.FormData();

		body.append( 'action', action );
		body.append( 'nonce', config.nonce || '' );

		Object.keys( data || {} ).forEach( function ( key ) {
			body.append( key, data[ key ] );
		} );

		return window
			.fetch( config.ajaxUrl, {
				method: 'POST',
				credentials: 'same-origin',
				body: body,
			} )
			.then( function ( response ) {
				return response.json().catch( function () {
					throw new Error( strings.genericError );
				} );
			} )
			.then( function ( payload ) {
				if ( ! payload || ! payload.success ) {
					throw new Error(
						( payload && payload.data && payload.data.message ) ||
							strings.genericError
					);
				}

				return payload.data || {};
			} );
	}

	/**
	 * Wire up one form.
	 *
	 * @param {HTMLElement} root Form container.
	 */
	function setupForm( root ) {
		var phoneStep = root.querySelector( '[data-texttango-step="phone"]' );
		var codeStep = root.querySelector( '[data-texttango-step="code"]' );
		var phoneField = root.querySelector( '[data-texttango-phone]' );
		var codeField = root.querySelector( '[data-texttango-code]' );
		var rememberField = root.querySelector( '[data-texttango-remember]' );
		var redirectField = root.querySelector( '[data-texttango-redirect]' );
		var messageEl = root.querySelector( '[data-texttango-otp-message]' );

		/**
		 * Show a message, coloured by outcome.
		 *
		 * @param {string}  text    Message to show.
		 * @param {boolean} isError Whether it reports a failure.
		 */
		function say( text, isError ) {
			if ( ! messageEl ) {
				return;
			}

			messageEl.textContent = text;
			messageEl.classList.remove(
				'tt-text-red-700',
				'tt-text-emerald-700'
			);
			messageEl.classList.add(
				'tt-font-medium',
				isError ? 'tt-text-red-700' : 'tt-text-emerald-700'
			);
		}

		/**
		 * Run a request with the button disabled and a busy label.
		 *
		 * @param {HTMLElement} button    Button that was clicked.
		 * @param {string}      busyLabel Label while in flight.
		 * @param {Function}    run       Returns a promise.
		 */
		function busy( button, busyLabel, run ) {
			var original = button.textContent;

			button.disabled = true;
			button.textContent = busyLabel;

			run().finally( function () {
				button.disabled = false;
				button.textContent = original;
			} );
		}

		/**
		 * Switch between the two steps.
		 *
		 * @param {string} step Either "phone" or "code".
		 */
		function showStep( step ) {
			phoneStep.hidden = 'phone' !== step;
			codeStep.hidden = 'code' !== step;

			if ( 'code' === step && codeField ) {
				codeField.focus();
			}
		}

		/**
		 * Ask for a code.
		 *
		 * @param {HTMLElement} button Button that was clicked.
		 */
		function requestCode( button ) {
			var phone = phoneField ? phoneField.value.trim() : '';

			if ( ! phone ) {
				say( strings.needPhone, true );
				return;
			}

			busy( button, strings.sending, function () {
				return request( 'texttango_otp_request', { phone: phone } )
					.then( function ( data ) {
						say( data.message, false );
						showStep( 'code' );
					} )
					.catch( function ( error ) {
						say( error.message, true );
					} );
			} );
		}

		/**
		 * Submit the code.
		 *
		 * @param {HTMLElement} button Button that was clicked.
		 */
		function verifyCode( button ) {
			var code = codeField ? codeField.value.trim() : '';

			if ( ! code ) {
				say( strings.needCode, true );
				return;
			}

			busy( button, strings.verifying, function () {
				return request( 'texttango_otp_verify', {
					phone: phoneField ? phoneField.value.trim() : '',
					code: code,
					remember: rememberField && rememberField.checked ? '1' : '',
					redirect_to: redirectField ? redirectField.value : '',
				} )
					.then( function ( data ) {
						say( data.message, false );
						window.location.assign( data.redirect );
					} )
					.catch( function ( error ) {
						say( error.message, true );

						if ( codeField ) {
							codeField.value = '';
							codeField.focus();
						}
					} );
			} );
		}

		root.addEventListener( 'click', function ( event ) {
			var button = event.target.closest( '[data-texttango-action]' );

			if ( ! button || ! root.contains( button ) ) {
				return;
			}

			event.preventDefault();

			var action = button.getAttribute( 'data-texttango-action' );

			if ( 'request' === action ) {
				requestCode( button );
			}

			if ( 'verify' === action ) {
				verifyCode( button );
			}

			if ( 'back' === action ) {
				if ( codeField ) {
					codeField.value = '';
				}

				say( '', false );
				showStep( 'phone' );
			}
		} );

		// Enter submits the step the user is on, rather than the page's own form.
		root.addEventListener( 'keydown', function ( event ) {
			if ( 'Enter' !== event.key ) {
				return;
			}

			var target = event.target;
			var selector = null;

			if ( target === phoneField ) {
				selector = '[data-texttango-step="phone"] [data-texttango-action="request"]';
			} else if ( target === codeField ) {
				selector = '[data-texttango-step="code"] [data-texttango-action="verify"]';
			}

			if ( selector ) {
				event.preventDefault();
				var button = root.querySelector( selector );

				if ( button ) {
					button.click();
				}
			}
		} );
	}

	document.addEventListener( 'DOMContentLoaded', function () {
		Array.prototype.forEach.call(
			document.querySelectorAll( '[data-texttango-otp]' ),
			setupForm
		);
	} );
} )();
