import {
	Fragment,
	useCallback,
	useEffect,
	useMemo,
	useState,
} from '@wordpress/element';
import {
	Button,
	Modal,
	Notice,
	Flex,
	FlexItem,
	Panel,
	PanelBody,
	Card,
	CardBody,
	TabPanel,
} from '@wordpress/components';
import { __, sprintf } from '@wordpress/i18n';
import { CheckCircle2, X } from 'lucide-react';
import { getFieldComponent, isEmptyValue, shouldDisplayField } from './fields';

const slugify = ( value, fallback ) => {
	const source =
		typeof value === 'string' && value.trim() !== ''
			? value
			: fallback ?? '';

	return (
		source
			.toString()
			.toLowerCase()
			.replace( /[^a-z0-9]+/g, '-' )
			.replace( /^-+|-+$/g, '' ) ||
		( fallback ?? '' )
	);
};

const slugSources = ( field ) => {
	if ( Array.isArray( field?.slugFrom ) ) {
		return field.slugFrom.filter( Boolean );
	}

	return field?.slugFrom ? [ field.slugFrom ] : [];
};

const composedSlug = ( field, values ) =>
	slugify(
		slugSources( field )
			.map( ( source ) => values?.[ source ] )
			.filter(
				( value ) =>
					value !== undefined &&
					value !== null &&
					String( value ).trim() !== ''
			)
			.join( '-' )
	);

// Field types that should always occupy the full width of a multi-column grid.
const FULL_WIDTH_TYPES = [
	'map',
	'marker-editor',
	'icon-picker',
	'repeater',
	'gallery',
	'textarea',
	'hours',
	'opening-hours',
	'checkboxes',
	'radio-image',
	'conditional',
	'submission',
	'grid-builder',
	'form-builder',
	'layout-builder',
];

const parseColumns = ( value ) => {
	const parsed = parseInt( value, 10 );

	if ( Number.isNaN( parsed ) ) {
		return 1;
	}

	return Math.min( Math.max( parsed, 1 ), 4 );
};

const collectSectionFields = ( sections ) => {
	const collected = [];

	sections.forEach( ( section ) => {
		section?.groups?.forEach( ( group ) => {
			group?.fields?.forEach( ( field ) => {
				if ( field ) {
					collected.push( field );
				}
			} );
		} );
	} );

	return collected;
};

const validateFields = ( fieldsList, currentValues, formValues ) => {
	const result = {};

	fieldsList.forEach( ( field ) => {
		if ( ! field ) {
			return;
		}

		if (
			! shouldDisplayField( field, { values: currentValues, formValues } )
		) {
			return;
		}

		if ( field.type === 'conditional' ) {
			const nestedErrors = validateFields(
				field.fields ?? [],
				currentValues,
				formValues
			);
			Object.assign( result, nestedErrors );
			return;
		}

		if ( ! field.name ) {
			return;
		}

		const value = currentValues?.[ field.name ];

		if ( field.type === 'repeater' ) {
			const repeaterErrors = {};
			const items = Array.isArray( value ) ? value : [];

			if ( field.required && items.length === 0 ) {
				repeaterErrors._error = __(
					'This field is required.',
					'cliq-store-locator'
				);
			}

			if (
				items.length > 0 &&
				Array.isArray( field.fields ) &&
				field.fields.length > 0
			) {
				const itemErrors = items.map( ( itemValue ) =>
					validateFields( field.fields, itemValue ?? {}, formValues )
				);
				const hasItemErrors = itemErrors.some(
					( itemError ) => Object.keys( itemError ?? {} ).length > 0
				);

				if ( hasItemErrors ) {
					repeaterErrors.items = itemErrors;
				}
			}

			if ( Object.keys( repeaterErrors ).length > 0 ) {
				result[ field.name ] = repeaterErrors;
			}

			return;
		}

		if ( field.required && isEmptyValue( value ) ) {
			result[ field.name ] = __(
				'This field is required.',
				'cliq-store-locator'
			);
		}
	} );

	return result;
};

const RecordFormModal = ( {
	isOpen,
	display = 'modal',
	title,
	submitLabel,
	fields = [],
	sections = [],
	initialValues = {},
	onSubmit,
	onCancel,
	isSaving = false,
	readOnly = false,
} ) => {
	const [ values, setValues ] = useState( initialValues );
	const [ errors, setErrors ] = useState( {} );
	const [ formError, setFormError ] = useState( '' );

	useEffect( () => {
		setValues( initialValues );
		setErrors( {} );
		setFormError( '' );
	}, [ initialValues, isOpen ] );

	const normalizedSections = useMemo( () => {
		if ( Array.isArray( sections ) && sections.length > 0 ) {
			return sections;
		}

		if ( fields.length > 0 ) {
			// Providers that supply a flat `fields` array (markers, categories,
			// brands…) get a tidy 2-column layout by default: short controls pair
			// up, while wide types (icon pickers, media fields, textareas…) span
			// the full row. Single-field forms still read fine in a grid.
			return [
				{
					id: 'general',
					render: 'regular',
					groups: [
						{
							id: 'general-group',
							label: '',
							layout: 'grid',
							columns: 2,
							fields,
						},
					],
				},
			];
		}

		return [];
	}, [ sections, fields ] );

	const topLevelFields = useMemo(
		() => collectSectionFields( normalizedSections ),
		[ normalizedSections ]
	);

	const updateValue = useCallback( ( name, newValue ) => {
		setValues( ( prev ) => {
			const next = { ...prev, [ name ]: newValue };

			// Slug-follow accepts one source or an ordered source array. It keeps
			// following the composed value until the user edits the slug by hand.
			topLevelFields.forEach( ( candidate ) => {
				if ( ! candidate || ! slugSources( candidate ).includes( name ) ) {
					return;
				}
				const current = next[ candidate.name ] ?? '';
				const previousSlug = composedSlug( candidate, prev );
				if ( current === '' || current === previousSlug ) {
					next[ candidate.name ] = composedSlug( candidate, next );
				}
			} );

			return next;
		} );
	}, [ topLevelFields ] );

	const renderField = useCallback(
		( field, overrides = {} ) => {
			if ( ! field ) {
				return null;
			}

			const contextValues = overrides.values ?? values;
			const formValues = overrides.formValues ?? values;
			const contextErrors = overrides.errors ?? errors;
			const shouldWrap = overrides.wrap ?? Boolean( field.name );

			if (
				! shouldDisplayField( field, {
					values: contextValues,
					formValues,
				} )
			) {
				return null;
			}

			const fieldName = field.name;
			let fieldValue;
			if ( overrides.value !== undefined ) {
				fieldValue = overrides.value;
			} else if ( fieldName !== undefined ) {
				fieldValue = contextValues?.[ fieldName ];
			}

			let fieldErrors;
			if ( overrides.errors !== undefined ) {
				fieldErrors = overrides.errors;
			} else if ( fieldName !== undefined ) {
				fieldErrors = contextErrors?.[ fieldName ];
			}

			const handleChange =
				overrides.onChange ||
				( fieldName
					? ( nextValue ) => updateValue( fieldName, nextValue )
					: undefined );

			const FieldComponent = getFieldComponent( field );
			const fieldNode = (
				<FieldComponent
					field={ field }
					value={ fieldValue }
					onChange={ handleChange }
					setFieldValue={ updateValue }
					values={ contextValues }
					formValues={ formValues }
					errors={ fieldErrors }
					renderField={ ( childField, childOverrides = {} ) =>
						renderField( childField, {
							...childOverrides,
							values: childOverrides.values ?? contextValues,
							formValues,
							errors: childOverrides.errors ?? fieldErrors,
						} )
					}
				/>
			);

			if ( ! shouldWrap ) {
				if ( overrides.key ) {
					return (
						<Fragment key={ overrides.key }>{ fieldNode }</Fragment>
					);
				}

				return fieldNode;
			}

			const displayError =
				typeof fieldErrors === 'string'
					? fieldErrors
					: overrides.errorMessage;

			const fieldKey =
				overrides.key ??
				field.name ??
				field.id ??
				( typeof field.label === 'string'
					? `field-${ slugify( field.label ) }`
					: undefined ) ??
				`field-${ field.type ?? 'input' }`;

			return (
				<div
					className={ `cliqthemes-ui-form-field is-type-${ slugify( field.type ?? 'input' ) }${
						overrides.fullWidth ? ' is-full-width' : ''
					}` }
					key={ fieldKey }
				>
					{ fieldNode }
					{ displayError && (
						<span className="cliqthemes-ui-field-error">
							{ displayError }
						</span>
					) }
				</div>
			);
		},
		[ errors, updateValue, values ]
	);

	const validate = useCallback( () => {
		const validationErrors = validateFields(
			topLevelFields,
			values,
			values
		);
		setErrors( validationErrors );

		return Object.keys( validationErrors ).length === 0;
	}, [ topLevelFields, values ] );

	const handleSubmit = () => {
		if ( ! validate() ) {
			setFormError(
				__( 'Please complete the required fields.', 'cliq-store-locator' )
			);
			return;
		}

		setFormError( '' );
		onSubmit( values );
	};

	if ( ! isOpen ) {
		return null;
	}

	const renderGroupFields = ( group ) => {
		const groupFields = group.fields ?? [];
		const rawColumns = group.columns ?? group.cols ?? 1;
		const columns = parseColumns( rawColumns );

		// A multi-column group OR an explicit grid layout renders as a CSS grid.
		if ( group.layout === 'grid' || columns > 1 ) {
			return (
				<div
					className={ `cliqthemes-ui-form-grid cliqthemes-ui-form-grid--cols-${ columns }` }
				>
					{ groupFields.map( ( field ) =>
						renderField( field, {
							key: `${ group.id ?? 'group' }-${
								field.name ?? field.id ?? 'field'
							}`,
							// Wide fields (map, repeater, gallery, textarea)
							// span the full row for readability.
							fullWidth: field.fullWidth ?? FULL_WIDTH_TYPES.includes( field.type ),
						} )
					) }
				</div>
			);
		}

		return groupFields.map( ( field ) =>
			renderField( field, {
				key: `${ group.id ?? 'group' }-${
					field.name ?? field.id ?? 'field'
				}`,
			} )
		);
	};

	const renderSection = ( section, sectionIndex ) => {
		const sectionId = slugify( section.id ?? '', `section-${ sectionIndex }` );
		const groups = section.groups ?? [];

		return (
			<div key={ sectionId } className="cliqthemes-ui-form-section-body">
				{ groups.map( ( group, groupIndex ) => (
					<div
						key={ group.id ?? `${ sectionId }-group-${ groupIndex }` }
						className="cliqthemes-ui-form-group"
					>
						{ group.label && (
							<h4 className="cliqthemes-ui-form-group__title">
								{ group.label }
							</h4>
						) }
						{ group.description && (
							<p className="cliqthemes-ui-form-group-description">
								{ group.description }
							</p>
						) }
						{ renderGroupFields( group ) }
					</div>
				) ) }
			</div>
		);
	};

	const formContent = (
		<>
			{ formError && (
				<Notice status="error" isDismissible={ false }>
					{ formError }
				</Notice>
			) }

			{ /* Read-only (view) mode: a native disabled fieldset neutralises every
			     control inside — inputs, selects, toggles — without per-field work. */ }
			<fieldset className="cliqthemes-ui-form-fieldset" disabled={ readOnly }>
				{ normalizedSections.length > 1 ? (
					<TabPanel
						className="cliqthemes-ui-form-tabs"
						tabs={ normalizedSections.map( ( section, index ) => ( {
							name:
								slugify( section.id ?? '', `section-${ index }` ) ||
								`section-${ index }`,
							title: section.label || `Section ${ index + 1 }`,
						} ) ) }
					>
						{ ( tab ) => {
							const index = normalizedSections.findIndex(
								( section, i ) =>
									( slugify( section.id ?? '', `section-${ i }` ) ||
										`section-${ i }` ) === tab.name
							);
							const safeIndex = index < 0 ? 0 : index;
							return renderSection(
								normalizedSections[ safeIndex ],
								safeIndex
							);
						} }
					</TabPanel>
				) : (
					<div className="cliqthemes-ui-form-sections">
						{ normalizedSections.map( renderSection ) }
					</div>
				) }
			</fieldset>

			<Flex
				className="cliqthemes-ui-form-actions"
				justify="flex-end"
				gap={ 12 }
			>
				<FlexItem>
					<Button
						variant="tertiary"
						className="cliqthemes-ui-button cliqthemes-ui-button--ghost"
						onClick={ onCancel }
						disabled={ isSaving }
					>
						<X
							size={ 16 }
							strokeWidth={ 1.8 }
							className="cliqthemes-ui-button__icon"
						/>
						{ readOnly ? __( 'Close', 'cliq-store-locator' ) : __( 'Cancel', 'cliq-store-locator' ) }
					</Button>
				</FlexItem>
				{ ! readOnly && (
					<FlexItem>
						<Button
							variant="primary"
							className="cliqthemes-ui-button cliqthemes-ui-button--primary"
							onClick={ handleSubmit }
							isBusy={ isSaving }
						>
							<CheckCircle2
								size={ 16 }
								strokeWidth={ 1.8 }
								className="cliqthemes-ui-button__icon"
							/>
							{ submitLabel }
						</Button>
					</FlexItem>
				) }
			</Flex>
		</>
	);

	if ( display === 'inline' ) {
		return (
			<div className="cliqthemes-ui-form-inline">
				{ title && (
					<h2 className="cliqthemes-ui-form-inline__title">
						{ title }
					</h2>
				) }
				{ formContent }
			</div>
		);
	}

	return (
		<Modal
			className="cliqthemes-ui-form-modal"
			title={ title }
			onRequestClose={ onCancel }
			shouldCloseOnClickOutside={ ! isSaving }
		>
			{ formContent }
		</Modal>
	);
};

export default RecordFormModal;
