/**
 * External dependencies
 */
import { defaultAddressFields } from '@woocommerce/settings';
import { useEffect, useCallback, useRef } from '@wordpress/element';

/**
 * Internal dependencies
 */
import {
	useShippingDataContext,
	useCustomerDataContext,
} from '../providers/cart-checkout';

/**
 * Custom hook for exposing address related functionality for the checkout address form.
 */
export const useCheckoutAddress = () => {
	const { needsShipping } = useShippingDataContext();
	const {
		billingData,
		setBillingData,
		shippingAddress,
		setShippingAddress,
		shippingAsBilling,
		setShippingAsBilling,
	} = useCustomerDataContext();

	const currentShippingAsBilling = useRef( shippingAsBilling );
	const previousBillingData = useRef( billingData );

	/**
	 * Sets shipping address data, and also billing if using the same address.
	 */
	const setShippingFields = useCallback(
		( value ) => {
			setShippingAddress( value );

			if ( shippingAsBilling ) {
				setBillingData( value );
			}
		},
		[ shippingAsBilling, setShippingAddress, setBillingData ]
	);

	/**
	 * Sets billing address data, and also shipping if shipping is disabled.
	 */
	const setBillingFields = useCallback(
		( value ) => {
			setBillingData( value );

			if ( ! needsShipping ) {
				setShippingAddress( value );
			}
		},
		[ needsShipping, setShippingAddress, setBillingData ]
	);

	// When the "Use same address" checkbox is toggled we need to update the current billing address to reflect this.
	// This either sets the billing address to the shipping address, or restores the billing address to it's previous state.
	useEffect( () => {
		if ( currentShippingAsBilling.current !== shippingAsBilling ) {
			if ( shippingAsBilling ) {
				previousBillingData.current = billingData;
				setBillingData( shippingAddress );
			} else {
				const {
					// We need to pluck out email from previous billing data because they can be empty, causing the current email to get emptied. See issue #4155
					/* eslint-disable no-unused-vars */
					email,
					/* eslint-enable no-unused-vars */
					...billingAddress
				} = previousBillingData.current;

				setBillingData( {
					...billingAddress,
				} );
			}
			currentShippingAsBilling.current = shippingAsBilling;
		}
	}, [ shippingAsBilling, setBillingData, shippingAddress, billingData ] );

	const setEmail = useCallback(
		( value ) =>
			void setBillingData( {
				email: value,
			} ),
		[ setBillingData ]
	);

	const setPhone = useCallback(
		( value ) =>
			void setBillingData( {
				phone: value,
			} ),
		[ setBillingData ]
	);

	const setShippingPhone = useCallback(
		( value ) =>
			void setShippingFields( {
				phone: value,
			} ),
		[ setShippingFields ]
	);

	// Note that currentShippingAsBilling is returned rather than the current state of shippingAsBilling--this is so that
	// the billing fields are not rendered before sync (billing field values are debounced and would be outdated)
	return {
		defaultAddressFields,
		shippingFields: shippingAddress,
		setShippingFields,
		billingFields: billingData,
		setBillingFields,
		setEmail,
		setPhone,
		setShippingPhone,
		shippingAsBilling,
		setShippingAsBilling,
		showShippingFields: needsShipping,
		showBillingFields:
			! needsShipping || ! currentShippingAsBilling.current,
	};
};