/**
 * Build the Advanced-flow browser callbacks shared by all Adyen Web actions.
 *
 * Keeping this state machine separate makes duplicate callback protection and
 * shopper-safe failure behavior directly testable in a real browser.
 *
 * @param {Object} config Localized action-page configuration.
 * @param {Object} dependencies Browser dependencies.
 * @return {Object} Adyen Web lifecycle callbacks.
 */
export function createAdyenActionLifecycle( config, dependencies = {} ) {
	const errorContainer = dependencies.errorContainer || document.getElementById( 'digitalapps-payments-for-adyen-action-error' );
	const fetchImpl = dependencies.fetchImpl || window.fetch.bind( window );
	const navigate = dependencies.navigate || ( ( url ) => window.location.assign( url ) );
	const schedule = dependencies.schedule || window.setTimeout.bind( window );
	let sessionKey = config.sessionKey;
	let completionUrl = config.returnUrl;
	let detailsInFlight = false;
	let navigationScheduled = false;

	const showActionError = ( message ) => {
		if ( ! errorContainer ) {
			return;
		}
		errorContainer.hidden = false;
		errorContainer.replaceChildren();
		const text = document.createElement( 'p' );
		text.textContent = message || config.messages?.failed;
		const link = document.createElement( 'a' );
		link.href = config.retryUrl;
		link.textContent = config.messages?.returnToCheckout || 'Return to checkout';
		errorContainer.append( text, link );
	};

	const scheduleCompletion = () => {
		if ( navigationScheduled ) {
			return;
		}
		navigationScheduled = true;
		schedule( () => navigate( completionUrl ), 0 );
	};

	return {
		onAdditionalDetails: async ( state, component, actions ) => {
			if ( detailsInFlight || navigationScheduled ) {
				return;
			}
			detailsInFlight = true;
			try {
				const response = await fetchImpl( config.detailsUrl, {
					method: 'POST',
					credentials: 'same-origin',
					headers: {
						'Content-Type': 'application/json',
						'X-WP-Nonce': config.restNonce,
					},
					body: JSON.stringify( {
						session_key: sessionKey,
						details: state.data.details,
					} ),
				} );
				const result = await response.json();
				if ( ! response.ok || ! result.success ) {
					throw new Error( config.messages?.failed );
				}
				sessionKey = result.continuation_token || sessionKey;
				completionUrl = result.redirect_url || completionUrl;
				actions.resolve( result.adyen_response );
				if ( result.classification === 'authorised' || result.classification === 'pending' ) {
					scheduleCompletion();
				}
			} catch {
				actions.reject();
				showActionError( config.messages?.failed );
			} finally {
				detailsInFlight = false;
			}
		},
		onPaymentCompleted: scheduleCompletion,
		onPaymentFailed: () => showActionError( config.messages?.failed ),
		onError: () => showActionError( config.messages?.failed ),
		showActionError,
	};
}
