/**
 * WordPress dependencies
 */
import {
	Button,
	DropdownMenu,
	Modal,
	Path,
	SelectControl,
	SVG,
	TextControl,
} from '@safe-wordpress/components';
import { useEffect, useState } from '@safe-wordpress/element';
import { _x, sprintf } from '@safe-wordpress/i18n';
import { addQueryArgs, getQueryArg } from '@safe-wordpress/url';

/**
 * External dependencies
 */
import { findIndex, noop } from 'lodash';
import { usePageAttribute, usePluginSetting } from '@nab/data';
import { getLetter } from '@nab/utils';
import type {
	Alternative,
	AlternativeId,
	Maybe,
	CssSelectorFinderState as State,
} from '@nab/types';

/**
 * Internal dependencies
 */
import './style.scss';

export type CssSelectorFinderProps = {
	readonly initialUrl: string;
	readonly initialMode: State[ 'mode' ];
	readonly initialValue: string;
	readonly alternatives: ReadonlyArray< Alternative >;
	readonly onClose?: () => void;
	readonly onAccept?: ( mode: State[ 'mode' ], val: string ) => void;
};

export const CssSelectorFinder = ( {
	initialUrl,
	initialMode,
	initialValue,
	...props
}: CssSelectorFinderProps ): JSX.Element => {
	const [ state, setState ] = useCssFinderState( initialMode, initialValue );

	const validationMessage = useValidationMessage( state );

	return (
		<Modal
			className="nab-css-selector-finder"
			title={
				(
					<Title
						initialUrl={ initialUrl }
						state={ state }
						setState={ ( v ) => v && setState( v ) }
						validationMessage={ validationMessage }
						{ ...props }
					/>
				 ) as unknown as string
			}
			onRequestClose={ noop }
			isDismissible={ false }
			style={ {
				width: '100%',
				height: '100%',
				padding: 0,
			} }
		>
			<iframe
				className="nab-css-selector-finder__iframe"
				title={ _x(
					'CSS Selector Finder',
					'text',
					'nelio-ab-testing'
				) }
				src={ addQueryArgs( state.initialUrl || initialUrl, {
					'nab-css-selector-finder': true,
				} ) }
				onLoad={ () => setState( state ) }
			></iframe>
		</Modal>
	);
};

type TitleProps = Omit<
	CssSelectorFinderProps,
	'initialValue' | 'initialMode'
> & {
	readonly state: State;
	readonly setState: ( s?: State ) => void;
	readonly validationMessage?: string;
};

const Title = ( {
	alternatives,
	initialUrl,
	state,
	setState,
	validationMessage,
	onClose = noop,
	onAccept = noop,
}: TitleProps ): JSX.Element => {
	const options = alternatives.map( ( { id }, i ) => ( {
		label: getLabel( i ),
		value: id,
	} ) );

	const { mode, alternative, value, interactionMode } = state;
	const isHovering = 'navigation' !== interactionMode;
	const placeholder = ( (): string => {
		switch ( mode ) {
			case 'id':
				return _x( 'Type an element ID…', 'user', 'nelio-ab-testing' );
			case 'class':
				return _x( 'Type a class name…', 'user', 'nelio-ab-testing' );
			case 'css':
				return isHovering
					? _x(
							'Click on an element or type a CSS selector…',
							'user',
							'nelio-ab-testing'
					  )
					: _x( 'Type a CSS selector…', 'user', 'nelio-ab-testing' );
		}
	} )();

	const loadAlternative = ( id: AlternativeId ) => {
		const index = findIndex( options, { value: id } );
		setState( {
			...state,
			alternative: index,
			initialUrl: alternatives[ index ]?.links.preview,
		} );
	};

	const homeUrl = usePreviewHomeUrl( state.initialUrl || initialUrl );
	const loadHome = homeUrl
		? () =>
				setState( {
					...state,
					initialUrl: addQueryArgs( homeUrl, {
						'nab-css-selector-finder': true,
						'nab-home':
							'1' ===
							getQueryArg( state?.initialUrl ?? '', 'nab-home' )
								? 2
								: 1,
					} ),
				} )
		: undefined;

	const setValue = ( val: string ) =>
		setState( {
			...state,
			value: val,
		} );

	const cleanState = () => setState( undefined );

	return (
		<div className="nab-css-selector-finder__header">
			<div className="nab-css-selector-finder__header-left">
				<SelectControl
					options={ options }
					value={ options[ alternative ]?.value }
					onChange={ loadAlternative }
				/>
				{ !! loadHome && (
					<Button
						variant="primary"
						className="nab-css-selector-finder__action"
						onClick={ loadHome }
						icon="admin-home"
					/>
				) }
			</div>

			<div className="nab-css-selector-finder__header-center">
				<TextControl
					className="nab-css-selector-finder__value"
					placeholder={ placeholder }
					value={ value }
					onChange={ setValue }
					help={ validationMessage }
				/>
				{ mode === 'css' && (
					<DropdownMenu
						className="nab-css-selector-finder__interactivity-mode"
						icon={
							isHovering ? SingleInspectorIcon : NavigationIcon
						}
						label={
							isHovering
								? _x(
										'Select an element',
										'user',
										'nelio-ab-testing'
								  )
								: _x(
										'Navigate your site',
										'user',
										'nelio-ab-testing'
								  )
						}
						toggleProps={ { variant: 'primary' } }
						controls={ [
							{
								title: _x(
									'Navigate your site',
									'user',
									'nelio-ab-testing'
								),
								isActive: ! isHovering,
								icon: NavigationIcon,
								onClick: () =>
									setState( {
										...state,
										interactionMode: 'navigation',
									} ),
							},
							{
								title: _x(
									'Select an element',
									'user',
									'nelio-ab-testing'
								),
								isActive: isHovering,
								icon: SingleInspectorIcon,
								onClick: () =>
									setState( {
										...state,
										interactionMode: 'single-inspector',
									} ),
							},
						] }
					/>
				) }
			</div>

			<div className="nab-css-selector-finder__header-right">
				<Button
					variant="secondary"
					className="nab-css-selector-finder__action"
					onClick={ () => {
						onClose();
						cleanState();
					} }
				>
					{ _x( 'Cancel', 'command', 'nelio-ab-testing' ) }
				</Button>

				<Button
					variant="primary"
					className="nab-css-selector-finder__action"
					disabled={ ! value }
					onClick={ () => {
						onAccept( mode, value );
						onClose();
						cleanState();
					} }
				>
					{ _x( 'OK', 'command', 'nelio-ab-testing' ) }
				</Button>
			</div>
		</div>
	);
};

const SingleInspectorIcon = (): JSX.Element => (
	<SVG
		xmlns="http://www.w3.org/2000/svg"
		viewBox="0 0 24 24"
		width="24"
		height="24"
	>
		<Path d="M 11.111328,0.953125 V 1.8046875 C 6.2950661,2.2761068 2.4488845,6.1214973 1.9765625,10.9375 H 1.125 v 2 h 0.8515625 c 0.472322,4.816002 4.3185036,8.661395 9.1347655,9.132812 v 0.851563 h 2 v -0.853516 -2.011718 -1.574219 c -0.331425,0.05268 -0.666373,0.0801 -1.001953,0.08203 -0.334226,-0.0015 -0.66786,-0.02825 -0.998047,-0.08008 v 1.572266 C 7.3830873,19.60592 4.4418121,16.66547 3.9902344,12.9375 H 5.5625 c -0.05127,-0.330881 -0.077386,-0.665172 -0.078125,-1 7.39e-4,-0.334828 0.026855,-0.669119 0.078125,-1 H 3.9902344 C 4.4418121,7.2095295 7.3830873,4.2690786 11.111328,3.8183594 V 5.390625 c 0.330187,-0.051825 0.663821,-0.078594 0.998047,-0.080078 0.33548,6.403e-4 0.670428,0.026757 1.001953,0.078125 V 3.8183594 c 3.726644,0.4522801 6.667487,3.3924551 7.119141,7.1191406 h -1.570313 c 0.05062,0.330931 0.07608,0.665221 0.07617,1 -0.0011,0.328259 -0.02653,0.655984 -0.07617,0.980469 h 4.435547 V 10.9375 H 22.244141 C 21.771754,6.1228345 17.925908,2.2798023 13.111328,1.8066406 V 0.953125 Z m 0.998047,7.7929688 c -1.750294,0 -3.1894531,1.4411122 -3.1894531,3.1914062 0,1.750294 1.4391591,3.191406 3.1894531,3.191406 1.750294,0 3.191406,-1.441112 3.191406,-3.191406 0,-1.750294 -1.441112,-3.1914063 -3.191406,-3.1914062 z m 0,2.0000002 c 0.669418,0 1.191406,0.521988 1.191406,1.191406 0,0.669418 -0.521988,1.191406 -1.191406,1.191406 -0.669418,0 -1.189453,-0.521988 -1.189453,-1.191406 0,-0.669418 0.520035,-1.191406 1.189453,-1.191406 z M 15.255525,15 v 6 h 2 V 18.5 L 19.985994,21.169922 21.39615,19.759766 18.675447,17 h 2.580078 v -2 z" />
	</SVG>
);

const NavigationIcon = (): JSX.Element => (
	<SVG
		xmlns="http://www.w3.org/2000/svg"
		viewBox="0 0 24 24"
		width="24"
		height="24"
	>
		<Path d="m12 7.3 4.3 10.4-3.5-1.5-.8-.4-.8.4-3.5 1.5L12 7.3M12 2 4.5 20.3l.7.7 6.8-3 6.8 3 .7-.7L12 2Z" />
	</SVG>
);

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

const useCssFinderState = (
	initialMode: State[ 'mode' ],
	initialValue: string
) => {
	const [ init, setInit ] = useState( { mode: 'css', value: '' } );
	const attrName = 'css-selector/cssSelectorFinderState';
	const [ state, setState ] = usePageAttribute( attrName, {
		alternative: 0,
		mode: 'css',
		value: '',
		interactionMode: 'single-inspector',
		breadcrumbs: [],
	} );

	useEffect( () => {
		if ( init.mode !== initialMode || init.value !== initialValue ) {
			setState( {
				alternative: 0,
				mode: initialMode,
				value: initialValue,
				interactionMode:
					'css' === initialMode ? 'single-inspector' : 'navigation',
				breadcrumbs: [],
			} );
			setInit( { mode: initialMode, value: initialValue } );
		}
	}, [ init, initialMode, initialValue, setInit, setState ] );

	return [ state, setState ] as const;
};

const useValidationMessage = ( state: State ): Maybe< string > => {
	if ( state.numberOfElements === undefined || state.numberOfElements ) {
		return undefined;
	}

	if ( ! state.value?.trim() ) {
		return undefined;
	}

	switch ( state.mode ) {
		case 'id':
			return _x( 'Element ID not found', 'text', 'nelio-ab-testing' );
		case 'class':
			return _x( 'Class name not found', 'text', 'nelio-ab-testing' );
		case 'css':
			return _x(
				'No elements match the current CSS selector',
				'text',
				'nelio-ab-testing'
			);
	}
};

const usePreviewHomeUrl = ( initialUrl: Maybe< string > ): Maybe< string > => {
	const homeUrl = usePluginSetting( 'homeUrl' );
	if ( ! initialUrl ) {
		return undefined;
	}

	const experiment = getQueryArg( initialUrl, 'experiment' );
	const alternative = getQueryArg( initialUrl, 'alternative' );
	const timestamp = getQueryArg( initialUrl, 'timestamp' );
	const nabnonce = getQueryArg( initialUrl, 'nabnonce' );

	if ( ! experiment || ! alternative || ! timestamp || ! nabnonce ) {
		return undefined;
	}

	return addQueryArgs( homeUrl, {
		'nab-preview': true,
		experiment,
		alternative,
		timestamp,
		nabnonce,
	} );
};

const getLabel = ( index: number ): string =>
	sprintf(
		/* translators: %s: Letter, such as A, B, or C. */
		_x( 'Variant %s', 'text', 'nelio-ab-testing' ),
		getLetter( index )
	);
