import { React, PropTypes, classnames } from '@gravityforms/libraries';
import { sprintf } from '@gravityforms/utils';
import Droplist from '../../Droplist';
import { DATE_KEY, SEARCH_KEY, getModules } from '../utils';

const { forwardRef } = React;

/**
 * @function getActiveFilterLabel
 * @description Get active filter label.
 *
 * @since 5.1.0
 *
 * @param {string}       key       The key of the filter.
 * @param {Array|string} value     The value of the filter.
 * @param {string}       pillLabel The pill label.
 * @param {object}       options   The options for the filter.
 *
 * @return {string} The active filter label.
 */
const getActiveFilterLabel = ( key = '', value = '', pillLabel = '', options = {} ) => {
	if ( key === SEARCH_KEY ) {
		return sprintf( pillLabel, value );
	}
	if ( key === DATE_KEY && Array.isArray( value ) && value.length === 2 ) {
		const startLabel = value[ 0 ].toLocaleDateString( undefined, options );
		const endLabel = value[ 1 ].toLocaleDateString( undefined, options );
		return startLabel === endLabel
			? sprintf( pillLabel, startLabel )
			: sprintf( pillLabel, `${ startLabel } - ${ endLabel }` );
	}
	return '';
};

/**
 * @function getUpdatedFilters
 * @description Get the updated filters.
 *
 * @since 5.1.0
 *
 * @param {Array}        filters   The array of filters.
 * @param {string}       key       The key of the filter.
 * @param {Array|string} value     The value of the filter.
 * @param {string}       pillLabel The pill label.
 * @param {object}       options   The options for the filter.
 *
 * @return {Array} The updated filters.
 */
const getUpdatedFilters = (
	filters = [],
	key = '',
	value = '',
	pillLabel = '',
	options = {},
) => {
	const hasFilter = filters.find( ( filter ) => filter.key === key );
	const hasValue = value.length > 0;
	let updatedFilters;
	if ( hasValue && hasFilter ) {
		updatedFilters = filters.map( ( filter ) => {
			if ( filter.key === key ) {
				return {
					...filter,
					value,
					label: getActiveFilterLabel( key, value, pillLabel, options ),
				};
			}
			return filter;
		} );
	} else if ( hasValue ) {
		updatedFilters = [
			...filters,
			{
				id: key,
				key,
				label: getActiveFilterLabel( key, value, pillLabel, options ),
				value,
			},
		];
	} else {
		updatedFilters = filters.filter( ( filter ) => filter.key !== key );
	}

	return updatedFilters;
};

/**
 * @function augmentListItem
 * @description Augment a list item with an onClick handler.
 *
 * @since 5.1.0
 *
 * @param {object}   item                     The list item.
 * @param {Function} props                    The component props.
 * @param {object}   props.listItemAttributes The list item attributes.
 * @param {Array}    props.modules            The modules for the data grid.
 * @param {boolean}  props.isLoading          Whether the data grid is loading.
 * @param {Function} props.onFilterChange     The filter change handler.
 * @param {Function} props.setIsLoading       Set the loading state of the data grid.
 * @param {object}   props.moduleState        The module state.
 * @param {Function} props.updateModuleState  The update module state handler.
 *
 * @return {object} The augmented list item.
 */
const augmentListItem = (
	item = {},
	{
		listItemAttributes = {},
		modules = [],
		isLoading = false,
		onFilterChange = () => {},
		setIsLoading = () => {},
		moduleState = {},
		updateModuleState = () => {},
	},
) => {
	if ( item?.props?.element === 'link' ) {
		return item;
	}

	const {
		iconAfter = '',
		iconPrefix = '',
	} = listItemAttributes;

	const { onClick: filterOnClick = () => {} } = item?.props?.customAttributes || {};
	const { filters = [] } = moduleState;
	const hasFilter = filters.find( ( filter ) => filter.id === item.props.customAttributes?.id );

	const onClick = ( event ) => {
		if ( isLoading ) {
			return;
		}

		filterOnClick( event );
		const updatedFilters = hasFilter
			? filters.filter( ( filter ) => filter.id !== item.props.customAttributes?.id )
			: [
				...filters,
				{
					key: item.props.customAttributes?.[ 'data-key' ] || '',
					value: item.props.customAttributes?.[ 'data-value' ] || '',
					id: item.props.customAttributes?.id || '',
					label: item.props.customAttributes?.[ 'data-pill-label' ] || '',
				},
			];
		const { BulkActions, Pagination } = getModules( modules, [ 'BulkActions', 'Pagination' ] );
		const updatedState = {
			filters: updatedFilters,
			...( ( BulkActions && BulkActions?.getDefaultState() ) || {} ),
			...( ( Pagination && Pagination?.getDefaultState() ) || {} ),
		};
		const combinedState = { ...moduleState, ...updatedState };
		setIsLoading( true );
		onFilterChange( combinedState )
			.then( () => {
				setIsLoading( false );
				updateModuleState( updatedState );
			} )
			.catch( ( error ) => {
				// handle error.
				setIsLoading( false );
				console.info( error );
			} );
	};

	return {
		...item,
		props: {
			...( item?.props || {} ),
			customAttributes: {
				...( item?.props?.customAttributes || {} ),
				onClick,
			},
			iconAfter: hasFilter ? iconAfter : undefined,
			iconPrefix,
		},
	};
};

/**
 * @function augmentListItems
 * @description Augment a list of items with an onClick handler.
 *
 * @since 5.1.0
 *
 * @param {Array}  items The list of items.
 * @param {object} props The component props.
 * @param {number} depth The depth of the list.
 *
 * @return {Array} The augmented list of items.
 */
const augmentListItems = ( items = [], props = {}, depth = 0 ) => {
	const {
		isLoading = false,
		setIsLoading = () => {},
		moduleState = {},
		updateModuleState = () => {},
		listItemAttributes: {
			groupIcon = '',
			iconPrefix = '',
		} = {},
		modules = [],
		onFilterChange = () => {},
		reset: {
			hasReset = false,
			label = '',
		},
	} = props;

	const augmentedItems = items.map( ( item, index ) => {
		if ( item.listItems ) {
			// has list items, call recursive.
			const triggerAttributes = { ...( item?.triggerAttributes || {} ) };
			if ( groupIcon ) {
				triggerAttributes.iconAfter = groupIcon;
			}
			if ( iconPrefix ) {
				triggerAttributes.iconPrefix = iconPrefix;
			}
			return {
				...item,
				triggerAttributes,
				customClasses: classnames(
					'gform-data-grid__controls-simple-filters-list-item',
					'gform-data-grid__controls-simple-filters-list-item--group',
					item.customClasses || [],
				),
				listItems: augmentListItems( item.listItems, props, depth + 1 ),
				hasDivider: index === items.length - 1 && hasReset && depth === 0,
			};
		}

		return augmentListItem( item, props );
	} );

	if ( hasReset && depth === 0 ) {
		augmentedItems.push( {
			key: 'reset',
			customClasses: [ 'gform-data-grid__controls-simple-filters-list-item', 'gform-data-grid__controls-simple-filters-list-item--reset' ],
			props: {
				element: 'button',
				label,
				customAttributes: {
					id: 'reset',
					onClick: () => {
						if ( isLoading ) {
							return;
						}

						const { filters = [] } = moduleState;
						const updatedFilters = filters.filter( ( filter ) => [ DATE_KEY, SEARCH_KEY ].includes( filter.key ) );
						const { BulkActions, Pagination } = getModules( modules, [ 'BulkActions', 'Pagination' ] );
						const updatedState = {
							filters: updatedFilters,
							...( ( BulkActions && BulkActions?.getDefaultState() ) || {} ),
							...( ( Pagination && Pagination?.getDefaultState() ) || {} ),
						};
						const combinedState = { ...moduleState, ...updatedState };
						setIsLoading( true );

						onFilterChange( combinedState )
							.then( () => {
								updateModuleState( updatedState );
								setIsLoading( false );
							} )
							.catch( ( error ) => {
								// handle error.
								setIsLoading( false );
								console.info( error );
							} );
					},
				},
			},
		} );
	}

	return augmentedItems;
};

/**
 * @module SimpleFilters
 * @description The SimpleFilters module.
 *
 * @since 5.1.0
 *
 * @param {object}      props                   The component props.
 * @param {boolean}     props.isLoading         Whether the data grid is loading.
 * @param {Array}       props.modules           The list of modules.
 * @param {object}      props.moduleAttributes  The module attributes.
 * @param {object}      props.moduleState       The module state.
 * @param {Function}    props.setIsLoading      Set the loading state of the data grid.
 * @param {Function}    props.updateModuleState Update the module state.
 * @param {object|null} ref                     Ref to the component.
 *
 * @return {JSX.Element} The SimpleFilters component.
 */
const SimpleFilters = forwardRef( ( {
	isLoading = false,
	modules = [],
	moduleAttributes = {},
	moduleState = {},
	setIsLoading = () => {},
	updateModuleState = () => {},
}, ref ) => {
	const {
		droplist: {
			customClasses = [],
			listItems = [],
			listItemAttributes = {},
			reset = {},
			triggerAttributes: {
				ariaText = '',
				...restTriggerAttributes
			} = {},
			...restDroplistAttributes
		} = {},
		onFilterChange = () => {},
	} = moduleAttributes?.simpleFilters || {};
	const { filters = [] } = moduleState;
	const augmentedListItems = augmentListItems(
		listItems,
		{ listItemAttributes, modules, onFilterChange, reset, moduleState, updateModuleState, isLoading, setIsLoading },
		0,
	);
	const numFilters = filters.filter( ( filter ) => ! [ DATE_KEY, SEARCH_KEY ].includes( filter.key ) ).length;
	const updatedAriaText = sprintf( ariaText, numFilters );

	const droplistProps = {
		align: 'right',
		closeOnClick: true,
		listItems: augmentedListItems,
		openOnHover: true,
		triggerAttributes: {
			ariaText: updatedAriaText,
			...restTriggerAttributes,
		},
		customClasses: classnames(
			'gform-data-grid__controls-simple-filters-droplist',
			customClasses,
		),
		...restDroplistAttributes,
	};

	return (
		<div className="gform-data-grid__controls-simple-filters" ref={ ref }>
			<Droplist { ...droplistProps } />
			{ numFilters ? (
				<span className="gform-data-grid__controls-simple-filters-count gform-typography--size-text-xxs gform-typography--weight-medium">
					{ numFilters }
				</span>
			) : null }
		</div>
	);
} );

SimpleFilters.propTypes = {
	isLoading: PropTypes.bool,
	modules: PropTypes.array,
	moduleAttributes: PropTypes.object,
	moduleState: PropTypes.object,
	setIsLoading: PropTypes.func,
	updateModuleState: PropTypes.func,
};

SimpleFilters.displayName = 'SimpleFilters';

/**
 * @function getDefaultState
 * @description Returns the default state.
 *
 * @since 5.1.0
 *
 * @return {object} The default state.
 */
const getDefaultState = () => ( {
	filters: [],
} );

export default {
	name: 'SimpleFilters',
	SimpleFilters,
	getActiveFilterLabel,
	getUpdatedFilters,
	getDefaultState,
};
