/**
 * Notification groups list data structure.
 * It contains mw.echo.dm.NotificationsList items
 *
 * This contains a list of grouped notifications by source, and
 * serves as a list of lists for cross-wiki notifications based
 * on their remote sources and/or wikis.
 *
 * @class
 * @extends mw.echo.dm.SortedList
 *
 * @constructor
 * @param {Object} [config={}]
 * @param {boolean} [config.foreign] The list contains foreign notifications
 */
mw.echo.dm.NotificationGroupsList = function MwEchoDmNotificationGroupsList( config ) {
	config = config || {};

	// Parent constructor
	mw.echo.dm.NotificationGroupsList.super.call( this );

	// Sorting callback
	this.setSortingCallback( ( a, b ) => {
		// Reverse sorting
		if ( b.getTimestamp() < a.getTimestamp() ) {
			return -1;
		} else if ( b.getTimestamp() > a.getTimestamp() ) {
			return 1;
		}

		// Fallback on Source
		return b.getName() - a.getName();
	} );

	this.foreign = !!config.foreign;
	this.groups = {};

	this.aggregate( { discard: 'groupDiscardItem' } );
	this.connect( this, { groupDiscardItem: 'onGroupDiscardItem' } );
};

/* Initialization */
OO.inheritClass( mw.echo.dm.NotificationGroupsList, mw.echo.dm.SortedList );

/* Events */

/**
 * A group was permanently removed
 *
 * @event mw.echo.dm.NotificationGroupsList#discard
 */

/* Methods */

/**
 * Handle a discard event from any list.
 * This means that one of the sources has discarded an item.
 *
 * @param {mw.echo.dm.NotificationsList} groupList List source model for the item
 */
mw.echo.dm.NotificationGroupsList.prototype.onGroupDiscardItem = function ( groupList ) {
	// Check if the list has anything at all
	if ( groupList.isEmpty() ) {
		// Remove it
		this.removeGroup( groupList.getName() );
	}
};

/**
 * Add a group to the list. This is a more convenient alias to using
 * addItems()
 *
 * @param {string} groupSource Symbolic name for the source of
 *  this group, to be recognized for API operations
 * @param {Object} sourceData Source data
 * @param {mw.echo.dm.NotificationItem[]} [groupItems] Optional items to add to this group
 */
mw.echo.dm.NotificationGroupsList.prototype.addGroup = function ( groupSource, sourceData, groupItems ) {
	const groupListModel = new mw.echo.dm.NotificationsList( {
		title: sourceData.title,
		name: groupSource,
		source: groupSource,
		sourceURL: sourceData.base,
		timestamp: sourceData.ts,
	} );

	if ( Array.isArray( groupItems ) && groupItems.length > 0 ) {
		groupListModel.addItems( groupItems );
	}

	// Add the group
	this.addItems( [ groupListModel ] );
};

/**
 * Get the timestamp of the list by taking the latest list's
 * timestamp.
 *
 * @return {string} Latest timestamp
 */
mw.echo.dm.NotificationGroupsList.prototype.getTimestamp = function () {
	const items = this.getItems();

	return (
		items.length > 0 ?
			items[ 0 ].getTimestamp() :
			0
	);
};

/**
 * Add items to a specific group by its source identifier.
 *
 * @param {string} groupSource Source identifier of the group
 * @param {mw.echo.dm.NotificationItem[]} groupItems Items to add to this group
 */
mw.echo.dm.NotificationGroupsList.prototype.addItemsToGroup = function ( groupSource, groupItems ) {
	const group = this.getGroupByName( groupSource );

	if ( group ) {
		group.addItems( groupItems );
	}
};
/**
 * Remove a group from the list. This is an easier to use alias
 * to 'removeItems()' method.
 *
 * Since this is an intentional action, we fire 'discard' event.
 * The main reason for this is that the event 'remove' is a general
 * event that is fired both when a user intends on removing an
 * item and also when an item is temporarily removed to be re-added
 * for the sake of sorting. To avoid ambiguity, we use 'discard' event.
 *
 * @param {string} groupName Group name
 * @fires mw.echo.dm.NotificationGroupsList#discard
 */
mw.echo.dm.NotificationGroupsList.prototype.removeGroup = function ( groupName ) {
	const group = this.getGroupByName( groupName );

	if ( group ) {
		this.removeItems( group );
		this.emit( 'discard', group );
	}
};

/**
 * Get a group by its source identifier.
 *
 * @param {string} groupName Group name
 * @return {mw.echo.dm.NotificationsList|null} Requested group, null if none was found.
 */
mw.echo.dm.NotificationGroupsList.prototype.getGroupByName = function ( groupName ) {
	const items = this.getItems();

	for ( let i = 0; i < items.length; i++ ) {
		if ( items[ i ].getName() === groupName ) {
			return items[ i ];
		}
	}
	return null;
};
