import { React, classnames } from '@gravityforms/libraries';
import { spacerClasses } from '@gravityforms/utils';
import Box from '../../elements/Box';
import Input from '../../elements/Input';
import Dropdown from '../Dropdown';

const { useState, useEffect, forwardRef } = React;

const NEEDS_I18N_LABEL = 'Needs i18n';

// Helper function to sort regions based on preferredRegions
const sortRegions = ( regions, preferred ) => {
	if ( ! preferred || ! Array.isArray( preferred ) || preferred.length === 0 ) {
		return [ ...regions ];
	}

	const preferredItems = [];
	const otherItems = [];

	regions.forEach( ( region ) => {
		const index = preferred.indexOf( region.value );
		if ( index !== -1 ) {
			preferredItems[ index ] = region;
		} else {
			otherItems.push( region );
		}
	} );

	// Filter out any undefined slots and combine with other items
	return [
		...preferredItems.filter( ( item ) => item !== undefined ),
		...otherItems,
	];
};

const transformRegionCodes = ( inputObj ) => {
	const result = {};
	for ( const key in inputObj ) {
		if ( Object.prototype.hasOwnProperty.call( inputObj, key ) ) {
			result[ key ] = Object.entries( inputObj[ key ] ).map( ( [ value, label ] ) => ( {
				value,
				label,
			} ) );
		}
	}

	return result;
};

/**
 * @module Address
 * @description Renders an address component.
 *
 * @since 5.5.0
 *
 * @param {React.ReactNode|React.ReactNode[]}          [children]                  - React element children
 * @param {Record<string, any>}                        [customAttributes]          - Custom attributes for the component
 * @param {string|string[]|Record<string, boolean>}    [customClasses]             - Custom classes for the component
 * @param {Record<string, any>}                        [countryDropdownAttributes] - Attributes for the country dropdown
 * @param {string|string[]|Record<string, boolean>}    [countryDropdownClasses]    - Classes for the country dropdown
 * @param {Record<string, any>}                        [defaultData]               - Default data for the component state
 * @param {string[]}                                   [disabledFields]            - Fields to disable in the component. Use the state keys: lineOne, lineTwo, city, state, postalCode, country
 * @param {Record<string, any>}                        [initialData]               - Initial data for the component state
 * @param {Record<string, any>}                        [i18n]                      - Internationalization settings
 * @param {function}                                   [onChange]                  - Callback for when the component changes, returns the state
 * @param {boolean}                                    [parseRegionCodes]          - Whether to parse region codes for the component
 * @param {Record<string, any>}                        [preferredRegions]          - Preferred regions for the component: countries, usStates, caProvinces
 * @param {Record<string, any>}                        [regionCodes]               - Region codes for the component
 * @param {string|number|string[]|Record<string, any>} [spacing='']                - The spacing for the component
 * @param {Record<string, any>}                        [stateDropdownAttributes]   - Attributes for the state/province dropdown
 * @param {string|string[]|Record<string, boolean>}    [stateDropdownClasses]      - Classes for the state/province dropdown
 * @param {boolean}                                    [useSelectPlaceholders]     - Whether to use select placeholders for the dropdowns
 * @param {React.RefObject<HTMLElement>|null}          ref                         - Ref to the component
 *
 * @return {JSX.Element} The Address component.
 *
 * @example
 * import Address from '@gravityforms/components/react/admin/modules/Address';
 *
 * // Basic usage with all fields
 * <Address
 *   regionCodes={{
 *     countries: [{ value: 'US', label: 'United States' }, { value: 'CA', label: 'Canada' }],
 *     usStates: [{ value: 'CA', label: 'California' }, { value: 'NY', label: 'New York' }],
 *     caProvinces: [{ value: 'ON', label: 'Ontario' }, { value: 'QC', label: 'Quebec' }]
 *   }}
 *   onChange={(data) => console.log(data)}
 * />
 *
 * // With preferred regions and initial data
 * <Address
 *   regionCodes={{
 *     countries: [{ value: 'US', label: 'United States' }, { value: 'CA', label: 'Canada' }],
 *     usStates: [{ value: 'CA', label: 'California' }, { value: 'NY', label: 'New York' }],
 *     caProvinces: [{ value: 'ON', label: 'Ontario' }, { value: 'QC', label: 'Quebec' }]
 *   }}
 *   preferredRegions={{
 *     countries: ['CA', 'US'],
 *     usStates: ['NY'],
 *     caProvinces: ['QC']
 *   }}
 *   initialData={{
 *     country: 'US',
 *     state: 'CA',
 *     city: 'San Francisco'
 *   }}
 * />
 *
 * // With disabled fields
 * <Address
 *   regionCodes={{
 *     countries: [{ value: 'US', label: 'United States' }, { value: 'CA', label: 'Canada' }]
 *   }}
 *   disabledFields={['lineTwo', 'state']}
 *   initialData={{ country: 'US' }}
 * />
 *
 * // With custom classes and spacing
 * <Address
 *   regionCodes={{
 *     countries: [{ value: 'US', label: 'United States' }]
 *   }}
 *   customClasses={['custom-address-class']}
 *   spacing={4}
 *   customAttributes={{ 'data-test': 'address-component' }}
 * >
 *   <div>Additional content</div>
 * </Address>
 */
const Address = forwardRef( ( {
	children = null,
	customAttributes = {},
	customClasses = [],
	countryDropdownAttributes = {},
	countryDropdownClasses = [],
	defaultData = {},
	disabledFields = [],
	initialData = {},
	i18n = {},
	onChange = () => {},
	parseRegionCodes = false,
	preferredRegions = {},
	regionCodes = {},
	spacing = '',
	stateDropdownAttributes = {},
	stateDropdownClasses = [],
	useSelectPlaceholders = true,
}, ref ) => {
	const getInitialValue = ( field ) => {
		if ( disabledFields.includes( field ) ) {
			return undefined;
		}
		return initialData[ field ] || defaultData[ field ] || '';
	};

	const [ addressData, setAddressData ] = useState( () => (
		[ 'lineOne', 'lineTwo', 'city', 'state', 'postalCode', 'country' ].reduce( ( carry, key ) => ( {
			...carry,
			[ key ]: getInitialValue( key ),
		} ), {} )
	) );

	const { countries = [], usStates = [], caProvinces = [] } = parseRegionCodes ? transformRegionCodes( regionCodes ) : regionCodes;

	const sortedCountries = sortRegions( countries, preferredRegions.countries );
	const sortedUsStates = sortRegions( usStates, preferredRegions.usStates );
	const sortedCaProvinces = sortRegions( caProvinces, preferredRegions.caProvinces );

	useEffect( () => {
		const filteredData = Object.fromEntries(
			Object.entries( addressData ).filter( ( [ key ] ) => ! disabledFields.includes( key ) )
		);
		onChange( filteredData );
	}, [ addressData ] );

	const handleInputChange = ( field ) => ( value ) => {
		if ( ! disabledFields.includes( field ) ) {
			setAddressData( ( prev ) => ( {
				...prev,
				[ field ]: value,
			} ) );
		}
	};

	const handleDropdownChange = ( field ) => ( event, regionCode ) => {
		if ( ! disabledFields.includes( field ) ) {
			let options = [];
			if ( field === 'country' ) {
				options = sortedCountries;
			} else if ( field === 'state' ) {
				if ( addressData.country === 'US' ) {
					options = sortedUsStates;
				} else if ( addressData.country === 'CA' ) {
					options = sortedCaProvinces;
				}
			}

			const selected = options.find( ( opt ) => opt.value === regionCode.value );

			setAddressData( ( prev ) => ( {
				...prev,
				[ field ]: selected ? selected.value : regionCode.value,
				...( field === 'country' && ! disabledFields.includes( 'state' ) && { state: '' } ),
			} ) );
		}
	};

	const getStateLabelPlaceholder = () => {
		if ( addressData.country === 'US' ) {
			return {
				label: i18n.stateLabel || NEEDS_I18N_LABEL,
				placeholder: i18n.statePlaceholder || NEEDS_I18N_LABEL,
			};
		}
		if ( addressData.country === 'CA' ) {
			return {
				label: i18n.provinceLabel || NEEDS_I18N_LABEL,
				placeholder: i18n.provincePlaceholder || NEEDS_I18N_LABEL,
			};
		}
		return {
			label: i18n.stateProvinceLabel || NEEDS_I18N_LABEL,
			placeholder: i18n.stateProvincePlaceholder || NEEDS_I18N_LABEL,
		};
	};

	const getPostalCodeLabelPlaceholder = () => {
		if ( addressData.country === 'US' ) {
			return {
				label: i18n.zipCodeLabel || NEEDS_I18N_LABEL,
				placeholder: i18n.zipCodePlaceholder || NEEDS_I18N_LABEL,
			};
		}
		if ( addressData.country === 'CA' ) {
			return {
				label: i18n.postalCodeLabel || NEEDS_I18N_LABEL,
				placeholder: i18n.postalCodePlaceholder || NEEDS_I18N_LABEL,
			};
		}
		return {
			label: i18n.zipPostalCodeLabel || NEEDS_I18N_LABEL,
			placeholder: i18n.zipPostalCodePlaceholder || NEEDS_I18N_LABEL,
		};
	};

	const renderInputField = ( key ) => {
		if ( disabledFields.includes( key ) ) {
			return null;
		}

		let label = i18n[ `${ key }Label` ] || NEEDS_I18N_LABEL;
		let placeholder = i18n[ `${ key }Placeholder` ] || NEEDS_I18N_LABEL;
		if ( key === 'state' ) {
			( { label, placeholder } = getStateLabelPlaceholder() );
		} else if ( key === 'postalCode' ) {
			( { label, placeholder } = getPostalCodeLabelPlaceholder() );
		}

		return <Input
			key={ key }
			labelAttributes={ { label } }
			value={ addressData[ key ] }
			onChange={ handleInputChange( key ) }
			placeholder={ placeholder }
			spacing={ 3 }
		/>;
	};

	const renderCountryField = () => {
		if ( disabledFields.includes( 'country' ) ) {
			return null;
		}
		if ( sortedCountries.length === 0 ) {
			return renderInputField( 'country' );
		}
		const listItems = useSelectPlaceholders ? [ {
			label: i18n.countrySelectPlaceholder || NEEDS_I18N_LABEL,
			value: '',
		}, ...sortedCountries ] : sortedCountries;
		const countryDropdownProps = {
			controlled: true,
			hasSearch: true,
			i18n,
			label: i18n.countryLabel || NEEDS_I18N_LABEL,
			listItems,
			onChange: handleDropdownChange( 'country' ),
			popoverMaxHeight: 300,
			size: 'r',
			value: addressData.country,
			...countryDropdownAttributes,
			className: classnames( {
				'gform-address__country-dropdown': true,
				...countryDropdownClasses,
			} ),
		};

		return (
			<Dropdown { ...countryDropdownProps } />
		);
	};

	const renderStateProvinceField = () => {
		if ( disabledFields.includes( 'state' ) ) {
			return null;
		}

		if ( addressData.country === 'US' && sortedUsStates.length > 0 ) {
			const listItems = useSelectPlaceholders ? [ {
				label: i18n.stateSelectPlaceholder || NEEDS_I18N_LABEL,
				value: '',
			}, ...sortedUsStates ] : sortedUsStates;
			const stateDropdownProps = {
				controlled: true,
				hasSearch: true,
				i18n,
				label: i18n.stateLabel || NEEDS_I18N_LABEL,
				listItems,
				onChange: handleDropdownChange( 'state' ),
				popoverMaxHeight: 300,
				size: 'r',
				value: addressData.state,
				...stateDropdownAttributes,
				className: classnames( {
					'gform-address__state-province-dropdown': true,
					...stateDropdownClasses,
				} ),
			};

			return ( <Dropdown { ...stateDropdownProps } /> );
		}
		if ( addressData.country === 'CA' && sortedCaProvinces.length > 0 ) {
			const listItems = useSelectPlaceholders ? [ {
				label: i18n.provinceSelectPlaceholder || NEEDS_I18N_LABEL,
				value: '',
			}, ...sortedCaProvinces ] : sortedCaProvinces;
			const stateDropdownProps = {
				controlled: true,
				hasSearch: true,
				i18n,
				label: i18n.provinceLabel || NEEDS_I18N_LABEL,
				listItems,
				onChange: handleDropdownChange( 'state' ),
				popoverMaxHeight: 300,
				size: 'r',
				value: addressData.state,
				...stateDropdownAttributes,
				className: classnames( {
					'gform-address__state-province-dropdown': true,
					...stateDropdownClasses,
				} ),
			};

			return (
				<Dropdown { ...stateDropdownProps } />
			);
		}

		return renderInputField( 'state' );
	};

	const attributes = {
		className: classnames( {
			'gform-address': true,
			...spacerClasses( spacing ),
		}, customClasses ),
		ref,
		...customAttributes,
	};

	return (
		<div { ...attributes }>
			{ renderInputField( 'lineOne' ) }
			{ renderInputField( 'lineTwo' ) }
			<Box display="flex" customClasses={ [ 'gform-address__row' ] }>
				{ renderInputField( 'city' ) }
				{ renderStateProvinceField() }
			</Box>
			<Box display="flex" customClasses={ [ 'gform-address__row' ] }>
				{ renderInputField( 'postalCode' ) }
				{ renderCountryField() }
			</Box>
			{ children }
		</div>
	);
} );

Address.displayName = 'Address';

export default Address;
