/**
 * Vanquish Multiple Addresses — block (React) checkout.
 *
 * Watches the "Use a saved address" select that the Additional Checkout Fields
 * API renders inside the billing and shipping address forms, and copies the
 * chosen address into WooCommerce's cart store.
 *
 * It subscribes to the data store rather than binding to DOM events on purpose:
 * the block checkout is React-rendered, so its markup and element ids are not a
 * stable contract, while the store is the documented one. No build step is
 * needed — this is plain ES5 using the wp globals.
 */
( function () {
	'use strict';

	var config = window.vanmuadBlocks;

	if ( ! config || ! window.wp || ! wp.data ) {
		return;
	}

	// The address keys the cart store understands.
	var ADDRESS_KEYS = [
		'first_name',
		'last_name',
		'company',
		'address_1',
		'address_2',
		'city',
		'state',
		'postcode',
		'country',
		'phone'
	];

	// The last address applied per type, so re-renders do not re-dispatch (which
	// would loop: dispatching updates the store, which wakes this subscriber).
	var applied = {
		billing: null,
		shipping: null
	};

	// ------------------------------------------------------- Preselection

	/*
	 * Setting the dropdown through the cart store did not stick: WooCommerce
	 * persists this field against the customer and replays the stored value, so a
	 * returning customer kept landing on whatever they picked last. Driving the
	 * real <select> is what settles it, because that is the same path a customer's
	 * own click takes.
	 *
	 * The catch, and the reason a plain `select.value = x` does nothing: React
	 * replaces the value setter on the elements it controls, so assigning to it
	 * updates the DOM without React ever noticing. Calling the setter from the
	 * prototype and then dispatching a bubbling 'change' is what React does listen
	 * for. No nodes are moved or removed, so there is nothing here for React's
	 * reconciliation to trip over.
	 */

	var preselected = {};
	var pollsLeft = 20;

	/**
	 * Our saved-address selects, wherever the block checkout put them.
	 *
	 * @return {Array} The select elements.
	 */
	function savedAddressSelects() {
		var found = document.querySelectorAll(
			'[class*="vanmuad-saved-address"] select, select[class*="vanmuad-saved-address"], select[id*="vanmuad"]'
		);

		return Array.prototype.slice.call( found );
	}

	/**
	 * Which address form a select belongs to.
	 *
	 * @param {Object} select The select element.
	 * @return {string} 'billing', 'shipping' or ''.
	 */
	function selectType( select ) {
		var probe = ( select.id || '' ) + ' ' + ( select.name || '' );

		if ( probe.indexOf( 'shipping' ) !== -1 ) {
			return 'shipping';
		}

		if ( probe.indexOf( 'billing' ) !== -1 ) {
			return 'billing';
		}

		// Nothing in the control itself says which form it is in, so ask the form:
		// the native fields around it carry the prefix.
		var form = select.closest( '.wc-block-components-address-form' );

		if ( form ) {
			if ( form.querySelector( '[id^="shipping-"], [name^="shipping"]' ) ) {
				return 'shipping';
			}

			if ( form.querySelector( '[id^="billing-"], [name^="billing"]' ) ) {
				return 'billing';
			}
		}

		return '';
	}

	/**
	 * Set a React-controlled select and let React know.
	 *
	 * @param {Object} select The select element.
	 * @param {string} value  The value to select.
	 */
	function setControlledValue( select, value ) {
		var descriptor = Object.getOwnPropertyDescriptor( window.HTMLSelectElement.prototype, 'value' );

		if ( descriptor && descriptor.set ) {
			descriptor.set.call( select, value );
		} else {
			select.value = value;
		}

		select.dispatchEvent( new Event( 'change', { bubbles: true } ) );
	}

	/**
	 * Put each form on the address the merchant configured — or on nothing when
	 * preselection is off. Done once per form; the customer is free to change it
	 * afterwards.
	 *
	 * @return {boolean} Whether every form has been handled.
	 */
	function applyPreselection() {
		if ( ! config.preselect ) {
			return true;
		}

		var selects = savedAddressSelects();

		if ( ! selects.length ) {
			return false;
		}

		selects.forEach( function ( select ) {
			var type = selectType( select );

			if ( ! type || preselected[ type ] ) {
				return;
			}

			preselected[ type ] = true;

			var want = config.preselect[ type ] || '';

			if ( select.value === want ) {
				return;
			}

			// Never select an option that is not there (a deleted address).
			if ( '' !== want && ! select.querySelector( 'option[value="' + want + '"]' ) ) {
				return;
			}

			setControlledValue( select, want );
		} );

		return !! ( preselected.billing && preselected.shipping );
	}

	/*
	 * The forms mount at different moments — the shipping one only once shipping
	 * is needed — so this keeps looking for a short while instead of assuming
	 * everything exists at load.
	 */
	var poll = window.setInterval( function () {
		pollsLeft--;

		if ( applyPreselection() || pollsLeft <= 0 ) {
			window.clearInterval( poll );
		}
	}, 400 );

	/**
	 * Build the payload for the cart store from a saved address.
	 *
	 * @param {Object} fields  The stored address fields.
	 * @param {string} chosen  The chosen address id.
	 * @param {string} type    'billing' or 'shipping'.
	 * @return {Object} The payload.
	 */
	function buildPayload( fields, chosen, type ) {
		var payload = {};

		ADDRESS_KEYS.forEach( function ( key ) {
			if ( typeof fields[ key ] !== 'undefined' ) {
				payload[ key ] = fields[ key ];
			}
		} );

		// Billing carries the email in the block checkout; shipping does not.
		if ( 'billing' === type && typeof fields.email !== 'undefined' ) {
			payload.email = fields.email;
		}

		// Keep the selection itself in the payload: the store update must not
		// clear the field that triggered it.
		payload[ config.fieldKey ] = chosen;

		return payload;
	}

	/**
	 * Apply the selection made in one address form.
	 *
	 * @param {string} type    'billing' or 'shipping'.
	 * @param {Object} address The address object from the store.
	 */
	function apply( type, address ) {
		if ( ! address ) {
			return;
		}

		var chosen = address[ config.fieldKey ];

		// Nothing picked, or already applied.
		if ( ! chosen || chosen === applied[ type ] ) {
			return;
		}

		var saved = config.addresses[ chosen ];

		if ( ! saved ) {
			return;
		}

		applied[ type ] = chosen;

		var dispatcher = wp.data.dispatch( 'wc/store/cart' );
		var setter = 'billing' === type ? 'setBillingAddress' : 'setShippingAddress';

		if ( dispatcher && typeof dispatcher[ setter ] === 'function' ) {
			dispatcher[ setter ]( buildPayload( saved, chosen, type ) );
		}

		/*
		 * Same extension point the classic checkout fires, so anything acting on
		 * a chosen address works in both. A CustomEvent rather than a jQuery one:
		 * nothing here depends on jQuery and there is no reason to start.
		 */
		document.body.dispatchEvent(
			new CustomEvent( 'vanmuad_address_applied', {
				detail: { purpose: type, fields: saved }
			} )
		);
	}

	wp.data.subscribe( function () {
		var store = wp.data.select( 'wc/store/cart' );

		if ( ! store || typeof store.getCustomerData !== 'function' ) {
			return;
		}

		var customer = store.getCustomerData();

		if ( ! customer ) {
			return;
		}

		apply( 'billing', customer.billingAddress );
		apply( 'shipping', customer.shippingAddress );
	} );
} )();
