text: /**
   * External dependencies
   */
  import { useEffect, useRef } from '@wordpress/element';
  
  /**
   * Internal dependencies
   */
  import {
  	normalizeShippingOptions,
  	getTotalPaymentItem,
  	normalizeLineItems,
  	getBillingData,
  	getPaymentMethodData,
  	getShippingData,
  } from '../stripe-utils';
  
  /**
   * @typedef {import('@woocommerce/type-defs/registered-payment-method-props').EventRegistrationProps} EventRegistrationProps
   * @typedef {import('@woocommerce/type-defs/registered-payment-method-props').BillingDataProps} BillingDataProps
   * @typedef {import('@woocommerce/type-defs/registered-payment-method-props').ShippingDataProps} ShippingDataProps
   * @typedef {import('@woocommerce/type-defs/registered-payment-method-props').EmitResponseProps} EmitResponseProps
   */
  
  /**
   * @param {Object} props
   *
   * @param {boolean}                props.canMakePayment                  Whether the payment request
   *                                                                       can make payment or not.
   * @param {boolean}                props.isProcessing                    Whether the express payment
   *                                                                       method is processing or not.
   * @param {EventRegistrationProps} props.eventRegistration               Various functions for
   *                                                                       registering observers to
   *                                                                       events.
   * @param {Object}                 props.paymentRequestEventHandlers     Cached handlers registered
   *                                                                       for paymentRequest events.
   * @param {function(string):void}  props.clearPaymentRequestEventHandler Clears the cached payment
   *                                                                       request event handler.
   * @param {BillingDataProps}       props.billing
   * @param {ShippingDataProps}      props.shippingData
   * @param {EmitResponseProps}      props.emitResponse
   * @param {string}                 props.paymentRequestType              The derived payment request
   *                                                                       type for the express
   *                                                                       payment being processed.
   * @param {function(any):void}     props.completePayment                 This is a callback
   *                                                                       receiving the source event
   *                                                                       and setting it to
   *                                                                       successful payment.
   * @param {function(any,string):any}     props.abortPayment                    This is a callback
   *                                                                       receiving the source
   *                                                                       event and setting it to
   *                                                                       failed payment.
   */
  export const useCheckoutSubscriptions = ( {
  	canMakePayment,
  	isProcessing,
  	eventRegistration,
  	paymentRequestEventHandlers,
  	clearPaymentRequestEventHandler,
  	billing,
  	shippingData,
  	emitResponse,
  	paymentRequestType,
  	completePayment,
  	abortPayment,
  } ) => {
  	const {
  		onShippingRateSuccess,
  		onShippingRateFail,
  		onShippingRateSelectSuccess,
  		onShippingRateSelectFail,
  		onPaymentProcessing,
  		onCheckoutAfterProcessingWithSuccess,
  		onCheckoutAfterProcessingWithError,
  	} = eventRegistration;
  	const { noticeContexts, responseTypes } = emitResponse;
  	const eventHandlers = useRef( paymentRequestEventHandlers );
  	const currentBilling = useRef( billing );
  	const currentShipping = useRef( shippingData );
  	const currentPaymentRequestType = useRef( paymentRequestType );
  
  	useEffect( () => {
  		eventHandlers.current = paymentRequestEventHandlers;
  		currentBilling.current = billing;
  		currentShipping.current = shippingData;
  		currentPaymentRequestType.current = paymentRequestType;
  	}, [
  		paymentRequestEventHandlers,
  		billing,
  		shippingData,
  		paymentRequestType,
  	] );
  
  	// subscribe to events.
  	useEffect( () => {
  		const onShippingRatesEvent = ( shippingRates ) => {
  			const handlers = eventHandlers.current;
  			const billingData = currentBilling.current;
  			if ( handlers.shippingAddressChange && isProcessing ) {
  				handlers.shippingAddressChange.updateWith( {
  					status: 'success',
  					shippingOptions: normalizeShippingOptions( shippingRates ),
  					total: getTotalPaymentItem( billingData.cartTotal ),
  					displayItems: normalizeLineItems(
  						billingData.cartTotalItems
  					),
  				} );
  				clearPaymentRequestEventHandler( 'shippingAddressChange' );
  			}
  		};
  		const onShippingRatesEventFail = ( currentErrorStatus ) => {
  			const handlers = eventHandlers.current;
  			if ( handlers.shippingAddressChange && isProcessing ) {
  				handlers.shippingAddressChange.updateWith( {
  					status: currentErrorStatus.hasInvalidAddress
  						? 'invalid_shipping_address'
  						: 'fail',
  					shippingOptions: [],
  				} );
  			}
  			clearPaymentRequestEventHandler( 'shippingAddressChange' );
  		};
  		const onShippingSelectedRate = ( forSuccess = true ) => () => {
  			const handlers = eventHandlers.current;
  			const shipping = currentShipping.current;
  			const billingData = currentBilling.current;
  			if (
  				handlers.shippingOptionChange &&
  				! shipping.isSelectingRate &&
  				isProcessing
  			) {
  				const updateObject = forSuccess
  					? {
  							status: 'success',
  							total: getTotalPaymentItem( billingData.cartTotal ),
  							displayItems: normalizeLineItems(
  								billingData.cartTotalItems
  							),
  					  }
  					: {
  							status: 'fail',
  					  };
  				handlers.shippingOptionChange.updateWith( updateObject );
  				clearPaymentRequestEventHandler( 'shippingOptionChange' );
  			}
  		};
  		const onProcessingPayment = () => {
  			const handlers = eventHandlers.current;
  			if ( handlers.sourceEvent && isProcessing ) {
  				const response = {
  					type: responseTypes.SUCCESS,
  					meta: {
  						billingData: getBillingData( handlers.sourceEvent ),
  						paymentMethodData: getPaymentMethodData(
  							handlers.sourceEvent,
  							currentPaymentRequestType.current
  						),
  						shippingData: getShippingData( handlers.sourceEvent ),
  					},
  				};
  				return response;
  			}
  			return { type: responseTypes.SUCCESS };
  		};
  		const onCheckoutComplete = ( checkoutResponse ) => {
  			const handlers = eventHandlers.current;
  			let response = { type: responseTypes.SUCCESS };
  			if ( handlers.sourceEvent && isProcessing ) {
  				const {
  					paymentStatus,
  					paymentDetails,
  				} = checkoutResponse.processingResponse;
  				if ( paymentStatus === responseTypes.SUCCESS ) {
  					completePayment( handlers.sourceEvent );
  				}
  				if (
  					paymentStatus === responseTypes.ERROR ||
  					paymentStatus === responseTypes.FAIL
  				) {
  					abortPayment( handlers.sourceEvent );
  					response = {
  						type: responseTypes.ERROR,
  						message: paymentDetails?.errorMessage,
  						messageContext: noticeContexts.EXPRESS_PAYMENTS,
  						retry: true,
  					};
  				}
  				clearPaymentRequestEventHandler( 'sourceEvent' );
  			}
  			return response;
  		};
  		if ( canMakePayment && isProcessing ) {
  			const unsubscribeShippingRateSuccess = onShippingRateSuccess(
  				onShippingRatesEvent
  			);
  			const unsubscribeShippingRateFail = onShippingRateFail(
  				onShippingRatesEventFail
  			);
  			const unsubscribeShippingRateSelectSuccess = onShippingRateSelectSuccess(
  				onShippingSelectedRate()
  			);
  			const unsubscribeShippingRateSelectFail = onShippingRateSelectFail(
  				onShippingRatesEventFail
  			);
  			const unsubscribePaymentProcessing = onPaymentProcessing(
  				onProcessingPayment
  			);
  			const unsubscribeCheckoutCompleteSuccess = onCheckoutAfterProcessingWithSuccess(
  				onCheckoutComplete
  			);
  			const unsubscribeCheckoutCompleteFail = onCheckoutAfterProcessingWithError(
  				onCheckoutComplete
  			);
  			return () => {
  				unsubscribeCheckoutCompleteFail();
  				unsubscribeCheckoutCompleteSuccess();
  				unsubscribePaymentProcessing();
  				unsubscribeShippingRateFail();
  				unsubscribeShippingRateSuccess();
  				unsubscribeShippingRateSelectSuccess();
  				unsubscribeShippingRateSelectFail();
  			};
  		}
  		return undefined;
  	}, [
  		canMakePayment,
  		isProcessing,
  		onShippingRateSuccess,
  		onShippingRateFail,
  		onShippingRateSelectSuccess,
  		onShippingRateSelectFail,
  		onPaymentProcessing,
  		onCheckoutAfterProcessingWithSuccess,
  		onCheckoutAfterProcessingWithError,
  		responseTypes,
  		noticeContexts,
  		completePayment,
  		abortPayment,
  		clearPaymentRequestEventHandler,
  	] );
  };
  