import { React, PropTypes, classnames } from '@gravityforms/libraries';
import Text from '../../../elements/Text';
import CommonSearch from '../../../utils/CommonSearch';
import { SEARCH_KEY, getModules } from '../utils';
import { getUpdatedFilters } from './utils/filters';

const { forwardRef } = React;

/**
 * @module Search
 * @description The search component for the data grid.
 *
 * @since 4.3.0
 *
 * @param {object}      props                   Component props.
 * @param {boolean}     props.gridLocked        Whether the grid is locked or not.
 * @param {boolean}     props.isLoading         Whether the data grid is loading.
 * @param {Array}       props.modules           The modules for the data grid.
 * @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 {boolean}     props.useAjax           Whether to use ajax or not.
 * @param {object|null} ref                     The ref object for this component.
 *
 * @return {JSX.Element} The Search component.
 */
const Search = forwardRef( ( {
	gridLocked = false,
	isLoading = false,
	modules = [],
	moduleAttributes = {},
	moduleState = {},
	setIsLoading = () => {},
	updateModuleState = () => {},
	useAjax = false,
}, ref ) => {
	const {
		filters = [],
		searchTerm = '',
	} = moduleState;
	const {
		input: {
			onChange = () => {},
			onClear = () => {},
			onKeyDown = () => {},
			wrapperClasses = [],
			...restInputAttributes
		} = {},
		button: {
			customClasses = [],
			...restButtonAttributes
		} = {},
		onSearch = () => {},
		i18n: {
			pillLabel: pillLabelI18n = 'Search: %s',
		} = {},
	} = moduleAttributes?.search || {};

	/**
	 * @function handleSearch
	 * @description Handle the search action.
	 *
	 * @since 4.3.0
	 *
	 * @param {boolean} clear Whether to clear the search term or not.
	 */
	const handleSearch = ( clear = false ) => {
		if ( useAjax ) {
			// handle ajax search.
			if ( isLoading ) {
				return;
			}

			const newSearchTerm = clear ? '' : searchTerm;
			const { BulkActions, GridPagination } = getModules( modules, [ 'BulkActions', 'GridPagination' ] );
			const updatedState = {
				searchTerm: newSearchTerm,
				searchActive: newSearchTerm !== '',
				filters: getUpdatedFilters( filters, SEARCH_KEY, newSearchTerm, pillLabelI18n ),
				...( ( BulkActions && BulkActions?.getDefaultState() ) || {} ),
				...( ( GridPagination && GridPagination?.getDefaultState() ) || {} ),
			};
			const combinedState = { ...moduleState, ...updatedState };
			setIsLoading( true );
			onSearch( combinedState )
				.then( () => {
					updateModuleState( updatedState );
					setIsLoading( false );
				} )
				.catch( ( error ) => {
					// handle error.
					setIsLoading( false );
					console.info( error );
				} );
		} else {
			// handle local search.
		}
	};

	const componentProps = {
		buttonAttributes: {
			disabled: gridLocked && searchTerm === '',
			onClick: () => handleSearch(),
			...restButtonAttributes,
		},
		buttonClasses: classnames(
			[ 'gform-data-grid__controls-search-button' ],
			customClasses,
		),
		customClasses: [ 'gform-data-grid__controls-search' ],
		inputAttributes: {
			disabled: gridLocked && searchTerm === '',
			onClear: () => {
				onClear();
				handleSearch( true );
			},
			onKeyDown: ( event ) => {
				if ( event.key === 'Enter' ) {
					handleSearch();
				}
				onKeyDown( event );
			},
			onChange: ( value ) => {
				updateModuleState( {
					searchTerm: value,
				} );
				onChange( value );
				if ( value === '' ) {
					handleSearch( true );
				}
			},
			value: searchTerm,
			wrapperClasses: classnames(
				[ 'gform-data-grid__controls-search-input' ],
				wrapperClasses,
			),
			...restInputAttributes,
		},
	};

	return <CommonSearch { ...componentProps } ref={ ref } />;
} );

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

Search.displayName = 'Search';

/**
 * @module NoResults
 * @description The NoResults component for the data grid.
 *
 * @since 4.3.0
 *
 * @param {object}      props                  The component props.
 * @param {object}      props.moduleAttributes The module attributes.
 * @param {object|null} ref                    The ref object for this component.
 *
 * @return {JSX.Element} The NoResults component.
 */
const NoResults = forwardRef( ( {
	moduleAttributes = {},
}, ref ) => {
	const {
		noResults: {
			customClasses = [],
			Image = null,
			...restAttributes
		},
		i18n: {
			noResultsTitle: searchNoResultsTitleI18n = '',
			noResultsMessage: searchNoResultsMessageI18n = '',
		},
	} = moduleAttributes?.search || {};
	const noResultsProps = {
		className: classnames( {
			'gform-data-grid__no-data-message': true,
			'gform-data-grid__no-search-results-message': true,
		}, customClasses ),
		...restAttributes,
	};

	return (
		<div { ...noResultsProps } ref={ ref }>
			{ Image && <Image /> }
			{ searchNoResultsTitleI18n && <Text size="text-md" weight="medium" spacing={ [ 5, 0, 0, 0 ] }>{ searchNoResultsTitleI18n }</Text> }
			{ searchNoResultsMessageI18n && <Text size="text-sm" spacing={ [ 3, 0, 0, 0 ] }>{ searchNoResultsMessageI18n }</Text> }
		</div>
	);
} );

NoResults.propTypes = {
	moduleAttributes: PropTypes.object,
};

NoResults.displayName = 'NoResults';

/**
 * @function getDefaultState
 * @description Returns the default state.
 *
 * @since 5.1.0
 *
 * @return {object} The default state.
 */
const getDefaultState = () => ( {
	searchTerm: '',
	searchActive: false,
} );

export default {
	name: 'Search',
	Search,
	NoResults,
	getDefaultState,
};
