/**
 * WordPress dependencies
 */
import { useDispatch, useSelect } from '@wordpress/data';
import { useState } from '@wordpress/element';

/**
 * External dependencies
 */
import isEqual from 'lodash/isEqual';
import sortedUniq from 'lodash/sortedUniq';
import without from 'lodash/without';
import type { ExperimentId } from '@nab/types';

/**
 * Internal dependencies
 */
import { getSegmentName, getTestLink, getTestName } from '../utils';
import { store as NAB_CHECKER } from '../store';
import { EMPTY_ARRAY } from '../constants';

import { areHeatmapConditionsValid } from '../../public/utils/segmentation';
import { setSegmentationSettings } from '../../public/utils/segmentation/segmentation-settings';
import { removeCookie } from '../../public/utils/cookies';
import type { ExperimentSummary, HeatmapSummary } from '../../public/types';

export const Experiments = (): JSX.Element => {
	const [ locked, lock ] = useState( false );
	const { areSegmentsDirty, newSegmentation, settings } = useSelect(
		( select ) => ( {
			areSegmentsDirty: ! isEqual(
				select( NAB_CHECKER ).getSegmentation().activeSegments,
				select( NAB_CHECKER ).getNewSegmentation().activeSegments
			),
			newSegmentation: select( NAB_CHECKER ).getNewSegmentation(),
			settings: select( NAB_CHECKER ).getSettings(),
		} ),
		[]
	);
	const { experiments, heatmaps } = settings;

	if ( ! experiments.length && ! heatmaps.length ) {
		return <div>No running experiments or heatmaps.</div>;
	}

	const showSegments = experiments.some( ( e ) => !! e.segments.length );

	const reloadSegments = () => {
		lock( true );
		setSegmentationSettings( newSegmentation );
		removeCookie( 'nabExperimentsWithPageViews' );
		removeCookie( 'nabUniqueViews' );
		window.location.reload();
	};

	return (
		<div>
			{ !! experiments.length && (
				<div style={ { marginTop: '1em' } }>
					<div
						style={ {
							display: 'flex',
							marginBottom: '0.5em',
							gap: '1em',
						} }
					>
						<strong>Tests</strong>
						{ areSegmentsDirty && (
							<button
								disabled={ locked }
								onClick={ reloadSegments }
							>
								Reload
							</button>
						) }
					</div>
					<div style={ { marginLeft: '1em', fontSize: '0.95em' } }>
						{ experiments.map( ( e ) => (
							<ExperimentView
								key={ e.id }
								attributes={ e }
								showSegments={ showSegments }
							/>
						) ) }
					</div>
				</div>
			) }
			{ !! heatmaps.length && (
				<div style={ { marginTop: '1em' } }>
					<div
						style={ {
							display: 'flex',
							marginBottom: '0.5em',
							gap: '1em',
						} }
					>
						<strong>Heatmaps</strong>
					</div>
					<div style={ { marginLeft: '1em', fontSize: '0.95em' } }>
						{ heatmaps.map( ( h ) => (
							<HeatmapView key={ h.id } attributes={ h } />
						) ) }
					</div>
				</div>
			) }
		</div>
	);
};

// =====
// VIEWS
// =====

function ExperimentView( {
	attributes,
	showSegments,
}: {
	readonly attributes: ExperimentSummary;
	readonly showSegments?: boolean;
} ) {
	const mode = useMode( attributes );
	const homeUrl = useSelect(
		( select ) => select( NAB_CHECKER ).getSettings().homeUrl,
		[]
	);
	return (
		<details style={ { marginBottom: '1em' } }>
			<Title
				experimentId={ attributes.id }
				mode={ mode }
				name={ getTestName( attributes ) }
				link={ getTestLink( homeUrl, attributes ) }
				segments={ attributes.segments }
				showSegments={ showSegments }
			/>
			<RawDetails data={ attributes } />
		</details>
	);
}

function HeatmapView( {
	attributes,
}: {
	readonly attributes: HeatmapSummary;
} ) {
	const mode = areHeatmapConditionsValid( attributes )
		? ( 'active' as const )
		: ( 'user-does-not-participate' as const );
	return (
		<details style={ { marginBottom: '1em' } }>
			<Title
				experimentId={ attributes.id }
				mode={ mode }
				name={ getTestName( attributes ) }
				segments={
					attributes.participation.length
						? [
								{
									id: 0,
									segmentationRules: attributes.participation,
								},
						  ]
						: EMPTY_ARRAY
				}
			/>
			<RawDetails data={ attributes } />
		</details>
	);
}

type TestStatus =
	| 'inactive'
	| 'user-participation-not-yet-established'
	| 'user-does-not-participate'
	| 'goal-tracking-only'
	| 'active';

function Title( {
	experimentId,
	mode,
	name,
	link,
	segments,
	showSegments,
}: {
	readonly experimentId: ExperimentId;
	readonly segments: ExperimentSummary[ 'segments' ];
	readonly mode: TestStatus;
	readonly name: string;
	readonly link?: string;
	readonly showSegments?: boolean;
} ) {
	const { color, label } = useColorAndLabel( experimentId, mode );
	return (
		<summary>
			<div
				style={ {
					display: 'inline-flex',
					gap: '0.5em',
					cursor: 'pointer',
					maxWidth: 'calc(100% - 3em)',
					width: '100%',
				} }
				title={ label }
			>
				<span
					style={ {
						display: 'inline-block',
						width: '1em',
						height: '1em',
						fontSize: '10px',
						borderRadius: '100%',
						backgroundColor: color,
						flexGrow: 0,
						flexShrink: 0,
					} }
				/>
				<span
					style={ {
						fontWeight: '500',
						marginTop: '-0.3em',
						flexGrow: 1,
					} }
				>
					{ name }
				</span>
				{ !! link && 'active' !== mode && (
					<span
						style={ {
							marginTop: '-0.3em',
							flexGrow: 0,
							flexShrink: 0,
						} }
					>
						<a
							style={ {
								color: '#fff',
								textDecoration: 'none',
								fontSize: '0.8em',
							} }
							href={ link }
						>
							View
						</a>
					</span>
				) }
			</div>
			{ showSegments && (
				<Segments experimentId={ experimentId } segments={ segments } />
			) }
		</summary>
	);
}

function Segments( {
	experimentId,
	segments,
}: {
	readonly experimentId: ExperimentId;
	readonly segments: ExperimentSummary[ 'segments' ];
} ) {
	const newSegments = useSelect(
		( select ) =>
			select( NAB_CHECKER ).getNewActiveSegments( experimentId ),
		[ experimentId ]
	);

	const { setNewActiveSegments: doSetNewActiveSegments } =
		useDispatch( NAB_CHECKER );

	if ( ! segments.length ) {
		return (
			<div
				style={ {
					alignItems: 'center',
					display: 'flex',
					flexDirection: 'row',
					fontSize: '0.8em',
					gap: '0.2em',
					marginLeft: '2rem',
				} }
			>
				<span style={ { marginRight: '0.2em' } }>Segments:</span>
				<input
					type="checkbox"
					checked
					disabled
					title="Default Segment"
				/>
			</div>
		);
	}

	const setNewActiveCheckers = ( index: number, enabled: boolean ) => {
		let newValue: ReadonlyArray< number > = sortedUniq(
			[ ...( newSegments ?? EMPTY_ARRAY ), 0, index ].sort()
		);
		if ( ! enabled ) {
			newValue = without( newValue, index );
			if ( newValue[ 0 ] === 0 && newValue.length === 1 ) {
				newValue = EMPTY_ARRAY;
			}
		}
		void doSetNewActiveSegments( experimentId, newValue );
	};

	return (
		<div
			style={ {
				alignItems: 'center',
				display: 'flex',
				flexDirection: 'row',
				fontSize: '0.8em',
				gap: '0.2em',
				marginLeft: '2rem',
			} }
		>
			<span style={ { marginRight: '0.2em' } }>Segments:</span>
			{ segments.map( ( s ) => (
				<input
					key={ s.id }
					type="checkbox"
					checked={ !! newSegments?.includes( s.id + 1 ) }
					onChange={ ( ev ) =>
						setNewActiveCheckers( s.id + 1, ev.target.checked )
					}
					title={ getSegmentName( s, s.id ) }
				/>
			) ) }
		</div>
	);
}

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

function useMode( experiment: ExperimentSummary ): TestStatus {
	const { actualSegments, hasViews } = useSelect(
		( select ) => ( {
			actualSegments: select( NAB_CHECKER ).getActiveSegments(
				experiment.id
			),
			hasViews: select( NAB_CHECKER ).hasViews( experiment.id ),
		} ),
		[ experiment.id ]
	);

	if ( experiment.segments.length ) {
		if ( undefined === actualSegments ) {
			return 'user-participation-not-yet-established';
		}
		if ( ! actualSegments.length ) {
			return 'user-does-not-participate';
		}
	}

	if ( experiment.active ) {
		return 'active';
	}

	if (
		hasViews &&
		experiment.goals.some( ( g ) =>
			g.conversionActions.some( ( a ) => a.active )
		)
	) {
		return 'goal-tracking-only';
	}

	return 'inactive';
}

function useColorAndLabel( experimentId: ExperimentId, mode: TestStatus ) {
	const { isAwaitingGdpr, hasViews } = useSelect(
		( select ) => ( {
			hasViews: select( NAB_CHECKER ).hasViews( experimentId ),
			isAwaitingGdpr: select( NAB_CHECKER ).isAwaitingGdpr(),
		} ),
		[ experimentId ]
	);

	if ( isAwaitingGdpr ) {
		return {
			color: '#7e8295',
			label: 'Awaiting GDPR consent…',
		};
	}

	switch ( mode ) {
		case 'inactive':
			return {
				color: '#ff2e2e',
				label: `Inactive test ${
					hasViews ? 'with' : 'without'
				} page views`,
			};

		case 'user-participation-not-yet-established':
			return {
				color: '#7e8295',
				label: 'User participation has not yet been established',
			};

		case 'user-does-not-participate':
			return {
				color: '#7e8295',
				label: 'User does not participate in this test',
			};

		case 'goal-tracking-only':
			return {
				color: '#ffae30',
				label: 'Inactive test with goal tracking',
			};

		case 'active':
			return {
				color: '#62b862',
				label: 'Active test',
			};
	}
}

function RawDetails( { data }: { data: unknown } ) {
	return (
		<pre
			style={ {
				marginLeft: '5px',
				maxWidth: 'calc(100% - 3em)',
				overflowX: 'auto',
				marginTop: '0.5em',
				marginBottom: '1em',
				background: '#fff2',
				borderLeft: '2px solid #fff',
				borderRadius: '2px',
				padding: '1em',
			} }
		>
			{ JSON.stringify( data, null, 2 ) }
		</pre>
	);
}
