( function ( window ) {
	'use strict';

	const globalObject = window;
	const configQueue = globalObject.ARAnalyticsConfig || [];
	globalObject.ARAnalyticsConfig = []; // reset after reading to avoid duplicate processing.

	if ( ! Array.isArray( configQueue ) || ! configQueue.length ) {
		globalObject.ARAnalytics = globalObject.ARAnalytics || {
			track: function () {},
			flush: function () {},
			getDefaultPluginId: function () {
				return null;
			},
		};
		return;
	}

	const configs = {};
	let defaultPluginId = null;

	configQueue.forEach( function ( rawConfig ) {
		if ( ! rawConfig || ! rawConfig.pluginId ) {
			return;
		}
		configs[ rawConfig.pluginId ] = rawConfig;
		if ( null === defaultPluginId ) {
			defaultPluginId = rawConfig.pluginId;
		}
	} );

	if ( ! defaultPluginId ) {
		return;
	}

	const queue = [];
	let flushTimer = null;
	let isFlushing = false;

	const storageKey = 'arAnalyticsClientId';

	function toSnakeCase( value ) {
		return String( value || '' )
			.replace( /[^\w\s-]/g, ' ' )
			.trim()
			.replace( /[\s-]+/g, '_' )
			.replace( /([a-z0-9])([A-Z])/g, function ( match, lower, upper ) {
				return lower + '_' + upper.toLowerCase();
			} )
			.toLowerCase();
	}

	function resolveGoogleAnalyticsEventName( event, gaConfig ) {
		if ( gaConfig.eventNameMap && gaConfig.eventNameMap[ event.type ] ) {
			return gaConfig.eventNameMap[ event.type ];
		}

		const prefix = typeof gaConfig.eventNamePrefix === 'string' && gaConfig.eventNamePrefix.length
			? gaConfig.eventNamePrefix
			: 'ar_';

		return ( prefix + event.type ).replace( /[^a-zA-Z0-9_]/g, '_' ).toLowerCase();
	}

	function buildGoogleAnalyticsParams( event, gaConfig ) {
		const params = Object.assign( {}, gaConfig.parameters || {} );

		params.plugin_id = event.pluginId;
		params.event_type = event.type;

		if ( event.modelId ) {
			params.model_id = event.modelId;
		}
		if ( event.instanceId ) {
			params.instance_id = event.instanceId;
		}
		if ( event.variant ) {
			params.variant = event.variant;
		}
		if ( event.device ) {
			params.device = event.device;
		}
		if ( event.pageUrl ) {
			params.page_url = event.pageUrl;
		}
		if ( event.referrer ) {
			params.referrer = event.referrer;
		}
		if ( event.clientId ) {
			params.client_id = event.clientId;
		}

		if ( event.context && typeof event.context === 'object' ) {
			Object.keys( event.context ).forEach( function ( key ) {
				const value = event.context[ key ];
				if ( value === null || value === undefined ) {
					return;
				}

				let formattedValue = value;
				if ( typeof value === 'object' ) {
					try {
						formattedValue = JSON.stringify( value );
					} catch ( error ) {
						formattedValue = String( value );
					}
				}

				const paramKey = 'context_' + toSnakeCase( key );
				params[ paramKey ] = formattedValue;
			} );
		}

		return params;
	}

	function maybeSendToGoogleAnalytics( event, config ) {
		if ( ! config || ! config.googleAnalytics ) {
			return;
		}

		const gaConfig = config.googleAnalytics;
		if ( ! gaConfig || ! gaConfig.enabled ) {
			return;
		}

		const mode = gaConfig.mode || 'auto';
		const eventName = resolveGoogleAnalyticsEventName( event, gaConfig );
		const params = buildGoogleAnalyticsParams( event, gaConfig );

		let dispatched = false;

		if ( ( mode === 'gtag' || mode === 'both' || mode === 'auto' ) && typeof window.gtag === 'function' ) {
			const gtagParams = Object.assign( {}, params, gaConfig.gtagParameters || {} );

			if ( Array.isArray( gaConfig.sendToList ) && gaConfig.sendToList.length ) {
				gtagParams.send_to = gaConfig.sendToList.filter( function ( destination ) {
					return typeof destination === 'string' && destination.length;
				} );
			} else if ( gaConfig.sendTo ) {
				gtagParams.send_to = gaConfig.sendTo;
			}

			if ( gaConfig.transport ) {
				gtagParams.transport_type = gaConfig.transport;
			}

			window.gtag( 'event', eventName, gtagParams );
			dispatched = true;
		}

		if ( mode === 'dataLayer' || mode === 'both' || ( mode === 'auto' && ! dispatched ) ) {
			const dataLayerName = gaConfig.dataLayerName || 'dataLayer';
			const dataLayer = window[ dataLayerName ];
			if ( Array.isArray( dataLayer ) ) {
				const dataLayerParams = Object.assign( {}, params, gaConfig.dataLayerParameters || {} );
				dataLayerParams.event = eventName;
				dataLayer.push( dataLayerParams );
				dispatched = true;
			}
		}

		if ( ! dispatched ) {
			logDebug( event.pluginId, 'Google Analytics enabled but no matching dispatcher was found for mode "' + mode + '".' );
		}
	}

	function logDebug( pluginId, message, data ) {
		const config = getConfig( pluginId );
		if ( ! config || ! config.debug ) {
			return;
		}
		if ( data ) {
			console.log( '[ARAnalytics]', message, data ); // eslint-disable-line no-console
		} else {
			console.log( '[ARAnalytics]', message ); // eslint-disable-line no-console
		}
	}

	function getConfig( pluginId ) {
		if ( pluginId && configs[ pluginId ] ) {
			return configs[ pluginId ];
		}
		return configs[ defaultPluginId ];
	}

	function detectDevice() {
		const ua = navigator.userAgent || '';
		if ( /android/i.test( ua ) ) {
			return 'android';
		}
		if ( /(iphone|ipad|ipod)/i.test( ua ) ) {
			return 'ios';
		}
		if ( /windows/i.test( ua ) ) {
			return 'windows';
		}
		if ( /mac os x/i.test( ua ) ) {
			return 'mac';
		}
		if ( /linux/i.test( ua ) ) {
			return 'linux';
		}
		return 'unknown';
	}

	function getClientId() {
		try {
			const existing = window.localStorage.getItem( storageKey );
			if ( existing ) {
				return existing;
			}
			const id = ( 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx' ).replace( /[xy]/g, function ( c ) {
				const r = Math.random() * 16 | 0; // eslint-disable-line no-bitwise
				const v = c === 'x' ? r : ( r & 0x3 | 0x8 ); // eslint-disable-line no-bitwise
				return v.toString( 16 );
			} );
			window.localStorage.setItem( storageKey, id );
			return id;
		} catch ( e ) {
			return '';
		}
	}

	function scheduleFlush( immediate ) {
		if ( immediate ) {
			flush();
			return;
		}

		if ( flushTimer ) {
			return;
		}

		const config = getConfig();
		const delay = config.flushInterval || 3000;

		flushTimer = window.setTimeout( function () {
			flush();
		}, delay );
	}

	function flush( useBeacon ) {
		if ( isFlushing || ! queue.length ) {
			return;
		}

		const events = queue.splice( 0, queue.length );
		const sampleEvent = events[ 0 ];
		const config = getConfig( sampleEvent.pluginId );

		if ( ! config || ! config.enabled ) {
			return;
		}

		isFlushing = true;
		if ( flushTimer ) {
			window.clearTimeout( flushTimer );
			flushTimer = null;
		}

		const requestBody = {
			events: events,
		};

		if ( config.nonce ) {
			requestBody.nonce = config.nonce;
		}

		if ( config.signature ) {
			requestBody.signature = config.signature;
		}

		const payload = JSON.stringify( requestBody );

		if ( useBeacon && navigator.sendBeacon ) {
			try {
				const blob = new Blob( [ payload ], { type: 'application/json' } );
				navigator.sendBeacon( config.restUrl, blob );
			} catch ( beaconError ) {
				logDebug( sampleEvent.pluginId, 'Beacon failed', beaconError );
			} finally {
				isFlushing = false;
			}
			return;
		}

		const headers = {
			'Content-Type': 'application/json',
		};

		if ( config.nonce ) {
			headers['X-WP-Nonce'] = config.nonce;
		}

		if ( config.signature ) {
			headers['X-AR-Signature'] = config.signature;
		}

		window.fetch( config.restUrl, {
			method: 'POST',
			credentials: 'same-origin',
			headers: headers,
			body: payload,
		} )
			.then( function ( response ) {
				if ( ! response.ok ) {
					throw new Error( response.statusText );
				}
				logDebug( sampleEvent.pluginId, 'Analytics batch sent', events );
			} )
			.catch( function ( error ) {
				logDebug( sampleEvent.pluginId, 'Analytics request failed', error );
				Array.prototype.unshift.apply( queue, events );
				scheduleFlush();
			} )
			.finally( function () {
				isFlushing = false;
			} );
	}

	function normalizeEvent( type, payload ) {
		const eventPayload = payload || {};
		const pluginId = eventPayload.pluginId || defaultPluginId;
		const config = getConfig( pluginId );

		if ( ! config || ! config.enabled ) {
			return null;
		}

		const baseContext = config.context || {};
		const mergedContext = Object.assign( {}, baseContext, eventPayload.context || {} );

		return {
			type: type,
			pluginId: pluginId,
			modelId: eventPayload.modelId || '',
			instanceId: eventPayload.instanceId || '',
			variant: eventPayload.variant || '',
			pageUrl: eventPayload.pageUrl || window.location.href,
			referrer: eventPayload.referrer || document.referrer || '',
			device: eventPayload.device || detectDevice(),
			clientId: getClientId(),
			context: mergedContext,
			timestamp: eventPayload.timestamp || Date.now(),
		};
	}

	function track( type, payload ) {
		const normalized = normalizeEvent( type, payload );
		if ( ! normalized ) {
			return;
		}

		const config = getConfig( normalized.pluginId );
		maybeSendToGoogleAnalytics( normalized, config );

		queue.push( normalized );
		logDebug( normalized.pluginId, 'Queued analytics event', normalized );

		const batchSize = config.batchSize || 6;
		if ( queue.length >= batchSize ) {
			scheduleFlush( true );
		} else {
			scheduleFlush();
		}
	}

	globalObject.ARAnalytics = {
		track: track,
		flush: flush,
		getDefaultPluginId: function () {
			return defaultPluginId;
		},
		getClientId: getClientId,
	};

	window.addEventListener( 'beforeunload', function () {
		if ( queue.length ) {
			flush( true );
		}
	} );

	document.addEventListener( 'visibilitychange', function () {
		if ( document.visibilityState === 'hidden' && queue.length ) {
			flush( true );
		}
	} );
}( window ) );

