/**
 * WordPress dependencies
 */
import { Dashicon, SelectControl } from '@safe-wordpress/components';
import { useDispatch, useSelect } from '@safe-wordpress/data';
import { createInterpolateElement } from '@safe-wordpress/element';
import { _x, sprintf } from '@safe-wordpress/i18n';

/**
 * External dependencies
 */
import { findIndex } from 'lodash';
import { v4 as uuid } from 'uuid';
import { usePluginSetting } from '@nab/data';
import { createSegment } from '@nab/utils';
import type {
	Dict,
	Segment as RealSegment,
	SegmentationRule,
	SegmentId,
} from '@nab/types';
type Segment = Omit< RealSegment, 'segmentationRules' >;

/**
 * Internal dependencies
 */
import './style.scss';
import { useExperimentAttribute } from '../hooks';
import { Segment as SegmentView } from '../segment';
import { SegmentList } from '../segment-list';
import { ReusableSegmentDialog } from '../reusable-segment-dialog';
import { store as NAB_EDITOR } from '../../store';

export const SegmentationSection = (): JSX.Element => {
	const [ segment, updateActiveSegment ] = useActiveSegment();
	const [ status ] = useExperimentAttribute( 'status' );
	const [ segmentEvaluation, setSegmentEvaluation ] = useSegmentEvaluation();
	const globalSegmentEvaluation = usePluginSetting( 'segmentEvaluation' );
	const isExperimentPaused = ( status || '' ).includes( 'paused' );

	const removeActiveSegment = useActiveSegmentRemover();
	const canSegmentBeRemoved = ! isExperimentPaused;
	const canSegmentBeDuplicated = ! isExperimentPaused;

	const rules = useSegmentRules( segment?.id );
	const { addSegments, addSegmentationRulesIntoSegment } =
		useDispatch( NAB_EDITOR );
	const duplicateSegment = () => {
		if ( ! segment ) {
			return;
		}
		const newSegment = {
			...createSegment(),
			attributes: {
				...segment.attributes,
				name: segment.attributes.name
					? sprintf(
							/* translators: %s: Name of an item being duplicated. */
							_x( 'Copy of %s', 'text', 'nelio-ab-testing' ),
							segment.attributes.name
					  )
					: '',
			},
		};
		const newRules: ReadonlyArray< SegmentationRule > = rules.map(
			( r ) => ( {
				...r,
				id: uuid(),
			} )
		);
		void addSegments( newSegment );
		void addSegmentationRulesIntoSegment( newSegment.id, newRules );
	};

	return (
		<div className="nab-edit-experiment__segmentation-section">
			<h2>
				<span>
					{ createInterpolateElement(
						sprintf(
							/* translators: %s: Dashicon. */
							_x( '%s Segmentation', 'text', 'nelio-ab-testing' ),
							'<icon></icon>'
						),
						{
							icon: (
								<Dashicon
									className="nab-segmentation-section__title-icon"
									icon="groups"
								/>
							),
						}
					) }
				</span>

				{ 'custom' === globalSegmentEvaluation && (
					<div className="nab-edit-experiment-segmentation-section__strategy">
						<SelectControl
							value={ segmentEvaluation }
							options={ [
								{
									value: 'site' as const,
									label: _x(
										'Evaluate on Site Landing',
										'command',
										'nelio-ab-testing'
									),
								},
								{
									value: 'tested-page' as const,
									label: _x(
										'Evaluate on Tested Pages',
										'text',
										'nelio-ab-testing'
									),
								},
							] }
							onChange={ setSegmentEvaluation }
						/>
					</div>
				) }
			</h2>

			<div className="nab-edit-experiment-segmentation-section__content">
				<SegmentList />
				{ segment ? (
					<SegmentView
						name={ segment.attributes.name }
						setName={ ( name ) =>
							updateActiveSegment( {
								...segment.attributes,
								name,
							} )
						}
						duplicateSegment={
							canSegmentBeDuplicated
								? duplicateSegment
								: undefined
						}
						removeSegment={
							canSegmentBeRemoved
								? removeActiveSegment
								: undefined
						}
					/>
				) : (
					<p className="nab-edit-experiment-segmentation-section__content--empty">
						{ _x(
							'Add a new segment to narrow your tested audience and target only a subset of your visitors.',
							'user',
							'nelio-ab-testing'
						) }
					</p>
				) }
			</div>

			<ReusableSegmentDialog />
		</div>
	);
};

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

const useActiveSegment = () => {
	const segment = useSelect(
		( select ) => select( NAB_EDITOR ).getActiveSegment(),
		[]
	);

	const { updateSegment: doUpdate } = useDispatch( NAB_EDITOR );
	const updateSegment = ( attributes: Dict ) => {
		if ( segment?.id ) {
			void doUpdate( segment.id, attributes );
		}
	};

	return [ segment, updateSegment ] as const;
};

const useActiveSegmentRemover = () => {
	const [ segment ] = useActiveSegment();
	const segments = useSelect(
		( select ) => select( NAB_EDITOR ).getSegments(),
		[]
	);

	const { removeSegments, setActiveSegment } = useDispatch( NAB_EDITOR );

	if ( ! segment ) {
		return () => null;
	}

	return () => {
		const nextSegment = getAdjacentSegment( segments, segment.id );
		if ( nextSegment ) {
			void setActiveSegment( nextSegment.id );
		}
		void removeSegments( segment.id );
	};
};

const useSegmentEvaluation = () => {
	const segmentEvaluation = useSelect(
		( select ) => select( NAB_EDITOR ).getSegmentEvaluation(),
		[]
	);
	const { setSegmentEvaluation } = useDispatch( NAB_EDITOR );
	return [ segmentEvaluation, setSegmentEvaluation ] as const;
};

const useSegmentRules = ( id: SegmentId | undefined ) =>
	useSelect(
		( select ) =>
			id ? select( NAB_EDITOR ).getSegmentationRules( id ) : [],
		[ id ]
	);

// =======
// HELPERS
// =======

function getAdjacentSegment(
	segments: ReadonlyArray< Segment >,
	segmentId: SegmentId
): Segment | false {
	const index = findIndex( segments, { id: segmentId } );
	return segments[ index + 1 ] || segments[ index - 1 ] || false;
}
