import TextField from './TextField';
import TextareaField from './TextareaField';
import SelectField from './SelectField';
import MultiSelectField from './MultiSelectField';
import RadioField from './RadioField';
import CheckboxField from './CheckboxField';
import CheckboxGroupField from './CheckboxGroupField';
import ToggleField from './ToggleField';
import RangeField from './RangeField';
import ColorField from './ColorField';
import MediaField from './MediaField';
import GalleryField from './GalleryField';
import RadioImageField from './RadioImageField';
import RepeaterField from './RepeaterField';
import ConditionalField from './ConditionalField';
import RemoteSelectField from './RemoteSelectField';
import HoursField from './HoursField';
import SubmissionField from './SubmissionField';
import RichTextField from './RichTextField';
import cliqUI from '../../registry';
import { applyFilters } from '../../../../shared/hooks';

const registry = {
	text: TextField,
	textarea: TextareaField,
	select: SelectField,
	multiselect: MultiSelectField,
	'multi-select': MultiSelectField,
	tokens: MultiSelectField,
	radio: RadioField,
	checkbox: CheckboxField,
	checkboxes: CheckboxGroupField,
	toggle: ToggleField,
	range: RangeField,
	slider: RangeField,
	color: ColorField,
	image: MediaField,
	gallery: GalleryField,
	'radio-image': RadioImageField,
	repeater: RepeaterField,
	conditional: ConditionalField,
	url: TextField,
	email: TextField,
	tel: TextField,
	number: TextField,
	'wp-resource': RemoteSelectField,
	'wp-post': RemoteSelectField,
	'wp-taxonomy': RemoteSelectField,
	'remote-select': RemoteSelectField,
	'query-select': RemoteSelectField,
	hours: HoursField,
	'opening-hours': HoursField,
	wysiwyg: RichTextField,
	richtext: RichTextField,
	'rich-text': RichTextField,
	submission: SubmissionField,
};

export const getFieldComponent = ( field ) => {
	if ( ! field ) {
		return TextField;
	}

	// Merge order: core fields < host-registered (cliqUI.registerField) < filter.
	// Lets a consumer plugin add domain field types (maps, markers, …) without
	// editing the framework.
	const merged = applyFilters( 'cliqui.field_components', {
		...registry,
		...( cliqUI.fields ? cliqUI.fields() : {} ),
	} );

	return merged[ field.type ] ?? TextField;
};

export const isEmptyValue = ( value ) => {
	if ( Array.isArray( value ) ) {
		return value.length === 0;
	}

	if ( typeof value === 'object' && value !== null ) {
		return Object.keys( value ).length === 0;
	}

	return value === undefined || value === null || value === '';
};

const compareValue = ( fieldValue, conditionValue, operator ) => {
	switch ( operator ) {
		case 'not_equals':
			return fieldValue !== conditionValue;
		case 'contains':
			return (
				Array.isArray( fieldValue ) &&
				fieldValue.includes( conditionValue )
			);
		case 'not_contains':
			return (
				Array.isArray( fieldValue ) &&
				! fieldValue.includes( conditionValue )
			);
		case 'greater_than':
			return Number( fieldValue ) > Number( conditionValue );
		case 'greater_than_or_equal':
			return Number( fieldValue ) >= Number( conditionValue );
		case 'less_than':
			return Number( fieldValue ) < Number( conditionValue );
		case 'less_than_or_equal':
			return Number( fieldValue ) <= Number( conditionValue );
		case 'empty':
			return isEmptyValue( fieldValue );
		case 'not_empty':
			return ! isEmptyValue( fieldValue );
		case 'truthy':
			return Boolean( fieldValue );
		case 'falsy':
			return ! fieldValue;
		case 'equals':
		default:
			return fieldValue === conditionValue;
	}
};

const evaluateCondition = ( condition, context ) => {
	if ( ! condition ) {
		return true;
	}

	const scope = condition.scope ?? 'form';
	const sourceValues = scope === 'item' ? context.values : context.formValues;

	if ( ! condition.field ) {
		return true;
	}

	const fieldValue = sourceValues?.[ condition.field ];
	const operator = condition.operator ?? 'equals';
	const expectedValue = condition.value;

	return compareValue( fieldValue, expectedValue, operator );
};

export const shouldDisplayField = ( field, context ) => {
	if ( ! field ) {
		return false;
	}

	const conditions = field.conditions ?? [];
	const hasConditions = conditions.length > 0 || field.condition;

	if ( ! hasConditions ) {
		return true;
	}

	const evaluations = [];

	if ( field.condition ) {
		evaluations.push( evaluateCondition( field.condition, context ) );
	}

	if ( conditions.length > 0 ) {
		conditions.forEach( ( condition ) => {
			evaluations.push( evaluateCondition( condition, context ) );
		} );
	}

	if ( evaluations.length === 0 ) {
		return true;
	}

	const logic = ( field.conditionLogic || 'AND' ).toUpperCase();

	if ( logic === 'OR' ) {
		return evaluations.some( Boolean );
	}

	return evaluations.every( Boolean );
};

export default registry;
