'use strict';

const { Experiment, UnenrolledExperiment, OverriddenExperiment } = require( './Experiment.js' );

const COOKIE_NAME = 'mpo';
/**
 * @type {Object}
 * @property {string} EveryoneExperimentEventIntakeServiceUrl
 * @property {string} LoggedInExperimentEventIntakeServiceUrl
 * @property {string} InstrumentEventIntakeServiceUrl
 * @property {Object|false} streamConfigs
 * @property {Object} instrumentConfigs
 * @ignore
 */
const config = require( './config.json' );

const { newMetricsClient, DefaultEventSubmitter } = require( 'ext.eventLogging.metricsPlatform' );

/**
 * @param {Object} streamConfigs
 * @param {string} intakeServiceUrl
 * @return {Object}
 * @ignore
 */
function newMetricsClientInternal( streamConfigs, intakeServiceUrl ) {
	return newMetricsClient( streamConfigs, new DefaultEventSubmitter( intakeServiceUrl ) );
}

const everyoneExperimentMetricsClient = newMetricsClientInternal(
	config.streamConfigs,
	config.EveryoneExperimentEventIntakeServiceUrl
);

const loggedInExperimentMetricsClient = newMetricsClientInternal(
	config.streamConfigs,
	config.LoggedInExperimentEventIntakeServiceUrl
);

/**
 * Gets an {@link mw.xLab.Experiment} instance that encapsulates the result of enrolling the current
 * user into the experiment. You can use that instance to get which group the user was assigned
 * when they were enrolled into the experiment and send experiment-related analytics events.
 *
 * @example
 * const e = mw.xLab.getExperiment( 'my-awesome-experiment' );
 * const myAwesomeDialog = require( 'my.awesome.dialog' );
 *
 * [
 *   'open',
 *   'default-action',
 *   'primary-action'
 * ].forEach( ( event ) => {
 *   myAwesomeDialog.on( event, () => e.send( event ) );
 * } );
 *
 * // Was the current user assigned to the treatment group?
 * if ( e.isAssignedGroup( 'treatment' ) ) {
 *   myAwesomeDialog.primaryAction.label = 'Awesome!';
 * }
 *
 * @param {string} experimentName The experiment name
 * @return {Experiment}
 * @memberof mw.xLab
 */
function getExperiment( experimentName ) {
	const userExperiments = mw.config.get( 'wgMetricsPlatformUserExperiments' );

	if ( !userExperiments || !userExperiments.assigned[ experimentName ] ) {
		mw.log( 'mw.xLab.getExperiment(): The "' + experimentName + '" experiment isn\'t registered. ' +
			'Is the experiment configured and running?' );

		return new UnenrolledExperiment( experimentName );
	}

	const assignedGroup = userExperiments.assigned[ experimentName ];
	const samplingUnit = userExperiments.sampling_units[ experimentName ];
	const isLoggedInExperiment = samplingUnit === 'mw-user';
	const subjectId = isLoggedInExperiment ?
		userExperiments.subject_ids[ experimentName ] :
		'awaiting';

	if ( userExperiments.overrides.includes( experimentName ) ) {
		return new OverriddenExperiment(
			experimentName,
			assignedGroup,
			samplingUnit,
			subjectId
		);
	}

	// Provide an alternate MetricsClient for logged-in experiments to override the
	// eventIntakeServiceUrl set by config (wgMetricsPlatformExperimentEventIntakeServiceUrl
	// = '/evt-103e/v2/events?hasty=true' on production) which drops events if everyone experiment
	// enrollments are not included. DefaultEventSubmitter sets DEFAULT_EVENT_INTAKE_URL to the
	// eventgate-analytics-external cluster. See https://phabricator.wikimedia.org/T395779.
	const metricsClient = isLoggedInExperiment ?
		loggedInExperimentMetricsClient :
		everyoneExperimentMetricsClient;

	return new Experiment(
		metricsClient,
		experimentName,
		assignedGroup,
		subjectId,
		samplingUnit
	);
}

/**
 * Gets a map of experiment to group for all experiments that the current user is enrolled into.
 *
 * This method is internal and should only be used by other Experimentation Lab components.
 * Currently, this method is only used by
 * [the Client Error Logging instrument in WikimediaEvents][0].
 *
 * @internal
 *
 * [0]: https://gerrit.wikimedia.org/r/plugins/gitiles/mediawiki/extensions/WikimediaEvents/+/refs/heads/master/OWNERS.md#client-error-logging
 *
 * @return {Object}
 * @memberof mw.xLab
 */
function getAssignments() {
	const userExperiments = mw.config.get( 'wgMetricsPlatformUserExperiments' );

	return userExperiments ? Object.assign( {}, userExperiments.assigned ) : {};
}

// ---

function setCookieAndReload( value ) {
	mw.cookie.set( COOKIE_NAME, value );

	// Reloading the window will break the QUnit unit tests. Only do so if we're not in a QUnit
	// testing environment.
	if ( !window.QUnit ) {
		window.location.reload();
	}
}

/**
 * Overrides an experiment enrolment and reloads the page.
 *
 * @param {string} experimentName The name of the experiment
 * @param {string} groupName The assigned group that will override the assigned one
 * @memberof mw.xLab
 */
function overrideExperimentGroup(
	experimentName,
	groupName
) {
	const rawOverrides = mw.cookie.get( COOKIE_NAME, null, '' );
	const part = `${ experimentName }:${ groupName }`;

	if ( rawOverrides === '' ) {
		// If the cookie isn't set, then the value of the cookie is the given override.
		setCookieAndReload( part );
	} else if ( !rawOverrides.includes( `${ experimentName }:` ) ) {
		// If the cookie is set but doesn't have an override for the given experiment name/group
		// variant pair, then append the given override.
		setCookieAndReload( `${ rawOverrides };${ part }` );
	} else {
		setCookieAndReload( rawOverrides.replace(
			new RegExp( `${ experimentName }:[A-Za-z0-9][-_.A-Za-z0-9]+?(?=;|$)` ),
			part
		) );
	}
}

/**
 * Clears all enrolment overrides for the experiment and reloads the page.
 *
 * @param {string} experimentName
 * @memberof mw.xLab
 */
function clearExperimentOverride( experimentName ) {
	const rawOverrides = mw.cookie.get( COOKIE_NAME, null, '' );

	let newRawOverrides = rawOverrides.replace(
		new RegExp( `;?${ experimentName }:[A-Za-z0-9][-_.A-Za-z0-9]+` ),
		''
	);

	// If the new cookie starts with a ';' character, then trim it.
	newRawOverrides = newRawOverrides.replace( /^;/, '' );

	// If the new cookie is empty, then clear the cookie.
	newRawOverrides = newRawOverrides || null;

	setCookieAndReload( newRawOverrides );
}

/**
 * Clears all experiment enrolment overrides for all experiments and reloads the page.
 *
 * @memberof mw.xLab
 */
function clearExperimentOverrides() {
	setCookieAndReload( null );
}

// ---

const instrumentMetricsClient = newMetricsClientInternal(
	config.instrumentConfigs,
	config.InstrumentEventIntakeServiceUrl
);

/**
 * Creates a new {@link Instrument} instance using config fetched from xLab.
 *
 * @param {string} instrumentName
 * @return {Instrument}
 * @memberof mw.xLab
 */
function getInstrument( instrumentName ) {
	return instrumentMetricsClient.newInstrument( instrumentName );
}

// ---

/**
 * @namespace mw.xLab
 */
mw.xLab = {
	getExperiment,
	getAssignments,
	getInstrument,
	overrideExperimentGroup,
	clearExperimentOverride,
	clearExperimentOverrides

};

// JS overriding experimentation feature
if ( window.QUnit ) {
	mw.xLab = Object.assign( mw.xLab, {
		Experiment,
		UnenrolledExperiment,
		OverriddenExperiment
	} );
}

require( './Experiments.js' );