/**
 * External dependencies
 */
import { __ } from '@wordpress/i18n';
import { COUNTRIES_STORE_NAME } from '@woocommerce/data';
import { decodeEntities } from '@wordpress/html-entities';
import { escapeRegExp } from 'lodash';
import { useEffect, useMemo, useState, useRef } from '@wordpress/element';
import { SelectControl, TextControl } from '@woocommerce/components';
import { Spinner } from '@wordpress/components';
import { useSelect } from '@wordpress/data';

/**
 * Internal dependencies
 */
import { getAdminSetting } from '~/utils/admin-settings';

const { countries } = getAdminSetting( 'dataEndpoints', { countries: {} } );
/**
 * Check if a given address field is required for the locale.
 *
 * @param {string} fieldName Name of the field to check.
 * @param {Object} locale Locale data.
 * @return {boolean} Field requirement.
 */
export function isAddressFieldRequired( fieldName, locale = {} ) {
	if ( locale[ fieldName ]?.hasOwnProperty( 'required' ) ) {
		return locale[ fieldName ]?.required;
	}

	if ( fieldName === 'address_2' ) {
		return false;
	}

	return true;
}

/**
 * Form validation.
 *
 * @param {Object} locale The store locale.
 * @return {Function} Validator function.
 */
export function getStoreAddressValidator( locale = {} ) {
	/**
	 * Form validator.
	 *
	 * @param {Object} values Keyed values of all fields in the form.
	 * @return {Object} Key value of fields and error messages, { myField: 'This field is required' }
	 */
	return ( values ) => {
		const errors: {
			[ key: string ]: string;
		} = {};

		if (
			isAddressFieldRequired( 'address_1', locale ) &&
			! values.addressLine1.trim().length
		) {
			errors.addressLine1 = __(
				'Please add an address',
				'woocommerce-admin'
			);
		}
		if ( ! values.countryState.trim().length ) {
			errors.countryState = __(
				'Please select a country / region',
				'woocommerce-admin'
			);
		}
		if (
			isAddressFieldRequired( 'city', locale ) &&
			! values.city.trim().length
		) {
			errors.city = __( 'Please add a city', 'woocommerce-admin' );
		}
		if (
			isAddressFieldRequired( 'postcode', locale ) &&
			! values.postCode.trim().length
		) {
			errors.postCode = __(
				'Please add a post code',
				'woocommerce-admin'
			);
		}

		return errors;
	};
}

/**
 * Get all country and state combinations used for select dropdowns.
 *
 * @return {Object} Select options, { value: 'US:GA', label: 'United States - Georgia' }
 */
export function getCountryStateOptions() {
	const countryStateOptions = countries.reduce( ( acc, country ) => {
		if ( ! country.states.length ) {
			acc.push( {
				key: country.code,
				label: decodeEntities( country.name ),
			} );

			return acc;
		}

		const countryStates = country.states.map( ( state ) => {
			return {
				key: country.code + ':' + state.code,
				label:
					decodeEntities( country.name ) +
					' — ' +
					decodeEntities( state.name ),
			};
		} );

		acc.push( ...countryStates );

		return acc;
	}, [] );

	return countryStateOptions;
}

/**
 * Get the autofill countryState fields and set value from filtered options.
 *
 * @param {Array} options Array of filterable options.
 * @param {string} countryState The value of the countryState field.
 * @param {Function} setValue Set value of the countryState input.
 * @return {Object} React component.
 */
export function useGetCountryStateAutofill( options, countryState, setValue ) {
	const [ autofillCountry, setAutofillCountry ] = useState( '' );
	const [ autofillState, setAutofillState ] = useState( '' );
	const isAutofillChange: {
		current: boolean;
	} = useRef();

	useEffect( () => {
		const option = options.find( ( opt ) => opt.key === countryState );
		const labels = option ? option.label.split( /\u2013|\u2014|\-/ ) : [];
		const newCountry = ( labels[ 0 ] || '' ).trim();
		const newState = ( labels[ 1 ] || '' ).trim();

		if (
			! isAutofillChange.current &&
			( newCountry !== autofillCountry || newState !== autofillState )
		) {
			setAutofillCountry( newCountry );
			setAutofillState( newState );
		}
		isAutofillChange.current = false;
	}, [ countryState ] );

	useEffect( () => {
		if ( ! autofillCountry && ! autofillState && countryState ) {
			// Clear form.
			isAutofillChange.current = true;
			setValue( 'countryState', '' );
		}
		let filteredOptions = [];
		const countrySearch = new RegExp(
			escapeRegExp( autofillCountry ),
			'i'
		);
		const stateSearch = new RegExp(
			escapeRegExp( autofillState.replace( /\s/g, '' ) ) + '$', // Always match the end of string for region.
			'i'
		);
		if ( autofillState.length || autofillCountry.length ) {
			filteredOptions = options.filter( ( option ) =>
				( autofillCountry.length ? countrySearch : stateSearch ).test(
					option.label
				)
			);
		}
		if ( autofillCountry.length && autofillState.length ) {
			const isStateAbbreviation = autofillState.length < 3;
			filteredOptions = filteredOptions.filter( ( option ) =>
				stateSearch.test(
					( isStateAbbreviation ? option.key : option.label )
						.replace( '-', '' )
						.replace( /\s/g, '' )
				)
			);

			const isCountryAbbreviation = autofillCountry.length < 3;
			if ( filteredOptions.length > 1 ) {
				let countryKeyOptions = [];
				countryKeyOptions = filteredOptions.filter( ( option ) =>
					countrySearch.test(
						isCountryAbbreviation ? option.key : option.label
					)
				);

				if ( countryKeyOptions.length > 0 ) {
					filteredOptions = countryKeyOptions;
				}
			}

			if ( filteredOptions.length > 1 ) {
				let stateKeyOptions = [];
				stateKeyOptions = filteredOptions.filter( ( option ) =>
					stateSearch.test(
						( isStateAbbreviation ? option.key : option.label )
							.replace( '-', '' )
							.replace( /\s/g, '' )
					)
				);

				if ( stateKeyOptions.length === 1 ) {
					filteredOptions = stateKeyOptions;
				}
			}
		}

		if (
			filteredOptions.length === 1 &&
			countryState !== filteredOptions[ 0 ].key
		) {
			isAutofillChange.current = true;
			setValue( 'countryState', filteredOptions[ 0 ].key );
		}
	}, [ autofillCountry, autofillState, options, setValue ] );

	return (
		<>
			<input
				onChange={ ( event ) =>
					setAutofillCountry( event.target.value )
				}
				value={ autofillCountry }
				name="country"
				type="text"
				className="woocommerce-select-control__autofill-input"
				tabIndex="-1"
				autoComplete="country"
			/>

			<input
				onChange={ ( event ) => setAutofillState( event.target.value ) }
				value={ autofillState }
				name="state"
				type="text"
				className="woocommerce-select-control__autofill-input"
				tabIndex="-1"
				autoComplete="address-level1"
			/>
		</>
	);
}

/**
 * Store address fields.
 *
 * @param {Object} props Props for input components.
 * @return {Object} -
 */
export function StoreAddress( props ) {
	const { getInputProps, setValue, onChange } = props;
	const countryState = getInputProps( 'countryState' ).value;
	const { locale, hasFinishedResolution } = useSelect( ( select ) => {
		return {
			locale: select( COUNTRIES_STORE_NAME ).getLocale( countryState ),
			hasFinishedResolution: select(
				COUNTRIES_STORE_NAME
			).hasFinishedResolution( 'getLocales' ),
		};
	} );
	const countryStateOptions = useMemo( () => getCountryStateOptions(), [] );
	const countryStateAutofill = useGetCountryStateAutofill(
		countryStateOptions,
		countryState,
		setValue
	);

	if ( ! hasFinishedResolution ) {
		return <Spinner />;
	}

	return (
		<div className="woocommerce-store-address-fields">
			{ ! locale?.address_1?.hidden && (
				<TextControl
					label={
						locale?.address_1?.label ||
						__( 'Address line 1', 'woocommerce-admin' )
					}
					required={ isAddressFieldRequired( 'address_1', locale ) }
					autoComplete="address-line1"
					{ ...getInputProps( 'addressLine1' ) }
				/>
			) }

			{ ! locale?.address_2?.hidden && (
				<TextControl
					label={
						locale?.address_2?.label ||
						__( 'Address line 2 (optional)', 'woocommerce-admin' )
					}
					required={ isAddressFieldRequired( 'address_2', locale ) }
					autoComplete="address-line2"
					{ ...getInputProps( 'addressLine2' ) }
				/>
			) }

			<SelectControl
				label={ __( 'Country / Region', 'woocommerce-admin' ) }
				required
				autoComplete="new-password" // disable autocomplete and autofill
				options={ countryStateOptions }
				excludeSelectedOptions={ false }
				showAllOnFocus
				isSearchable
				{ ...getInputProps( 'countryState' ) }
				controlClassName={ getInputProps( 'countryState' ).className }
			>
				{ countryStateAutofill }
			</SelectControl>

			{ ! locale?.city?.hidden && (
				<TextControl
					label={
						locale?.city?.label || __( 'City', 'woocommerce-admin' )
					}
					required={ isAddressFieldRequired( 'city', locale ) }
					{ ...getInputProps( 'city' ) }
					autoComplete="address-level2"
				/>
			) }

			{ ! locale?.postcode?.hidden && (
				<TextControl
					label={
						locale?.postcode?.label ||
						__( 'Post code', 'woocommerce-admin' )
					}
					required={ isAddressFieldRequired( 'postcode', locale ) }
					autoComplete="postal-code"
					{ ...getInputProps( 'postCode' ) }
				/>
			) }
		</div>
	);
}
