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

const { forwardRef, useEffect, useState } = React;

/**
 * @module Search
 * @description The Search module for the kanban.
 *
 * @since 5.8.4
 *
 * @param {object}      props                   Component props.
 * @param {boolean}     props.isLoading         Whether the kanban is loading.
 * @param {object}      props.moduleAttributes  The module attributes.
 * @param {object}      props.moduleState       The module state.
 * @param {Function}    props.setIsLoading      Set the loading state of the kanban.
 * @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( ( {
	isLoading = false,
	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 || {};

	const [ searchValue, setSearchValue ] = useState( searchTerm );

	// If search term is updated externally (e.g. Active Filters), update the local state.
	useEffect( () => {
		if ( searchTerm === searchValue ) {
			return;
		}
		setSearchValue( searchTerm );
	}, [ searchTerm ] ); // eslint-disable-line react-hooks/exhaustive-deps

	/**
	 * @function handleSearch
	 * @description Handles the search action.
	 *
	 * @since 5.8.4
	 *
	 * @param {string} value The search value.
	 */
	const handleSearch = ( value = '' ) => {
		if ( ! useAjax ) {
			return;
		}

		if ( isLoading ) {
			return;
		}

		const updatedState = {
			filters: getUpdatedFilters( filters, SEARCH_KEY, value, pillLabelI18n ),
			searchTerm: value,
			searchActive: value !== '',
		};
		const combinedState = { ...moduleState, ...updatedState };
		setIsLoading( true );
		onSearch( combinedState )
			.then( ( success ) => {
				if ( ! success ) {
					return;
				}
				if ( ! value ) {
					setSearchValue( '' );
				}
				updateModuleState( updatedState );
			} )
			.catch( ( error ) => {
				console.info( error );
			} )
			.finally( () => {
				setIsLoading( false );
			} );
	};

	const componentProps = {
		buttonAttributes: {
			disabled: searchValue === '' || isLoading,
			onClick: () => handleSearch( searchValue ),
			...restButtonAttributes,
		},
		buttonClasses: classnames(
			'gform-kanban__controls-search-button',
			customClasses,
		),
		customClasses: [ 'gform-kanban__controls-search' ],
		inputAttributes: {
			directControlled: true,
			onClear: () => {
				onClear();
				handleSearch();
			},
			onKeyDown: ( event ) => {
				if ( event.key === 'Enter' ) {
					handleSearch( searchValue );
				}
				onKeyDown( event );
			},
			onChange: ( value ) => {
				setSearchValue( value );
				onChange( value );
				if ( value === '' ) {
					handleSearch();
				}
			},
			value: searchValue,
			wrapperClasses: classnames(
				'gform-kanban__controls-search-input',
				wrapperClasses,
			),
			...restInputAttributes,
			disabled: isLoading,
			clearableButtonAttributes: {
				...( restInputAttributes?.clearableButtonAttributes || {} ),
				disabled: isLoading,
			},
		},
	};

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

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

Search.displayName = 'Search';

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

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