/*!
 * JS for special page.
 * @author Niklas Laxström
 * @author Sucheta Ghoshal
 * @author Amir E. Aharoni
 * @author Pau Giner
 * @license GPL-2.0-or-later
 */

( function () {
	'use strict';

	var delay;

	/**
	 * A callback for sorting translations.
	 *
	 * @param {Object} translationA Object loaded from translation stash
	 * @param {Object} translationB Object loaded from translation stash
	 * @return {number} String comparison of language codes
	 */
	function sortTranslationsByLanguage( translationA, translationB ) {
		var a = translationA.title.split( '/' ).pop(),
			b = translationB.title.split( '/' ).pop();

		return a.localeCompare( b );
	}

	function doApiAction( options ) {
		var api = new mw.Api(),
			optionsWithDefaults = $.extend( {}, { action: 'translatesandbox' }, options );

		return api.postWithToken( 'csrf', optionsWithDefaults ).promise();
	}

	function removeSelectedRequests() {
		var $selectedRequests = $( '.request-selector:checked' );

		var $nextRequest = $selectedRequests
			.first() // First selected request
			.closest( '.request' ) // The request corresponds that checkbox
			.prevAll( ':not(.hide)' ) // Go back till a non-hidden request
			.first(); // The above selector gives list from bottom to top. Select the bottom one.

		$selectedRequests.closest( '.request' ).remove();

		updateRequestCount();

		if ( !$nextRequest.length ) {
			// If there's no request above the first checked request,
			// try to get the first request in the column
			$nextRequest = $( '.requests .request:not(.hide)' ).first();
		}

		if ( $nextRequest.length ) {
			$nextRequest.trigger( 'click' );
			updateSelectedIndicator( 1 );
		} else {
			updateSelectedIndicator( 0 );
		}
	}

	/**
	 * Display the request details when user clicks on a request item
	 *
	 * @param {Object} request The request data set from backend on request items
	 */
	function displayRequestDetails( request ) {
		var $reminderStatus = $( '<span>' ).addClass( 'reminder-status' ),
			$detailsPane = $( '.details.pane' );
		if ( request.reminderscount ) {
			var agoText = moment.isMoment( request.lastreminder ) ? moment( request.lastreminder ).fromNow() : request.lastreminder;
			$reminderStatus.text( mw.msg(
				'tsb-reminder-sent',
				request.reminderscount,
				agoText
			) );
		}

		$detailsPane.empty().append(
			$( '<div>' )
				.addClass( 'tsb-header row' )
				.text( request.username ),
			$( '<div>' )
				.addClass( 'reminder-email row' )
				.append(
					$( '<span>' )
						.attr( { dir: 'ltr' } )
						.text( request.email ),
					$( '<a>' )
						.prop( 'href', '#' )
						.addClass( 'send-reminder link' )
						.text( mw.msg( 'tsb-reminder-link-text' ) )
						.on( 'click', function ( e ) {
							e.preventDefault();

							$reminderStatus
								.text( mw.msg( 'tsb-reminder-sending' ) );

							doApiAction( {
								do: 'remind',
								userid: request.userid
							} ).done( function () {
								request.lastreminder = moment();
								request.reminderscount++;
								$reminderStatus.text( mw.msg( 'tsb-reminder-sent-new' ) );
							} ).fail( function () {
								$reminderStatus.text( mw.msg( 'tsb-reminder-failed' ) );
							} );
						} ),
					$reminderStatus
				),
			$( '<div>' )
				.addClass( 'languages row autonym' ),
			$( '<div>' )
				.addClass( 'signup-comment row' ),
			$( '<div>' )
				.addClass( 'actions row' )
				.append(
					$( '<button>' )
						.addClass( 'accept mw-ui-button mw-ui-progressive' )
						.text( mw.msg( 'tsb-accept-button-label' ) )
						.on( 'click', function () {
							mw.notify( mw.msg( 'tsb-accept-confirmation', 1 ) );

							window.tsbUpdatingUsers = true;

							doApiAction( {
								userid: request.userid,
								do: 'promote'
							} ).done( function () {
								removeSelectedRequests();

								window.tsbUpdatingUsers = false;
							} );
						} ),
					$( '<button>' )
						.addClass( 'reject mw-ui-button mw-ui-destructive' )
						.text( mw.msg( 'tsb-reject-button-label' ) )
						.on( 'click', function () {
							mw.notify( mw.msg( 'tsb-reject-confirmation', 1 ) );

							window.tsbUpdatingUsers = true;

							doApiAction( {
								userid: request.userid,
								do: 'delete'
							} ).done( function () {
								removeSelectedRequests();

								window.tsbUpdatingUsers = false;
							} );
						} )
				),
			$( '<div>' )
				.addClass( 'translations row' )
		);

		if ( request.languagepreferences ) {
			if ( request.languagepreferences.languages ) {
				request.languagepreferences.languages.forEach( function ( language ) {
					$detailsPane.find( '.languages' ).append(
						$( '<span>' )
							.prop( {
								dir: $.uls.data.getDir( language ),
								lang: language
							} )
							.text( $.uls.data.getAutonym( language ) )
					);
				} );
			}

			if ( request.languagepreferences.comment ) {
				$detailsPane.find( '.signup-comment' ).append(
					$( '<div>' )
						.addClass( 'signup-comment-label' )
						.text( mw.msg( 'tsb-user-posted-a-comment' ) ),
					$( '<div>' )
						.addClass( 'signup-comment-text' )
						.text( request.languagepreferences.comment )
				);
			}
		}

		getUserTranslations( request.username ).done( showTranslations );
	}

	/**
	 * Get the current users translations.
	 *
	 * @param {string} user User name
	 * @return {jQuery.Promise}
	 */
	function getUserTranslations( user ) {
		var api = new mw.Api();

		return api.postWithToken( 'csrf', {
			action: 'translationstash',
			subaction: 'query',
			username: user
		} );
	}

	function showTranslations( translations ) {
		var $target = $( '.translations' );

		$target.empty();

		// Display a message if the user didn't make any translations
		if ( !translations.translationstash.translations.length ) {
			$target.append(
				$( '<div>' )
					.addClass( 'tsb-details-no-translations' )
					.text( mw.msg( 'tsb-didnt-make-any-translations' ) )
			);

			return;
		}

		var gender = $( '.requests-list .request.selected' ).data( 'data' ).gender;
		$target.append(
			$( '<div>' )
				.addClass( 'row title' )
				.append(
					$( '<div>' )
						.text( mw.msg( 'tsb-translations-source' ) )
						.addClass( 'four columns' ),
					$( '<div>' )
						.text( mw.msg( 'tsb-translations-user', gender ) )
						.addClass( 'four columns' ),
					$( '<div>' )
						.text( mw.msg( 'tsb-translations-current' ) )
						.addClass( 'four columns' )
				)
		);

		translations.translationstash.translations.sort( sortTranslationsByLanguage );
		translations.translationstash.translations.forEach( function ( translation ) {
			showTranslation( translation );
		} );
	}

	function showTranslation( translation ) {
		var $target = $( '.translations' ),
			translationLang = translation.title.split( '/' ).pop();

		$target.append( $( '<div>' )
			.addClass( 'row' )
			.append(
				$( '<div>' )
					.addClass( 'four columns source' )
					.text( translation.definition ),
				$( '<div>' )
					.addClass( 'four columns translation' )
					.append(
						$( '<div>' ).text( translation.translation )
							.prop( {
								dir: $.uls.data.getDir( translationLang ),
								lang: translationLang
							} ),
						$( '<div>' )
							.addClass( 'info autonym' )
							.prop( {
								dir: $.uls.data.getDir( translationLang ),
								lang: translationLang
							} )
							.text(
								$.uls.data.getAutonym( translationLang )
							)
					),
				$( '<div>' )
					.addClass( 'four columns comparison' )
					.append(
						$( '<div>' ).text( translation.comparison || '' ),
						$( '<div>' )
							.addClass( 'info' )
							.text( translation.title )
					)
			)
		);
	}

	/**
	 * Display when multiple requests are checked.
	 */
	function displayOnMultipleSelection() {
		var selectedUserIDs = $( '.request-selector:checked' ).map( function ( i, checkedBox ) {
			return $( checkedBox ).parents( 'div.request' ).data( 'data' ).userid;
		} ).toArray();

		$( '.details.pane' ).empty().append(
			$( '<div>' )
				.addClass( 'tsb-header row' ),
			$( '<div>' )
				.addClass( 'actions row' )
				.append(
					$( '<button>' )
						.addClass( 'accept-all mw-ui-button mw-ui-progressive' )
						.text( mw.msg( 'tsb-accept-all-button-label' ) )
						.on( 'click', function () {
							mw.notify( mw.msg( 'tsb-accept-confirmation', selectedUserIDs.length ) );

							window.tsbUpdatingUsers = true;

							doApiAction( {
								userid: selectedUserIDs,
								do: 'promote'
							} ).done( function () {
								removeSelectedRequests();

								window.tsbUpdatingUsers = false;
							} );
						} ),
					$( '<button>' )
						.addClass( 'reject-all mw-ui-button mw-ui-destructive' )
						.text( mw.msg( 'tsb-reject-all-button-label' ) )
						.on( 'click', function () {
							mw.notify( mw.msg( 'tsb-reject-confirmation', selectedUserIDs.length ) );

							window.tsbUpdatingUsers = true;

							doApiAction( {
								userid: selectedUserIDs,
								do: 'delete'
							} ).done( function () {
								removeSelectedRequests();

								window.tsbUpdatingUsers = false;
							} );
						} )
				)
		);
	}

	/**
	 * Updates the counter of the selected users.
	 *
	 * @param {number} count The number of selected users
	 */
	function updateSelectedIndicator( count ) {
		var text = mw.msg( 'tsb-selected-count', mw.language.convertNumber( count ) );

		$( '.requests.pane .request-footer .selected-counter' ).text( text );
		if ( count > 1 ) {
			$( '.details.pane .tsb-header' ).text( text );
		}
	}

	/**
	 * Returns older requests with the same number of translations.
	 *
	 * @return {jQuery} Older requests
	 */
	function getOlderRequests() {
		var $lastSelectedRequest = $( '.row.request.selected' ).last(),
			currentTranslationCount = $lastSelectedRequest.data( 'data' ).translations;

		return $lastSelectedRequest.nextAll( ':not(.hide)' ).filter( function () {
			return ( $( this ).data( 'data' ).translations === currentTranslationCount );
		} );
	}

	/**
	 * Updates the number of older requests with the same number
	 * of translations at the link in the bottom of the requests row
	 * or hides that link if there are no such requests.
	 */
	function indicateOlderRequests() {
		var $olderRequests = getOlderRequests(),
			$olderRequestsIndicator = $( '.older-requests-indicator' );

		var oldRequestsCount = $olderRequests.length;
		var oldRequestsCountString = mw.language.convertNumber( oldRequestsCount );

		if ( oldRequestsCount ) {
			$olderRequestsIndicator
				.text( mw.msg( 'tsb-older-requests', oldRequestsCountString ) )
				.removeClass( 'hide' );
		} else {
			$olderRequestsIndicator
				.addClass( 'hide' );
		}
	}

	/**
	 * Updates the number of requests.
	 */
	function updateRequestCount() {
		var $requests = $( '.requests-list .request' ),
			visibleRequestsCount = $requests.filter( ':not(.hide)' ).length;

		$( '.request-count' ).text(
			mw.msg( 'tsb-request-count', mw.language.convertNumber( visibleRequestsCount ) )
		);

		if ( $requests.length === 0 ) {
			$( '.details.pane' )
				.empty()
				.append(
					$( '<div>' )
						.addClass( 'tsb-header row' )
						.text( mw.msg( 'tsb-no-requests-from-new-users' ) )
				);
		}
	}

	/**
	 * Sets the height of the panes to the window height.
	 */
	function setPanesHeight() {
		var $detailsPane = $( '.details.pane' ),
			$requestsPane = $( '.requests.pane' ),
			detailsHeight = $( window ).height() - $detailsPane.offset().top,
			requestsHeight = detailsHeight -
				$requestsPane.find( '.request-footer' ).height() -
				$requestsPane.find( '.request-header' ).height();

		$detailsPane.css( 'max-height', detailsHeight );
		$requestsPane.find( '.requests-list' ).css( 'max-height', requestsHeight );
	}

	function selectAllRequests() {
		var $requestCheckboxes = $( '.request-selector' ),
			$detailsPane = $( '.details.pane' ),
			$selectAll = $( '.request-selector-all' ),
			$requestRows = $( '.requests .request' ),
			selectAllChecked = $selectAll.prop( 'checked' ),
			$visibleRows = $requestRows.not( '.hide' );

		$visibleRows.each( function ( index, row ) {
			$( row ).find( '.request-selector' ).prop( {
				checked: selectAllChecked,
				disabled: false
			} );
		} );

		var selectedCount;
		if ( selectAllChecked ) {
			displayOnMultipleSelection();
			$visibleRows.addClass( 'selected' );
			selectedCount = $requestCheckboxes.filter( ':checked' ).length;
		} else {
			$detailsPane.empty();
			$requestRows.removeClass( 'selected' );
			selectedCount = 0;
		}

		updateSelectedIndicator( selectedCount );
		indicateOlderRequests();
	}

	/**
	 * Handle click on request row
	 *
	 * @param {jQuery.Event} e
	 */
	function onSelectRequest( e ) {
		var $requestRow = $( e.target ).closest( '.request' ),
			$requestRows = $( '.requests .request' ),
			$selectAll = $( '.request-selector-all' );

		displayRequestDetails( $requestRow.data( 'data' ) );

		// Clicking a row makes only that row selected and unselects all other rows
		$requestRows.each( function ( i, row ) {
			var $row = $( row );

			if ( row === $requestRow[ 0 ] ) {
				$row.addClass( 'selected' )
					.find( '.request-selector' ).prop( {
						checked: true,
						disabled: true
					} );
			} else {
				$row.removeClass( 'selected' )
					.find( '.request-selector' ).prop( {
						checked: false,
						disabled: false
					} );
			}
		} );

		$selectAll.prop( 'indeterminate', true );

		updateSelectedIndicator( 1 );
		indicateOlderRequests();
	}

	/**
	 * Event handler for request checkbox selection.
	 *
	 * @param {jQuery.Event} e
	 */
	function requestSelectHandler( e ) {
		var request = e.target,
			$detailsPane = $( '.details.pane' ),
			$requestCheckboxes = $( '.request-selector' ),
			$selectAll = $( '.request-selector-all' ),
			$thisRequestRow = $( request ).parents( 'div.request' );

		// Uncheck the rows that were selected by clicking the row
		$requestCheckboxes.filter( ':disabled' ).prop( 'disabled', false );

		if ( request.checked ) {
			$thisRequestRow.addClass( 'selected' );
		} else {
			$thisRequestRow.removeClass( 'selected' );
		}

		var $checkedBoxes = $requestCheckboxes.filter( ':checked' );
		var checkedCount = $checkedBoxes.length;

		if ( checkedCount === $requestCheckboxes.length ) {
			// All boxes are selected
			$selectAll.prop( {
				checked: true,
				indeterminate: false
			} );

			displayOnMultipleSelection();
		} else if ( checkedCount === 0 ) {
			// No boxes are selected
			$selectAll.prop( {
				checked: false,
				indeterminate: false
			} );

			$detailsPane.empty();
		} else if ( checkedCount === 1 ) {
			$selectAll.prop( {
				checked: false,
				indeterminate: true
			} );

			$checkedBoxes.prop( 'disabled', true );

			// Here we know that only one checkbox is selected,
			// so it's OK to query the data from it
			displayRequestDetails( $checkedBoxes.parents( 'div.request' ).data( 'data' ) );
		} else {
			$selectAll.prop( {
				checked: false,
				indeterminate: true
			} );

			displayOnMultipleSelection();
		}

		updateSelectedIndicator( checkedCount );
		indicateOlderRequests();

		e.stopPropagation();
	}

	/**
	 * Old request click handler.
	 *
	 * @param {jQuery.Event} e
	 */
	function oldRequestSelector( e ) {
		e.preventDefault();

		getOlderRequests().each( function ( index, request ) {
			$( request ).find( '.request-selector' )
				.prop( 'checked', true ) // Otherwise the state doesn't actually change
				.trigger( 'change' );
		} );
	}

	// ======================================
	// LanguageFilter plugin
	// ======================================
	function LanguageFilter( element ) {
		this.$selector = $( element );
		this.init();
	}

	LanguageFilter.prototype.init = function () {
		var languageFilter = this;

		var $clearButton = $( '<button>' )
			.addClass( 'clear-language-selector hide' )
			.text( '×' );

		languageFilter.$selector.after( $clearButton );
		// Activate language selector
		languageFilter.$selector.uls( {
			onSelect: function ( language ) {
				languageFilter.$selector
					.removeClass( 'unselected' )
					.addClass( 'selected autonym' )
					.prop( {
						dir: $.uls.data.getDir( language ),
						lang: language
					} )
					.text( $.uls.data.getAutonym( language ) );

				languageFilter.filter( language );
				$clearButton.removeClass( 'hide' );
				indicateOlderRequests();
			},
			ulsPurpose: 'translate-special-managetranslatorsandbox',
			quickList: mw.uls.getFrequentLanguageList
		} );

		$clearButton.on( 'click', function () {
			var userLang = mw.config.get( 'wgUserLanguage' );

			languageFilter.$selector
				.removeClass( 'selected autonym' )
				.prop( {
					dir: $.uls.data.getDir( userLang ),
					lang: userLang
				} )
				.addClass( 'unselected' )
				.text( mw.msg( 'tsb-all-languages-button-label' ) );

			languageFilter.filter();
			$clearButton.addClass( 'hide' );
		} );
	};

	/**
	 * Filter the requests by language.
	 *
	 * @param {string} [language] Language code
	 */
	LanguageFilter.prototype.filter = function ( language ) {
		var $requests = $( '.request' );

		$requests.each( function ( index, request ) {
			var $request = $( request ),
				requestData = $request.data( 'data' );

			if ( !language ||
				( requestData.languagepreferences &&
					requestData.languagepreferences.languages &&
					requestData.languagepreferences.languages.indexOf( language ) > -1 )
			) {
				// Found language
				$request.removeClass( 'hide' );
			} else {
				$request.addClass( 'hide' );
			}
		} );

		updateAfterFiltering();
	};

	$.fn.languageFilter = function () {
		return this.each( function () {
			if ( !$.data( this, 'LanguageFilter' ) ) {
				$.data( this, 'LanguageFilter', new LanguageFilter( this ) );
			}
		} );
	};

	// ======================================
	// TranslatorSearch plugin
	// ======================================
	function TranslatorSearch( element ) {
		this.$search = $( element );
		this.init();
	}

	TranslatorSearch.prototype.init = function () {
		this.$search.on( 'search keyup', this.keyup.bind( this ) );
	};

	TranslatorSearch.prototype.keyup = function () {
		var translatorSearch = this;

		// Respond to the keypress events after a small timeout to avoid freeze when typed fast
		delay( function () {
			var query = translatorSearch.$search.val().trim().toLowerCase();
			translatorSearch.filter( query );
		}, 300 );
	};

	TranslatorSearch.prototype.filter = function ( query ) {
		var $requests = $( '.request' );

		$requests.each( function ( index, request ) {
			var $request = $( request ),
				requestData = $request.data( 'data' );

			if ( query.length === 0 ||
				requestData.username.toLowerCase().indexOf( query ) === 0 ||
				requestData.email.toLowerCase().indexOf( query ) === 0
			) {
				$request.removeClass( 'hide' );
			} else {
				$request.addClass( 'hide' );
			}
		} );

		updateAfterFiltering();
	};

	function updateAfterFiltering() {
		var $firstVisibleUser = $( '.request:not(.hide)' ).first();

		if ( $firstVisibleUser.length ) {
			$firstVisibleUser.trigger( 'click' );
		} else {
			$( '.details.pane' ).empty();
			var $selectedRequests = $( '.request-selector:checked' );
			$selectedRequests.closest( '.request' ).removeClass( 'selected' );
			$selectedRequests.prop( {
				checked: false,
				disabled: false
			} );

			updateSelectedIndicator( 0 );
		}

		updateRequestCount();
	}

	$.fn.translatorSearch = function () {
		return this.each( function () {
			if ( !$.data( this, 'TranslatorSearch' ) ) {
				$.data( this, 'TranslatorSearch', new TranslatorSearch( this ) );
			}
		} );
	};

	delay = ( function () {
		var timer = 0;

		return function ( callback, milliseconds ) {
			clearTimeout( timer );
			timer = setTimeout( callback, milliseconds );
		};
	}() );

	$( function () {
		var $requestCheckboxes = $( '.request-selector' ),
			$selectAll = $( '.request-selector-all' ),
			$requestRows = $( '.requests .request' );

		// Delay so we get the correct height on page load
		window.setTimeout( setPanesHeight, 0 );
		$( window ).on( 'resize', setPanesHeight );

		$( '.request-filter-box' ).translatorSearch();
		$( '.language-selector' ).languageFilter();

		// Handle clicks for the 'Select all' checkbox
		$selectAll.on( 'click', selectAllRequests );

		// Handle clicks on request checkboxes.
		$requestCheckboxes.on( 'click change', requestSelectHandler );

		// Handle clicks on request rows.
		$requestRows.on( 'click', onSelectRequest );

		$( '.older-requests-indicator' ).on( 'click', oldRequestSelector );

		if ( $requestRows.length ) {
			$requestRows.first().trigger( 'click' );
		}

		updateRequestCount();
	} );
}() );
