import { React, PropTypes, classnames } from '@gravityforms/libraries';
import { SprintR } from '@gravityforms/react-utils';
import Button from '../../../elements/Button';
import Checkbox from '../../../elements/Checkbox';
import Select from '../../../elements/Select';
import Text from '../../../elements/Text';

const { forwardRef } = React;

/**
 * @module BulkActions
 * @description A component used to display bulk actions for the data grid.
 *
 * @since 4.3.0
 *
 * @param {object}      props                   The component props.
 * @param {object}      props.moduleAttributes  Custom attributes for modules.
 * @param {object}      props.moduleState       The module state.
 * @param {Function}    props.updateModuleState Update the module state.
 * @param {object|null} ref                     The ref object for this component.
 *
 * @return {JSX.Element} The BulkActions component.
 */
const BulkActions = forwardRef( ( {
	moduleAttributes = {},
	moduleState = {},
	updateModuleState = () => {},
}, ref ) => {
	const {
		selectedBulkAction = '',
		selectedRows = [],
	} = moduleState;

	const {
		button: {
			customClasses = [],
			onClick = () => {},
			...restButtonAttributes
		} = {},
		select: {
			blankValue = -1,
			onChange = () => {},
			wrapperClasses = [],
			...restSelectAttributes
		} = {},
	} = moduleAttributes?.bulkActions || {};

	const bulkActionsButtonProps = {
		customClasses: classnames(
			[ 'gform-data-grid__controls-bulk-actions-button' ],
			customClasses,
		),
		onClick: async () => {
			if ( ! selectedBulkAction || ! selectedRows.length ) {
				return;
			}
			await onClick( moduleState );
		},
		size: 'size-height-m',
		type: 'white',
		...restButtonAttributes,
	};
	const bulkActionsSelectProps = {
		controlled: true,
		wrapperClasses: classnames(
			[ 'gform-data-grid__controls-bulk-actions-select' ],
			wrapperClasses,
		),
		onChange: ( value ) => {
			const selectedValue = value === blankValue ? '' : value;
			updateModuleState( {
				selectedBulkAction: selectedValue,
			} );
			onChange( value );
		},
		initialValue: selectedBulkAction,
		...restSelectAttributes,
	};

	return (
		<div className="gform-data-grid__controls-bulk-actions" ref={ ref }>
			<Select { ...bulkActionsSelectProps } />
			<Button { ...bulkActionsButtonProps } />
		</div>
	);
} );

BulkActions.propTypes = {
	moduleAttributes: PropTypes.object,
	moduleState: PropTypes.object,
	updateModuleState: PropTypes.func,
};

BulkActions.displayName = 'BulkActions';

/**
 * @module BulkSelect
 * @description A component used to display a bulk select checkbox for the data grid.
 *
 * @since 4.3.0
 *
 * @param {object}      props                   The component props.
 * @param {Array}       props.displayedData     The displayed data.
 * @param {boolean}     props.gridLocked        Whether the grid is locked.
 * @param {string}      props.id                The id of the component.
 * @param {string}      props.location          The location of the component.
 * @param {object}      props.moduleAttributes  Custom attributes for modules.
 * @param {object}      props.moduleState       The module state.
 * @param {Function}    props.updateModuleState Update the module state.
 * @param {object|null} ref                     The ref object for this component.
 *
 * @return {JSX.Element} The BulkSelect component.
 */
const BulkSelect = forwardRef( ( {
	displayedData = [],
	gridLocked = false,
	id = '',
	location = 'header',
	moduleAttributes = {},
	moduleState = {},
	updateModuleState = () => {},
}, ref ) => {
	const {
		selectedAllRowsOnPage = false,
		selectedRows = [],
	} = moduleState;
	const {
		bulkSelectI18n = '',
	} = moduleAttributes?.bulkActions?.i18n || {};

	return (
		<div className={ `gform-data-grid__column gform-data-grid__column--bulk-select gform-data-grid__column--bulk-select--${ location }` } ref={ ref }>
			<Checkbox
				disabled={ gridLocked }
				externalControl={ true }
				externalChecked={ selectedAllRowsOnPage }
				indeterminate={ Boolean( selectedRows.length && selectedRows.length < displayedData.length ) }
				id={ `${ id }-gform-data-grid__column--bulk-select-${ location }-checkbox` }
				labelAttributes={ {
					children: <span className="gform-visually-hidden">{ bulkSelectI18n }</span>,
				} }
				onChange={ ( checked ) => {
					if ( checked ) {
						if ( selectedRows.length ) {
							// Indeterminate state, clear all selected rows.
							updateModuleState( {
								selectedAllRowsOnPage: false,
								selectedRows: [],
							} );
						} else {
							// Select all rows on the current page.
							const newSelectedRows = displayedData.map( ( row, rowIndex ) => row.id ? row.id : rowIndex );
							updateModuleState( {
								selectedAllRowsOnPage: newSelectedRows.length === displayedData.length,
								selectedRows: newSelectedRows,
							} );
						}
					} else {
						// Clear all selected rows.
						updateModuleState( getDefaultState() );
					}
				} }
				wrapperClasses={ [ 'gform-data-grid__bulk-select-checkbox' ] }
			/>
		</div>
	);
} );

BulkSelect.propTypes = {
	displayedData: PropTypes.array,
	gridLocked: PropTypes.bool,
	id: PropTypes.string,
	location: PropTypes.oneOf( [ 'header', 'footer' ] ),
	moduleAttributes: PropTypes.object,
	moduleState: PropTypes.object,
	updateModuleState: PropTypes.func,
};

BulkSelect.displayName = 'BulkSelect';

/**
 * @module BulkSelectNotice
 * @description A component used to display a notice when all rows on the page are selected.
 *
 * @since 4.3.0
 *
 * @param {object}      props                   The component props.
 * @param {boolean}     props.gridLocked        Whether the grid is locked.
 * @param {object}      props.moduleAttributes  Custom attributes for modules.
 * @param {object}      props.moduleState       The module state.
 * @param {Function}    props.updateModuleState Update the module state.
 * @param {object|null} ref                     The ref object for this component.
 *
 * @return {JSX.Element} The BulkSelectNotice component.
 */
const BulkSelectNotice = forwardRef( ( {
	gridLocked = false,
	moduleAttributes = {},
	moduleState = {},
	updateModuleState = () => {},
}, ref ) => {
	const {
		selectedAllRowsOnAllPages = false,
		selectedAllRowsOnPage = false,
		selectedRows = [],
	} = moduleState;
	const {
		i18n: {
			selectNoticeClearSelectionI18n = '',
			selectNoticeSelectAllNumberEntriesI18n = '',
			selectNoticeSelectedAllNumberEntriesI18n = '',
			selectNoticeSelectedNumberEntriesI18n = '',
		} = {},
		notice: {
			rowCount = 0,
		} = {},
	} = moduleAttributes?.bulkActions || {};

	if ( ! selectedAllRowsOnPage || gridLocked ) {
		return null;
	}

	const textProps = {
		content: selectedAllRowsOnAllPages
			? selectNoticeSelectedAllNumberEntriesI18n.replace( '%1$s', String( rowCount ) )
			: selectNoticeSelectedNumberEntriesI18n.replace( '%1$s', String( selectedRows.length ) ),
		size: 'text-sm',
		weight: 'medium',
	};
	const buttonProps = {
		children: <span className="gform-button__text gform-typography--size-text-xs">{
			! selectedAllRowsOnAllPages
				? (
					<SprintR
						text={ selectNoticeSelectAllNumberEntriesI18n }
						tokens={ [
							{
								component: Text,
								props: {
									content: rowCount.toLocaleString(),
									customClasses: [ 'gform-data-grid__bold-text' ],
									size: 'text-xs',
									tagName: 'span',
								},
							},
						] }
					/>
				)
				: selectNoticeClearSelectionI18n
		}</span>,
		onClick: selectedAllRowsOnAllPages
			? () => {
				updateModuleState( getDefaultState() );
			}
			: () => updateModuleState( {
				selectedAllRowsOnAllPages: true,
			} ),
		size: 'size-height-s',
		type: 'white',
	};

	return (
		<div className="gform-data-grid__select-notice" ref={ ref }>
			<Text { ...textProps } />
			{ rowCount !== selectedRows.length && <Button { ...buttonProps } /> }
		</div>
	);
} );

BulkSelectNotice.propTypes = {
	gridLocked: PropTypes.bool,
	moduleAttributes: PropTypes.object,
	moduleState: PropTypes.object,
	updateModuleState: PropTypes.func,
};

BulkSelectNotice.displayName = 'BulkSelectNotice';

/**
 * @module RowSelect
 * @description A component used to display a row select checkbox for the data grid.
 *
 * @since 4.3.0
 *
 * @param {object}      props                   The component props.
 * @param {Array}       props.displayedData     The data displayed in the grid.
 * @param {boolean}     props.gridLocked        Whether the grid is locked.
 * @param {object}      props.i18n              The i18n object.
 * @param {string}      props.id                The id of the component.
 * @param {string}      props.rowId             The id of the row.
 * @param {number}      props.rowIndex          The index of the row.
 * @param {object}      props.moduleState       The module state.
 * @param {Function}    props.updateModuleState Update the module state.
 * @param {object|null} ref                     The ref object for this component.
 *
 * @return {JSX.Element} The RowSelect component.
 */
const RowSelect = forwardRef( ( {
	displayedData = [],
	gridLocked = false,
	i18n = {},
	id = '',
	rowId = '',
	rowIndex = 0,
	moduleState = {},
	updateModuleState = () => {},
}, ref ) => {
	const { selectedRows = [] } = moduleState;
	const { selectRowI18n = '' } = i18n;

	return (
		<div className="gform-data-grid__column gform-data-grid__column--row-select" ref={ ref }>
			<Checkbox
				disabled={ gridLocked }
				externalControl={ true }
				externalChecked={ selectedRows.includes( rowId ) }
				id={ `${ id }-gform-data-grid__column--row-select-${ rowIndex }` }
				labelAttributes={ {
					children: <span className="gform-visually-hidden">{ selectRowI18n }</span>,
				} }
				onChange={ ( checked ) => {
					if ( checked ) {
						const newSelectedRows = [ ...selectedRows, rowId ];
						updateModuleState( {
							selectedRows: newSelectedRows,
							selectedAllRowsOnPage: newSelectedRows.length === displayedData.length,
						} );
					} else {
						const newSelectedRows = selectedRows.filter( ( selectedRow ) => selectedRow !== rowId );
						updateModuleState( {
							selectedRows: newSelectedRows,
							selectedAllRowsOnPage: newSelectedRows.length ? newSelectedRows.length === displayedData.length : false,
						} );
					}
				} }
				value={ String( rowId ) }
				wrapperClasses={ [ 'gform-data-grid__row-select-checkbox' ] }
			/>
		</div>
	);
} );

RowSelect.propTypes = {
	displayedData: PropTypes.array,
	gridLocked: PropTypes.bool,
	i18n: PropTypes.object,
	id: PropTypes.string,
	rowId: PropTypes.oneOfType( [ PropTypes.string, PropTypes.number ] ),
	rowIndex: PropTypes.number,
	moduleState: PropTypes.object,
	updateModuleState: PropTypes.func,
};

RowSelect.displayName = 'RowSelect';

/**
 * @function getDefaultState
 * @description Returns the default state.
 *
 * @since 4.3.0
 *
 * @return {object} The default state.
 */
const getDefaultState = () => ( {
	selectedAllRowsOnAllPages: false,
	selectedAllRowsOnPage: false,
	selectedBulkAction: '',
	selectedRows: [],
} );

export default {
	name: 'BulkActions',
	BulkActions,
	BulkSelect,
	BulkSelectNotice,
	RowSelect,
	getDefaultState,
};
