import { React, PropTypes } from '@gravityforms/libraries';
import Alert from '../modules/Alert';
import Box from '../elements/Box';
import Checkbox from '../elements/Checkbox';
import Heading from '../elements/Heading';
import Icon from '../elements/Icon';
import Input from '../elements/Input';
import InputGroup from '../modules/InputGroup';
import Label from '../elements/Label';
import BasicLink from '../elements/Link';
import Radio from '../elements/Radio';
import Select from '../elements/Select';
import Text from '../elements/Text';
import Textarea from '../elements/Textarea';
import Toggle from '../elements/Toggle';

const { Fragment, memo, useCallback } = React;

/**
 * @module FormField
 * @description Renders a form field based on passed field data from the server.
 *
 * @since 3.1.0
 *
 * @param {object}   props              Component props.
 * @param {string}   props.id           Field ID.
 * @param {object}   props.field        Field data.
 * @param {Function} props.setFieldData Passed handler for field changes if the field type supports it.
 *
 * @return {JSX.Element} The FormField component.
 */
const FormField = memo( ( { id, field, setFieldData } ) => {
	const handleChange = useCallback( ( value, e ) => {
		if ( e && e.target.type === 'radio' ) {
			setFieldData( e.target.name, e.target.value );
		} else if ( field.props && field.props.name ) {
			setFieldData( field.props.name, value );
		}
	}, [ field.props, setFieldData ] );

	const renderNestedFields = ( fields ) => {
		if ( ! fields ) {
			return null;
		}

		return fields.map( ( nestedField, index ) => {
			const fieldId = `${ id }-${ index }`;
			return <FormField key={ fieldId } id={ fieldId } field={ nestedField } setFieldData={ setFieldData } />;
		} );
	};

	let component = null;
	if ( field.external ) {
		const External = field.component;
		const fieldProps = {
			...field.props,
			onChange: field?.handle_change ? handleChange : undefined,
		};
		component = <External { ...fieldProps } />;
	} else {
		const fieldProps = {
			id,
			...field.props,
		};
		switch ( field.component ) {
			case 'Alert':
				component = <Alert { ...fieldProps }>{ renderNestedFields( field.fields ) }</Alert>;
				break;
			case 'Box':
				component = <Box { ...fieldProps }>{ renderNestedFields( field.fields ) }</Box>;
				break;
			case 'Checkbox':
				component = <Checkbox { ...fieldProps } onChange={ handleChange } />;
				break;
			case 'Heading':
				component = <Heading { ...fieldProps } />;
				break;
			case 'Icon':
				component = <Icon { ...fieldProps } />;
				break;
			case 'Input':
				component = <Input { ...fieldProps } onChange={ handleChange } />;
				break;
			case 'InputGroup':
				component = <InputGroup { ...fieldProps } onChange={ handleChange } />;
				break;
			case 'Label':
				component = <Label { ...fieldProps } />;
				break;
			case 'Link':
				component = <BasicLink { ...fieldProps } />;
				break;
			case 'Radio':
				component = <Radio { ...fieldProps } onChange={ handleChange } />;
				break;
			case 'Select':
				component = <Select { ...fieldProps } onChange={ handleChange } />;
				break;
			case 'Text':
				component = <Text { ...fieldProps } />;
				break;
			case 'Textarea':
				component = <Textarea { ...fieldProps } onChange={ handleChange } />;
				break;
			case 'Toggle':
				component = <Toggle { ...fieldProps } onChange={ handleChange } />;
				break;
			default:
				component = null;
		}
	}

	return (
		<Fragment>
			{ component }
		</Fragment>
	);
} );

FormField.propTypes = {
	id: PropTypes.string,
	field: PropTypes.object,
	setFieldData: PropTypes.func,
};

export default FormField;
