'use strict';

/**
 * @classdesc This class enables pending callbacks to fire all at once, on a
 * synchronized schedule instead of one by one.  This is useful to group
 * operations that wake up expensive resources such as a mobile radio.
 *
 * @example
 * enqueue = ( new BackgroundQueue() ).add;
 * ...
 * enqueue( callback );
 * // callbacks will be fired in batches every 30 seconds (default)
 *
 * @class mw.eventLog.BackgroundQueue
 * @param {number} [intervalSecs=30] seconds to wait before calling callbacks
 */
module.exports = function BackgroundQueue( intervalSecs ) {
	let timer = null;
	const pendingCallbacks = [];
	let discardingPage;
	const queue = this;

	intervalSecs = intervalSecs || 30;

	/**
	 * Add a callback to the queue, to be flushed when the timer runs out.
	 *
	 * @method flush
	 * @param {Function} fn Callback to add
	 * @memberof mw.eventLog.BackgroundQueue
	 * @instance
	 */
	queue.add = function ( fn ) {
		if ( discardingPage ) {
			// If we're in the middle of discarding this page, every add will
			// immediately run the callback to avoid losing data.
			fn();
			return;
		}
		pendingCallbacks.push( fn );
		if ( !timer ) {
			timer = setTimeout( queue.flush, intervalSecs * 1000 );
		}
	};

	/**
	 * Manually execute all the callbacks, same as if the timer runs out.
	 *
	 * @method flush
	 * @memberof mw.eventLog.BackgroundQueue
	 * @instance
	 */
	queue.flush = function () {
		if ( timer ) {
			clearTimeout( timer );
			timer = null;
		}
		while ( pendingCallbacks.length ) {
			pendingCallbacks.shift()();
		}
	};

	// If the user navigates to another page or closes the tab/window/application,
	// then send any queued events.
	// Listen to the pagehide and visibilitychange events as Safari 12 and Mobile Safari 11
	// don't appear to support the Page Visbility API yet.
	window.addEventListener( 'pagehide', () => {
		// Record when the page is in the process of being discarded.
		discardingPage = true;
		queue.flush();
	} );

	// If the page was just suspended and gets reactivated, re-enable queuing.
	window.addEventListener( 'pageshow', () => {
		discardingPage = false;
	} );

	// https://developer.mozilla.org/en-US/docs/Web/API/Document/onvisibilitychange
	document.addEventListener( 'visibilitychange', () => {
		if ( document.hidden ) {
			queue.flush();
		}
	} );

	// Not allowed outside unit tests
	if ( window.QUnit ) {
		queue.getTimer = function () {
			return timer;
		};
		queue.getCallbacks = function () {
			return pendingCallbacks;
		};
	}
};