/*global OO*/
( function ( mw, $ ) {
	/**
	 * This is a factory for MessagePoster objects, which allows a pluggable to way to script leaving a
	 * talk page message.
	 *
	 * @class mw.messagePoster.factory
	 * @singleton
	 */
	function MwMessagePosterFactory() {
		this.api = new mw.Api();
		this.contentModelToClass = {};
	}

	OO.initClass( MwMessagePosterFactory );

	// Note: This registration scheme is currently not compatible with LQT, since that doesn't
	// have its own content model, just islqttalkpage.  LQT pages will be passed to the wikitext
	// MessagePoster.
	/**
	 * Registers a MessagePoster subclass for a given content model.
	 *
	 * @param {string} contentModel Content model of pages this MessagePoster can post to
	 * @param {Function} messagePosterConstructor Constructor for MessagePoster
	 */
	MwMessagePosterFactory.prototype.register = function ( contentModel, messagePosterConstructor ) {
		if ( this.contentModelToClass[ contentModel ] !== undefined ) {
			throw new Error( 'The content model \'' + contentModel + '\' is already registered.' );
		}

		this.contentModelToClass[ contentModel ] = messagePosterConstructor;
	};

	/**
	 * Unregisters a given content model
	 * This is exposed for testing and should not normally be needed.
	 *
	 * @param {string} contentModel Content model to unregister
	 */
	MwMessagePosterFactory.prototype.unregister = function ( contentModel ) {
		delete this.contentModelToClass[ contentModel ];
	};

	/**
	 * Creates a MessagePoster, given a title.  A promise for this is returned.
	 * This works by determining the content model, then loading the corresponding
	 * module (which will register the MessagePoster class), and finally constructing it.
	 *
	 * This does not require the message and should be called as soon as possible, so it does the
	 * API and ResourceLoader requests in the background.
	 *
	 * @param {mw.Title} title Title that will be posted to
	 * @return {jQuery.Promise} Promise resolving to a mw.messagePoster.MessagePoster.
	 *   For failure, rejected with up to three arguments:
	 *
	 *   - errorCode Error code string
	 *   - error Error explanation
	 *   - details Further error details
	 */
	MwMessagePosterFactory.prototype.create = function ( title ) {
		var pageId, page, contentModel, moduleName,
			factory = this;

		return this.api.get( {
			action: 'query',
			prop: 'info',
			indexpageids: 1,
			titles: title.getPrefixedDb()
		} ).then( function ( result ) {
			if ( result.query.pageids && result.query.pageids.length > 0 ) {
				pageId = result.query.pageids[ 0 ];
				page = result.query.pages[ pageId ];

				contentModel = page.contentmodel;
				moduleName = 'mediawiki.messagePoster.' + contentModel;
				return mw.loader.using( moduleName ).then( function () {
					return factory.createForContentModel(
						contentModel,
						title
					);
				}, function () {
					return $.Deferred().reject( 'failed-to-load-module', 'Failed to load the \'' + moduleName + '\' module' );
				} );
			} else {
				return $.Deferred().reject( 'unexpected-response', 'Unexpected API response' );
			}
		}, function ( errorCode, details ) {
			return $.Deferred().reject( 'content-model-query-failed', errorCode, details );
		} ).promise();
	};

	/**
	 * Creates a MessagePoster instance, given a title and content model
	 *
	 * @private
	 *
	 * @param {string} contentModel Content model of title
	 * @param {mw.Title} title Title being posted to
	 * @return {mw.messagePoster.MessagePoster}
	 *
	 */
	MwMessagePosterFactory.prototype.createForContentModel = function ( contentModel, title ) {
		return new this.contentModelToClass[ contentModel ]( title );
	};

	mw.messagePoster = {
		factory: new MwMessagePosterFactory()
	};
}( mediaWiki, jQuery ) );
