/**
 * WordPress dependencies
 */
import {
	SelectControl,
	TextareaControl,
	TextControl,
} from '@safe-wordpress/components';
import { useDispatch } from '@safe-wordpress/data';
import { _x, sprintf } from '@safe-wordpress/i18n';

/**
 * External dependencies
 */
import { countBy, drop, take, toPairs } from 'lodash';
import { createScopeRule } from '@nab/utils';
import type {
	QueryArgSetting,
	TestedUrlWithQueryArgsScopeRule,
} from '@nab/types';

/**
 * Internal dependencies
 */
import './style.scss';
import { store as NAB_EDITOR } from '../../../../store';

export type TestedUrlWithQueryArgsScopeProps = {
	readonly rules: ReadonlyArray< TestedUrlWithQueryArgsScopeRule >;
};

export const TestedUrlWithQueryArgsScope = ( {
	rules,
}: TestedUrlWithQueryArgsScopeProps ): JSX.Element => {
	const { setScopeRules } = useDispatch( NAB_EDITOR );
	const rule = rules[ 0 ] ?? DEFAULT_RULE;
	const values = rule.attributes.value.args ?? [];
	return (
		<div className="tested-url-with-query-args-scope">
			<p>
				{ _x(
					'Test will be active on tested URLs if their query parameters match the following conditions:',
					'text',
					'nelio-ab-testing'
				) }
			</p>
			<QueryArgSettings
				value={ values }
				onChange={ ( args ) =>
					setScopeRules( [
						{
							...rule,
							attributes: {
								...rule.attributes,
								value: {
									...rule.attributes.value,
									args,
								},
							},
						},
					] )
				}
			/>
		</div>
	);
};

// ============
// HELPER VIEWS
// ============

const QueryArgSettings = ( {
	value: args,
	onChange,
}: {
	readonly value: ReadonlyArray< QueryArgSetting >;
	readonly onChange: ( args: ReadonlyArray< QueryArgSetting > ) => void;
} ): JSX.Element => {
	const names = countBy( args, 'name' );
	return (
		<div className="tested-url-with-query-args-scope">
			<div className="tested-url-with-query-args-scope__block">
				<div className="tested-url-with-query-args-scope__arguments">
					{ [ ...args, NEW_ARG ].map( ( arg, index ) => (
						<QueryArgSetting
							key={ index }
							value={ arg }
							hasDuplicateName={
								!! arg.name && ( names[ arg.name ] ?? 0 ) >= 2
							}
							onChange={ ( newArg ) =>
								onChange( [
									...take( args, index ),
									{ ...arg, ...newArg },
									...drop( args, index + 1 ),
								] )
							}
						/>
					) ) }
				</div>
			</div>
		</div>
	);
};

const QueryArgSetting = ( {
	hasDuplicateName,
	value: setting,
	onChange,
}: {
	readonly hasDuplicateName: boolean;
	readonly value: QueryArgSetting;
	readonly onChange: ( attrs: Partial< QueryArgSetting > ) => void;
} ): JSX.Element => (
	<div
		className="tested-url-with-query-args-scope__argument"
		data-operator={ _x( 'and', 'text', 'nelio-ab-testing' ) }
	>
		<div className="tested-url-with-query-args-scope__argument-setup">
			<TextControl
				placeholder={ _x( 'Name', 'text', 'nelio-ab-testing' ) }
				value={ setting.name }
				onChange={ ( name ) => onChange( { name } ) }
			/>
			<SelectControl
				options={ CONDITIONS }
				value={ setting.condition }
				onChange={ ( condition ) => onChange( { condition } ) }
			/>
		</div>
		<div className="tested-url-with-query-args-scope__argument-value">
			<QueryArgValue
				type={ setting.condition }
				value={ setting.value }
				onChange={ ( value ) => onChange( { value } ) }
			/>
		</div>
		{ hasDuplicateName && (
			<div className="tested-url-with-query-args-scope__argument-error">
				{ sprintf(
					/* translators: %s: Query parameter’s name. */
					_x(
						'Query parameter “%s” should only appear once in the test scope',
						'text',
						'nelio-ab-testing'
					),
					setting.name
				) }
			</div>
		) }
	</div>
);

const QueryArgValue = ( {
	type,
	value,
	onChange,
}: {
	readonly type: QueryArgSetting[ 'condition' ];
	readonly value: string;
	readonly onChange: ( value: string ) => void;
} ): JSX.Element | null => {
	switch ( type ) {
		case 'exists':
		case 'does-not-exist':
			return null;

		case 'is-equal-to':
		case 'is-not-equal-to':
		case 'contains':
		case 'does-not-contain':
			return (
				<TextControl
					placeholder={ _x( 'Value', 'text', 'nelio-ab-testing' ) }
					value={ value }
					onChange={ onChange }
				/>
			);

		case 'is-any-of':
		case 'is-none-of':
			return (
				<TextareaControl
					placeholder={ _x(
						'Value (one per line)',
						'text',
						'nelio-ab-testing'
					) }
					value={ value }
					onChange={ onChange }
				/>
			);
	}
};

// ====
// DATA
// ====

const NEW_ARG: QueryArgSetting = {
	name: '',
	condition: 'exists',
	value: '',
};

const DEFAULT_RULE = createScopeRule( {
	type: 'tested-url-with-query-args',
	value: { urls: [], args: [] },
} ) as TestedUrlWithQueryArgsScopeRule;

type Condition = QueryArgSetting[ 'condition' ];
const CONDITION_LABEL_RECORD: Record< Condition, string > = {
	exists: _x( 'Exists', 'text', 'nelio-ab-testing' ),
	'does-not-exist': _x( 'Does not exist', 'text', 'nelio-ab-testing' ),
	'is-equal-to': _x( 'Is equal to', 'text', 'nelio-ab-testing' ),
	'is-not-equal-to': _x( 'Is not equal to', 'text', 'nelio-ab-testing' ),
	contains: _x( 'Contains', 'text', 'nelio-ab-testing' ),
	'does-not-contain': _x( 'Does not contain', 'text', 'nelio-ab-testing' ),
	'is-any-of': _x( 'Is any of', 'text', 'nelio-ab-testing' ),
	'is-none-of': _x( 'Is none of', 'text', 'nelio-ab-testing' ),
};
const CONDITIONS = toPairs( CONDITION_LABEL_RECORD ).map(
	( [ value, label ] ) => ( {
		label,
		value,
	} )
);
