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

/**
 * External dependencies
 */
import clsx from 'clsx';
import { keys } from 'lodash';
import { ErrorText } from '@nab/components';
import { usePluginSetting } from '@nab/data';
import {
	createScopeRule,
	isUrlFragmentInvalid,
	getLocalUrlError,
} from '@nab/utils';
import type { CustomUrlScopeRule, ScopeRuleId } from '@nab/types';

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

export type CustomUrlScopeProps = {
	readonly rules: ReadonlyArray< CustomUrlScopeRule >;
	readonly scopeSupport: 'urls-with-tested-post' | 'urls';
};

export const CustomUrlScope = ( {
	rules,
	scopeSupport,
}: CustomUrlScopeProps ): JSX.Element => {
	const [ newRule, setNewRule ] = useState(
		createScopeRule( { type: 'partial', value: '' } ) as CustomUrlScopeRule
	);
	const { setScopeRules } = useDispatch( NAB_EDITOR );

	const editRule = (
		id: ScopeRuleId,
		attributes: CustomUrlScopeRule[ 'attributes' ]
	) => {
		const isNewRule = newRule.id === id;
		if ( isNewRule ) {
			setNewRule(
				createScopeRule( {
					type: 'partial',
					value: '',
				} ) as CustomUrlScopeRule
			);
		}

		const ruleToAdd = { ...newRule, attributes };
		if ( isNewRule ) {
			void setScopeRules( [ ...rules, ruleToAdd ] );
		} else {
			void setScopeRules(
				rules.map( ( r ) => ( r.id === id ? { ...r, attributes } : r ) )
			);
		}
	};

	return (
		<>
			<Explanation rules={ rules } scopeSupport={ scopeSupport } />
			<ul className="nab-experiment-scope__rule-list">
				{ [ ...rules, newRule ].map( ( { id, attributes } ) => (
					<RuleSettings
						key={ id }
						attributes={ attributes }
						setAttributes={ ( attrs ) =>
							editRule( id, { ...attributes, ...attrs } )
						}
					/>
				) ) }
			</ul>
		</>
	);
};

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

const Explanation = ( {
	rules,
	scopeSupport,
}: CustomUrlScopeProps ): JSX.Element => {
	if ( ! rules.length ) {
		return (
			<p>
				{ 'urls-with-tested-post' === scopeSupport
					? _x(
							'Variants will be visible on all the pages of your website, including that of the tested element.',
							'user',
							'nelio-ab-testing'
					  )
					: _x(
							'Variants will be visible on all the pages of your website.',
							'user',
							'nelio-ab-testing'
					  ) }{ ' ' }
				{ _x(
					'If you wish to limit its scope to certain pages only, use the following form to define the URLs in which the test should run.',
					'user',
					'nelio-ab-testing'
				) }
			</p>
		);
	}

	return (
		<p>
			{ 'urls-with-tested-post' === scopeSupport
				? _x(
						'Variants will be visible on the tested element’s page, as well as pages matching one of the following criteria.',
						'text',
						'nelio-ab-testing'
				  )
				: _x(
						'Variants will be visible only on pages matching one of the following criteria.',
						'text',
						'nelio-ab-testing'
				  ) }{ ' ' }
			{ _x(
				'To remove one criterion, leave it empty and save the test.',
				'user',
				'nelio-ab-testing'
			) }
		</p>
	);
};

const RuleSettings = ( {
	attributes,
	setAttributes,
}: {
	readonly attributes: CustomUrlScopeRule[ 'attributes' ];
	readonly setAttributes: (
		attrs: Partial< CustomUrlScopeRule[ 'attributes' ] >
	) => void;
} ): JSX.Element => {
	const error = useError( attributes.type, attributes.value );
	return (
		<li
			className={ clsx( {
				'nab-experiment-scope__rule-list-item': true,
			} ) }
			data-operator={ _x( 'or', 'text', 'nelio-ab-testing' ) }
		>
			<div className="nab-experiment-scope__rule-mode">
				<SelectControl
					value={ attributes.type }
					options={ OPTIONS }
					onChange={ ( type ) => setAttributes( { type } ) }
				/>
			</div>

			<div className="nab-experiment-scope__rule-value">
				<TextControl
					placeholder={ _x( 'Value', 'text', 'nelio-ab-testing' ) }
					value={ attributes.value }
					onChange={ ( value ) => setAttributes( { value } ) }
				/>
				<ErrorText value={ error } />
			</div>
		</li>
	);
};

// =====
// HOOKS
// =====

function useError(
	type: CustomUrlScopeRule[ 'attributes' ][ 'type' ],
	value: string
): string {
	const homeUrl = usePluginSetting( 'homeUrl' );
	switch ( type ) {
		case 'partial':
		case 'partial-not-included':
			return value ? isUrlFragmentInvalid( value ) || '' : '';
		case 'exact':
		case 'different':
			return value ? getLocalUrlError( value, homeUrl ) || '' : '';
	}
}

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

const OPTIONS_RECORD: Record<
	CustomUrlScopeRule[ 'attributes' ][ 'type' ],
	string
> = {
	exact: _x( 'URL is', 'text', 'nelio-ab-testing' ),
	different: _x( 'URL is not', 'text', 'nelio-ab-testing' ),
	partial: _x( 'URL contains', 'text', 'nelio-ab-testing' ),
	'partial-not-included': _x(
		'URL does not contain',
		'text',
		'nelio-ab-testing'
	),
};

const OPTIONS = keys( OPTIONS_RECORD )
	.map( ( value ) => value as CustomUrlScopeRule[ 'attributes' ][ 'type' ] )
	.map( ( value ) => ( {
		label: OPTIONS_RECORD[ value ],
		value,
	} ) );
