/**
 * Handle getting labels for wikibase items
 *
 * Caller is responsible for determining if this should be used
 *
 * @class GlobalWatchlistWikibaseHandler
 * @constructor
 *
 * @param {GlobalWatchlistDebugger} globalWatchlistDebug Debugger instance to log to
 * @param {mw.ForeignApi} api Instance of mw.ForeignApi to use
 * @param {string} userLang language to fetch labels in
 */
function GlobalWatchlistWikibaseHandler( globalWatchlistDebug, api, userLang ) {
	// Logger to send debug info to
	this.debugLogger = globalWatchlistDebug;

	// api for the wikibase repo site
	this.api = api;

	// Language to fetch the labels in
	this.userLang = userLang;
}

/**
 * Shortcut for sending information to the debug logger
 *
 * @param {string} msg Message for debug entry
 * @param {string} [extraInfo] Extra information for the debug entry
 */
GlobalWatchlistWikibaseHandler.prototype.debug = function ( msg, extraInfo ) {
	this.debugLogger.info( 'wikibase:' + msg, extraInfo );
};

/**
 * Fetch the labels for all of the ids given
 *
 * Since the api is usually limited to 50 ids at a time, called recursively
 * until all ids are processed. No special handling for users with `apihighlimits`,
 * still only fetch 50 at a time
 *
 * The returned promise resolves to an object with each of the entity ids being a key
 * to the relevant information. To help visualize, below is a partial result of the
 * wbgetentities query[1] performed on wikidata for Q5, P10, and L2, with the exception
 * that the `forms` and `senses` for L2 are not included.
 *
 * ```json
 *    {
 *        "Q5": {
 *            "type": "item",
 *            "id": "Q5",
 *            "labels": {
 *                "en": {
 *                    "language": "en",
 *                    "value": "human"
 *                }
 *            }
 *        },
 *        "P10": {
 *            "type": "property",
 *            "datatype": "commonsMedia",
 *            "id": "P10",
 *            "labels": {
 *                "en": {
 *                    "language": "en",
 *                    "value": "video"
 *                }
 *            }
 *        },
 *        "L2": {
 *            "type": "lexeme",
 *            "id": "L2",
 *            "lemmas": {
 *                "en": {
 *                    "language": "en",
 *                    "value": "first"
 *                }
 *            },
 *            "lexicalCategory": "Q1084",
 *            "language": "Q1860",
 *            "forms": [ ... ],
 *            "senses": [ ... ]
 *        }
 *    }
 * ```
 *
 *
 * [1] See:
 * https://www.wikidata.org/w/api.php?action=wbgetentities&ids=Q5|P10|L2&languages=en&props=labels&formatversion=2
 *
 * @see {@link GlobalWatchlistWikibaseHandler#cleanupRawLabels #cleanupRawLabels} for converting
 * to a more usable form
 *
 * @param {Array} entityIds The ids to get labels for
 * @return {Promise} Promise of api result
 */
GlobalWatchlistWikibaseHandler.prototype.getRawLabels = function ( entityIds ) {
	var that = this;

	return new Promise( function ( resolve ) {
		var query = {
			action: 'wbgetentities',
			formatversion: 2,
			ids: entityIds.slice( 0, 50 ),
			languages: that.userLang,
			props: 'labels'
		};
		that.api.get( query ).then( function ( response ) {
			that.debug( 'getRawLabels - api response', response );
			var labels = response.entities;
			if ( entityIds.length > 50 ) {
				// Recursive processing
				that.getRawLabels( entityIds.slice( 50 ) ).then( function ( extraLabels ) {
					var bothLabels = $.extend( {}, labels, extraLabels );
					that.debug( 'getRawLabels - bothLabels', bothLabels );
					resolve( bothLabels );
				} );
			} else {
				// No need for further processing, either had less than 50 to
				// begin with or this was the final recursive call
				that.debug( 'getRawLabels - last', labels );
				resolve( labels );
			}
		} );
	} );
};

/**
 * Convert the messy object returned from getRawLabels to something clearer
 *
 * Resulting object has the following form (see documentation in
 * {@link GlobalWatchlistWikibaseHandler#getRawLabels #getRawLabels} for the original)
 *
 *```json
 *    {
 *        "Q5": "human",
 *        "P10": "video",
 *        "L2": "first"
 *    }
 *```
 *
 * @param {Object} rawLabels Labels in the format returns from the api
 * @return {Object} Labels in a more usable format
 */
GlobalWatchlistWikibaseHandler.prototype.cleanupRawLabels = function ( rawLabels ) {
	this.debug( 'cleanupRawLabels - starting (raw)', rawLabels );

	var cleanedLabels = {};
	var entityIds = Object.keys( rawLabels );
	var that = this;
	var entityInfo,
		labelKey;

	entityIds.forEach( function ( entityId ) {
		// Object.keys -> known to be a valid key
		entityInfo = rawLabels[ entityId ];

		// Lexemes have `lemmas`, items and properties have `labels`
		if ( entityInfo.type === 'lexeme' ) {
			labelKey = 'lemmas';
		} else {
			labelKey = 'labels';
		}

		if ( entityInfo[ labelKey ] &&
			entityInfo[ labelKey ][ that.userLang ] &&
			entityInfo[ labelKey ][ that.userLang ].value
		) {
			cleanedLabels[ entityId ] = entityInfo[ labelKey ][ that.userLang ].value;
		}
	} );
	this.debug( 'cleanupRawLabels - ending (clean)', cleanedLabels );

	return cleanedLabels;
};

/**
 * Set entities' titleMsg (title without the `Property:` or `Lexeme:` prefix) and
 * get a list of the ids to fetch in the form of Q1/P2/L3
 *
 * @param {GlobalWatchlistEntryBase[]} entries Original summary entries
 * @return {Object} updated entries and ids
 */
GlobalWatchlistWikibaseHandler.prototype.getEntityIds = function ( entries ) {
	var ids = [];

	entries.forEach( function ( entry ) {
		entry.titleMsg = entry.title.replace(
			/^(?:Property|Lexeme):/,
			''
		);

		if ( entry.ns === 0 || entry.title !== entry.titleMsg ) {
			// Either:
			// * main namespace, title doesn't have a prefix to remove
			// * property/lexeme, prefix removed
			// Add the Q/P/L id, without duplication
			if ( ids.indexOf( entry.titleMsg ) === -1 ) {
				ids.push( entry.titleMsg );
			}
		}
	} );

	return {
		entries: entries,
		ids: ids
	};
};

/**
 * Entry point - alter the entities given to have titleMsg that reflects the labels
 *
 * Promise resolves to the summary entries with updated info
 *
 * @param {GlobalWatchlistEntryBase[]} summaryEntries Original summary, entries have titleMsg as
 *   just the plain title (Q1, P2, L3, etc.)
 * @return {Promise} Promise of updated summary, with labels
 */
GlobalWatchlistWikibaseHandler.prototype.addWikibaseLabels = function ( summaryEntries ) {
	var that = this;

	return new Promise( function ( resolve ) {
		var extractedInfo = that.getEntityIds( summaryEntries );
		that.debug( 'addLabels - extractedInfo', extractedInfo );

		var updatedEntries = extractedInfo.entries;
		var entityIds = extractedInfo.ids;

		if ( entityIds.length === 0 ) {
			// Nothing to fetch
			resolve( updatedEntries );
			return;
		}

		that.getRawLabels( entityIds ).then( function ( rawLabels ) {
			var cleanedLabels = that.cleanupRawLabels( rawLabels );

			updatedEntries.forEach( function ( entry ) {
				if ( cleanedLabels[ entry.titleMsg ] ) {
					entry.titleMsg += ' ' + mw.msg( 'parentheses', cleanedLabels[ entry.titleMsg ] );
				}
			} );

			resolve( updatedEntries );
		} );
	} );
};

module.exports = GlobalWatchlistWikibaseHandler;