/**
 * External dependencies
 */
import { decodeEntities } from '@wordpress/html-entities';

/**
 * Internal dependencies
 */
import { TYPES, DEFAULT_STATE, STATUS } from './constants';

const {
	SET_PRISTINE,
	SET_IDLE,
	SET_PROCESSING,
	SET_BEFORE_PROCESSING,
	SET_AFTER_PROCESSING,
	SET_PROCESSING_RESPONSE,
	SET_REDIRECT_URL,
	SET_COMPLETE,
	SET_HAS_ERROR,
	SET_NO_ERROR,
	INCREMENT_CALCULATING,
	DECREMENT_CALCULATING,
	SET_CUSTOMER_ID,
	SET_ORDER_ID,
	SET_ORDER_NOTES,
	SET_SHOULD_CREATE_ACCOUNT,
} = TYPES;

const {
	PRISTINE,
	IDLE,
	PROCESSING,
	BEFORE_PROCESSING,
	AFTER_PROCESSING,
	COMPLETE,
} = STATUS;

/**
 * Prepares the payment_result data from the server checkout endpoint response.
 *
 * @param {Object}        data                 The value of `payment_result` from the checkout
 *                                             processing endpoint response.
 * @param {string}        data.message         If there was a general error message it will appear
 *                                             on this property.
 * @param {string}        data.payment_status  The payment status. One of 'success', 'failure',
 *                                             'pending', 'error'.
 * @param {Array<Object>} data.payment_details An array of Objects with a 'key' property that is a
 *                                             string and value property that is a string. These are
 *                                             converted to a flat object where the key becomes the
 *                                             object property and value the property value.
 *
 * @return {Object} A new object with 'paymentStatus', and 'paymentDetails' as the properties.
 */
export const prepareResponseData = ( data ) => {
	const responseData = {
		message: data?.message || '',
		paymentStatus: data.payment_status,
		paymentDetails: {},
	};
	if ( Array.isArray( data.payment_details ) ) {
		data.payment_details.forEach( ( { key, value } ) => {
			responseData.paymentDetails[ key ] = decodeEntities( value );
		} );
	}
	return responseData;
};

/**
 * Reducer for the checkout state
 *
 * @param {Object} state  Current state.
 * @param {Object} action Incoming action object.
 * @param {string} action.url URL passed in.
 * @param {string} action.type Type of action.
 * @param {string} action.customerId Customer ID.
 * @param {string} action.orderId Order ID.
 * @param {Array} action.orderNotes Order notes.
 * @param {boolean} action.shouldCreateAccount True if shopper has requested a user account (signup checkbox).
 * @param {Object} action.data Other action payload.
 */
export const reducer = (
	state = DEFAULT_STATE,
	{ url, type, customerId, orderId, orderNotes, shouldCreateAccount, data }
) => {
	let newState = state;
	switch ( type ) {
		case SET_PRISTINE:
			newState = DEFAULT_STATE;
			break;
		case SET_IDLE:
			newState =
				state.status !== IDLE
					? {
							...state,
							status: IDLE,
					  }
					: state;
			break;
		case SET_REDIRECT_URL:
			newState =
				url !== state.url
					? {
							...state,
							redirectUrl: url,
					  }
					: state;
			break;
		case SET_PROCESSING_RESPONSE:
			newState = {
				...state,
				processingResponse: data,
			};
			break;

		case SET_COMPLETE:
			newState =
				state.status !== COMPLETE
					? {
							...state,
							status: COMPLETE,
							redirectUrl: data?.redirectUrl || state.redirectUrl,
					  }
					: state;
			break;
		case SET_PROCESSING:
			newState =
				state.status !== PROCESSING
					? {
							...state,
							status: PROCESSING,
							hasError: false,
					  }
					: state;
			// clear any error state.
			newState =
				newState.hasError === false
					? newState
					: { ...newState, hasError: false };
			break;
		case SET_BEFORE_PROCESSING:
			newState =
				state.status !== BEFORE_PROCESSING
					? {
							...state,
							status: BEFORE_PROCESSING,
							hasError: false,
					  }
					: state;
			break;
		case SET_AFTER_PROCESSING:
			newState =
				state.status !== AFTER_PROCESSING
					? {
							...state,
							status: AFTER_PROCESSING,
					  }
					: state;
			break;
		case SET_HAS_ERROR:
			newState = state.hasError
				? state
				: {
						...state,
						hasError: true,
				  };
			newState =
				state.status === PROCESSING ||
				state.status === BEFORE_PROCESSING
					? {
							...newState,
							status: IDLE,
					  }
					: newState;
			break;
		case SET_NO_ERROR:
			newState = state.hasError
				? {
						...state,
						hasError: false,
				  }
				: state;
			break;
		case INCREMENT_CALCULATING:
			newState = {
				...state,
				calculatingCount: state.calculatingCount + 1,
			};
			break;
		case DECREMENT_CALCULATING:
			newState = {
				...state,
				calculatingCount: Math.max( 0, state.calculatingCount - 1 ),
			};
			break;
		case SET_CUSTOMER_ID:
			newState = {
				...state,
				customerId,
			};
			break;
		case SET_ORDER_ID:
			newState = {
				...state,
				orderId,
			};
			break;
		case SET_SHOULD_CREATE_ACCOUNT:
			if ( shouldCreateAccount !== state.shouldCreateAccount ) {
				newState = {
					...state,
					shouldCreateAccount,
				};
			}
			break;
		case SET_ORDER_NOTES:
			if ( state.orderNotes !== orderNotes ) {
				newState = {
					...state,
					orderNotes,
				};
			}
			break;
	}
	// automatically update state to idle from pristine as soon as it
	// initially changes.
	if (
		newState !== state &&
		type !== SET_PRISTINE &&
		newState.status === PRISTINE
	) {
		newState.status = IDLE;
	}
	return newState;
};
