/**
 * Internal dependencies
 */
import type {
	Field,
	FieldTypeName,
	NormalizedField,
	SortDirection,
} from '../types';
import type { FieldType } from '../types/private';
import { getControl } from '../components/dataform-controls';
import getFilterBy from './utils/get-filter-by';
import getValueFromId from './utils/get-value-from-id';
import hasElements from './utils/has-elements';
import setValueFromId from './utils/set-value-from-id';
import { default as email } from './email';
import { default as integer } from './integer';
import { default as number } from './number';
import { default as text } from './text';
import { default as datetime } from './datetime';
import { default as date } from './date';
import { default as boolean } from './boolean';
import { default as media } from './media';
import { default as array } from './array';
import { default as password } from './password';
import { default as telephone } from './telephone';
import { default as color } from './color';
import { default as url } from './url';
import { default as noType } from './no-type';
import getIsValid from './utils/get-is-valid';
import getFilter from './utils/get-filter';
import getFormat from './utils/get-format';

/**
 *
 * @param {FieldTypeName} type The field type definition to get.
 *
 * @return A field type definition.
 */
function getFieldTypeByName< Item >( type?: FieldTypeName ): FieldType< Item > {
	const found = [
		email,
		integer,
		number,
		text,
		datetime,
		date,
		boolean,
		media,
		array,
		password,
		telephone,
		color,
		url,
	].find( ( fieldType ) => fieldType?.type === type );

	if ( !! found ) {
		return found;
	}

	// This is a fallback for fields that don't provide a type.
	// It can be removed when/if the field.type becomes mandatory.
	return noType;
}

/**
 * Apply default values and normalize the fields config.
 *
 * @param fields Fields config.
 * @return Normalized fields config.
 */
export default function normalizeFields< Item >(
	fields: Field< Item >[]
): NormalizedField< Item >[] {
	return fields.map( ( field ) => {
		const fieldType = getFieldTypeByName< Item >( field.type );

		const getValue = field.getValue || getValueFromId( field.id );
		const sort = function ( a: any, b: any, direction: SortDirection ) {
			const aValue = getValue( { item: a } );
			const bValue = getValue( { item: b } );
			return field.sort
				? field.sort( aValue, bValue, direction )
				: fieldType.sort( aValue, bValue, direction );
		};

		return {
			id: field.id,
			label: field.label || field.id,
			header: field.header || field.label || field.id,
			description: field.description,
			placeholder: field.placeholder,
			getValue,
			setValue: field.setValue || setValueFromId( field.id ),
			elements: field.elements,
			getElements: field.getElements,
			hasElements: hasElements( field ),
			isVisible: field.isVisible,
			enableHiding: field.enableHiding ?? true,
			readOnly: field.readOnly ?? false,
			// The type provides defaults for the following props
			type: fieldType.type,
			render: field.render ?? fieldType.render,
			Edit: getControl( field, fieldType.Edit ),
			sort,
			enableSorting: field.enableSorting ?? fieldType.enableSorting,
			enableGlobalSearch:
				field.enableGlobalSearch ?? fieldType.enableGlobalSearch,
			isValid: getIsValid( field, fieldType ),
			filterBy: getFilterBy(
				field,
				fieldType.defaultOperators,
				fieldType.validOperators
			),
			filter: getFilter( fieldType ),
			format: getFormat( field, fieldType ),
			getValueFormatted:
				field.getValueFormatted ?? fieldType.getValueFormatted,
		};
	} );
}
