/* global $ */
/* See T354224 for information on the @wikimedia/mediawiki.skins.clientpreferences module. */
const clientPrefs = require( '@wikimedia/mediawiki.skins.clientpreferences' ),
	toast = require( './mobile.startup/showOnPageReload' ),
	EXPAND_SECTIONS_KEY = 'mf-expand-sections',
	WP25_EASTER_EGGS_KEY = 'wp25eastereggs-enable',
	msg = mw.msg,
	USER_FONT_SIZE_SMALL = 'small',
	USER_FONT_SIZE_REGULAR = 'regular',
	USER_FONT_SIZE_LARGE = 'large',
	THEME = 'skin-theme',
	// FIXME: This value should be synced between back-end and front-end code,
	// but it's currently hard-coded because ResourceLoader virtual imports
	// i.e. require( './config.json') are incompatible with the
	// Webpack build. Requires updating to Webpack 5 and common.js magic comments.
	// https://webpack.js.org/configuration/module/#moduleparserjavascriptcommonjsmagiccomments
	// e.g: require(/* webpackIgnore: true */ './config.json');
	FONT_SIZE_KEY = 'mf-font-size';

/**
 * Notifies the user that settings were asynchronously saved.
 *
 * @private
 * @param {boolean} [isPending] if set toast will show after page has been reloaded.
 */
function notify( isPending ) {
	if ( isPending ) {
		toast.showOnPageReload( msg( 'mobile-frontend-settings-save' ) );
	} else {
		mw.notify( msg( 'mobile-frontend-settings-save' ) );
	}
}

let api;
/**
 * @ignore
 * @param {Object<string,string|number>} options
 * @return {JQuery.Promise<Object>}
 */
function saveOptions( options ) {
	api = api || new mw.Api();
	// @ts-ignore
	return api.saveOptions( options, {
		global: 'update'
	} );
}

/**
 * Adds a font changer field to the form
 *
 * @private
 * @param {jQuery.Object} $form
 * @param {Record<string,ClientPreference>} clientPreferences
 * @return {Promise<Node>}
 */
function addClientPreferencesToForm( $form, clientPreferences ) {
	const cp = document.createElement( 'div' );
	const id = 'mf-client-preferences';
	cp.id = id;
	$form.prepend( cp );
	return clientPrefs.render( `#${ id }`, clientPreferences, { saveOptions } );
}

/**
 * Helper method to infuse checkbox elements with OO magic
 * Additionally it applies all known hacks to make it mobile friendly
 *
 * @private
 * @param {Object} toggleObject a toggle object to infuse
 * @param {jQuery.Object} $form form to submit when there is interaction with toggle
 */
function infuseToggle( toggleObject, $form ) {
	const $toggleElement = toggleObject.$el;
	const enableToggle = OO.ui.infuse( $toggleElement );
	const $checkbox = enableToggle.$element;
	const toggleSwitch = new OO.ui.ToggleSwitchWidget( {
		value: enableToggle.isSelected()
	} );

	// Strangely the ToggleSwitchWidget does not behave as an input so any change
	// to it is not reflected in the form. (see T182466)
	// Ideally we'd replaceWith here and not have to hide the original element.
	toggleSwitch.$element.insertAfter( $checkbox );
	// although the checkbox is hidden already, that is done via visibility
	// as a result, it still takes up space. We don't want it to any more now that the
	// new toggle switch has been added.
	$checkbox.hide();

	// listening on checkbox change is required to make the clicking on label working.
	// Otherwise clicking on label changes the checkbox "checked" state
	// but it's not reflected in the toggle switch
	$checkbox.on( 'change', () => {
		// disable checkbox as submit is delayed by 0.25s
		$checkbox.attr( 'disabled', true );
		toggleSwitch.setValue( enableToggle.isSelected() );
	} );
	toggleSwitch.on( 'change', ( value ) => {
		// execute callback
		toggleObject.onToggle( value );

		// ugly hack, we're delaying submit form by 0.25s
		// and we want to disable registering clicks
		// we want to disable the toggleSwitch
		// but we cannot use setDisabled(true) as it makes button gray
		toggleSwitch.setValue = function () {};

		$checkbox.find( 'input' )
			.prop( 'checked', value );
		notify( true );
		// On some Android devices animation gets stuck in the middle as browser
		// starts submitting the form.
		// Let's call submit on the form after toggle button transition is done
		// (0.25s, defined in OOUI)
		setTimeout( () => {
			$form.trigger( 'submit' );
		}, 250 );
	} );
}

/**
 * Add features, that depends on localStorage, such as "expand all sections" or "fontchanger".
 * The checkbox is used for turning on/off expansion of all sections on page load.
 *
 * @private
 */
function initMobileOptions() {
	const $form = $( '#mobile-options' ),
		$amcToggle = $( '#enable-amc-toggle' );

	if ( $amcToggle.length ) {
		infuseToggle( { $el: $amcToggle }, $form );
	}

	const clientPreferences = {};
	const skin = mw.config.get( 'skin' );

	if ( skin === 'minerva' ) {
		clientPreferences[ WP25_EASTER_EGGS_KEY ] = {
			options: [
				'0',
				'1'
			],
			type: 'switch',
			preferenceKey: WP25_EASTER_EGGS_KEY,
			callback: notify
		};
	}

	if ( mw.config.get( 'wgMFEnableFontChanger' ) ) {
		clientPreferences[ FONT_SIZE_KEY ] = {
			options: [
				USER_FONT_SIZE_SMALL,
				USER_FONT_SIZE_REGULAR,
				USER_FONT_SIZE_LARGE
			],
			preferenceKey: FONT_SIZE_KEY,
			callback: notify
		};
	}

	clientPreferences[ THEME ] = {
		options: [ 'day', 'night', 'os' ],
		preferenceKey: `${ skin }-theme`
	};

	clientPreferences[ EXPAND_SECTIONS_KEY ] = {
		options: [
			'0',
			'1'
		],
		type: 'switch',
		preferenceKey: EXPAND_SECTIONS_KEY,
		callback: notify
	};

	if ( !mw.user.isAnon() ) {
		clientPreferences[ 'mw-mf-amc' ] = {
			options: [
				'0',
				'1'
			],
			type: 'switch',
			preferenceKey: 'mf_amc_optin',
			callback: () => location.reload()
		};
	}

	// Transport existing links to new layout.

	/**
	 * Currently toggle switches have duplicate headings and a description that is not
	 * part of the toggle switch layout.
	 * This works around this to retain the classic MobileFrontend layout for these
	 * controls.
	 * FIXME: This should be upstreamed to @wikimedia/mediawiki.skins.clientpreferences
	 *
	 * @param {jQuery} $node
	 */
	function modifyToggleSwitch( $node ) {
		// Move the description from the heading into the label.
		$node.find( '.skin-client-pref-description' )
			.appendTo( $node.find( '.cdx-toggle-switch__label' ) );
		// Drop the duplicate label.
		$node.find( '> label' ).remove();
	}

	addClientPreferencesToForm( $form, clientPreferences ).then( () => {
		// Make some modifications that are currently not supported by the Vector client preferences
		// Move the links from the server side preference into the row.
		$( '#amc-field .option-links' ).appendTo( '#skin-client-prefs-mw-mf-amc' );
		modifyToggleSwitch( $( '#skin-client-prefs-mf-expand-sections' ) );
		modifyToggleSwitch( $( '#skin-client-prefs-wp25eastereggs-enable' ) );
		modifyToggleSwitch( $( '#skin-client-prefs-mw-mf-amc' ) );
		// Remove the server side rendered OOUI field.
		$( '#amc-field' ).remove();

		// For the purposes of ReaderExperiments StickyHeaders prototype only:
		// - This code toggles on and disables the expand section setting for enrolled users T409485
		// - Please remove code after the stickyHeaders A/B experiment is complete T410754
		if ( mw.config.get(
			'wgReaderExperimentsStickyHeaders' ) &&
			mw.config.get( 'wgReaderExperimentsStickyHeaders' ) === 'enrolled'
		) {
			const $toggleSwitch = $( '#skin-client-prefs-mf-expand-sections input' );

			if ( $toggleSwitch ) {
				$toggleSwitch.prop( 'checked', true );
				$toggleSwitch.prop( 'disabled', true );
			}
		}
	} );
}

if ( !window.QUnit ) {
	mw.loader.using( 'oojs-ui-widgets' ).then( initMobileOptions );
}

module.exports = {
	test: {
		addClientPreferencesToForm
	}
};
