/**
 * Edit check to detect pasted content, which is often a sign of copyright violation/plagiarism.
 *
 * @class
 * @extends mw.editcheck.BaseEditCheck
 *
 * @constructor
 * @param {mw.editcheck.Controller} controller
 * @param {Object} [config]
 * @param {boolean} [includeSuggestions=false]
 */
mw.editcheck.PasteCheck = function MWPasteCheck() {
	// Parent constructor
	mw.editcheck.PasteCheck.super.apply( this, arguments );
};

/* Inheritance */

OO.inheritClass( mw.editcheck.PasteCheck, mw.editcheck.BaseEditCheck );

/* Static properties */

mw.editcheck.PasteCheck.static.defaultConfig = ve.extendObject( {}, mw.editcheck.PasteCheck.super.static.defaultConfig, {
	showAsSuggestion: false,
	minimumCharacters: 50,
	ignoreQuotedContent: true
} );

mw.editcheck.PasteCheck.static.title = OO.ui.deferMsg( 'editcheck-copyvio-title' );

mw.editcheck.PasteCheck.static.description = ve.deferJQueryMsg( 'editcheck-copyvio-description' );

mw.editcheck.PasteCheck.static.prompt = OO.ui.deferMsg( 'editcheck-copyvio-prompt' );

mw.editcheck.PasteCheck.static.success = OO.ui.deferMsg( 'editcheck-copyvio-remove-notify' );

mw.editcheck.PasteCheck.static.name = 'paste';

mw.editcheck.PasteCheck.static.choices = [
	{
		action: 'keep',
		label: OO.ui.deferMsg( 'editcheck-copyvio-action-keep' )
	},
	{
		action: 'remove',
		label: OO.ui.deferMsg( 'editcheck-copyvio-action-remove' )
	}
];

mw.editcheck.PasteCheck.static.takesFocus = true;

mw.editcheck.PasteCheck.static.originalPasteLengths = {};

/**
 * Categories of paste sources that have a lower plagiarism risk
 *
 * @static
 * @property {string[]}
 */
mw.editcheck.PasteCheck.static.trustedPasteCategories = [
	'internal', // Another VE instance
	'wordProcessor', // Word, Google Docs, etc.
	'plain' // Plain text sources, e.g. Notepad, or copied as plain text
];

/* Methods */

mw.editcheck.PasteCheck.prototype.onDocumentChange = function ( surfaceModel ) {
	const pastesById = {};
	const doc = surfaceModel.getDocument();
	doc.getDocumentNode().getAnnotationRanges().forEach( ( annRange ) => {
		const annotation = annRange.annotation;
		if (
			annotation instanceof ve.dm.ImportedDataAnnotation && !(
				annotation.getAttribute( 'source' ) &&
				this.constructor.static.trustedPasteCategories.some(
					( category ) => annotation.getAttribute( 'source' ).categories.includes( category )
				)
			)
		) {
			const id = annotation.getAttribute( 'eventId' );
			if ( this.isDismissedId( id ) ) {
				return;
			}
			if ( !this.isRangeValid( annRange.range, doc ) ) {
				return;
			}
			pastesById[ id ] = pastesById[ id ] || [];
			pastesById[ id ].push( annRange.range );
		}
	} );
	return Object.keys( pastesById ).map( ( id ) => {
		const fragments = pastesById[ id ].map( ( range ) => surfaceModel.getLinearFragment( range ) );
		if ( !( id in mw.editcheck.PasteCheck.static.originalPasteLengths ) ) {
			const combinedLength = pastesById[ id ].reduce( ( sum, range ) => sum + range.getLength(), 0 );
			mw.editcheck.PasteCheck.static.originalPasteLengths[ id ] = combinedLength;
		}
		if ( mw.editcheck.PasteCheck.static.originalPasteLengths[ id ] < this.config.minimumCharacters ) {
			return null;
		}

		return new mw.editcheck.EditCheckAction( {
			fragments,
			id,
			check: this
		} );
	} ).filter( Boolean );
};

// TODO: enable this once issues editing content from pre-save are resolved (T407543)
// mw.editcheck.PasteCheck.prototype.onBeforeSave = mw.editcheck.PasteCheck.prototype.onDocumentChange;

mw.editcheck.PasteCheck.prototype.act = function ( choice, action, surface ) {
	switch ( choice ) {
		case 'keep':
			return action.widget.showFeedback( {
				description: ve.msg( 'editcheck-copyvio-keep-description' ),
				choices: [ 'wrote', 'permission', 'other' ].map(
					( key ) => ( {
						data: key,
						// Messages that can be used here:
						// * editcheck-copyvio-keep-wrote
						// * editcheck-copyvio-keep-permission
						// * editcheck-copyvio-keep-other
						label: ve.msg( 'editcheck-copyvio-keep-' + key )
					} ) )
			} ).then( ( reason ) => {
				this.dismiss( action );
				this.showSuccess( ve.msg( 'editcheck-copyvio-keep-notify' ) );
				return ve.createDeferred().resolve( { action: choice, reason } ).promise();
			} );
		case 'remove': {
			action.fragments.forEach( ( fragment ) => {
				fragment.removeContent();

				// If removal leaves an empty content branch node, then remove it too
				const range = fragment.getSelection().getCoveringRange();
				const node = surface.getModel().getDocument().getBranchNodeFromOffset( range.start );
				if ( node && node.canContainContent() && node.getRange().isCollapsed() ) {
					surface.getModel().getLinearFragment( node.getOuterRange() ).removeContent();
				}
			} );
			// If in pre-save mode, close the check dialog
			const closePromise = this.controller.inBeforeSave ? this.controller.closeDialog() : ve.createDeferred().resolve().promise();
			return closePromise.then( () => {
				// Auto-scrolling causes selection and focus changes...
				setTimeout( () => {
					action.fragments[ action.fragments.length - 1 ].select();
					surface.getView().focus();
				}, 500 );
				this.showSuccess();
			} );
		}
	}
};

/* Registration */

mw.editcheck.editCheckFactory.register( mw.editcheck.PasteCheck );