/**
 * External dependencies
 */
import { __, _n, sprintf } from '@wordpress/i18n';
import Button from '@woocommerce/base-components/button';
import { Icon, check } from '@wordpress/icons';
import { useState, useEffect } from '@wordpress/element';
import { useAddToCartFormContext } from '@woocommerce/base-context';
import {
	useStoreEvents,
	useStoreAddToCart,
} from '@woocommerce/base-context/hooks';
import { useInnerBlockLayoutContext } from '@woocommerce/shared-context';

/**
 * Add to Cart Form Button Component.
 */
const AddToCartButton = () => {
	const {
		showFormElements,
		productIsPurchasable,
		productHasOptions,
		product,
		productType,
		isDisabled,
		isProcessing,
		eventRegistration,
		hasError,
		dispatchActions,
	} = useAddToCartFormContext();
	const { parentName } = useInnerBlockLayoutContext();
	const { dispatchStoreEvent } = useStoreEvents();
	const { cartQuantity } = useStoreAddToCart( product.id || 0 );
	const [ addedToCart, setAddedToCart ] = useState( false );
	const addToCartButtonData = product.add_to_cart || {
		url: '',
		text: '',
	};

	// Subscribe to emitter for after processing.
	useEffect( () => {
		const onSuccess = () => {
			if ( ! hasError ) {
				setAddedToCart( true );
			}
			return true;
		};
		const unsubscribeProcessing = eventRegistration.onAddToCartAfterProcessingWithSuccess(
			onSuccess,
			0
		);
		return () => {
			unsubscribeProcessing();
		};
	}, [ eventRegistration, hasError ] );

	/**
	 * We can show a real button if we are:
	 *
	 *  	a) Showing a full add to cart form.
	 * 		b) The product doesn't have options and can therefore be added directly to the cart.
	 * 		c) The product is purchasable.
	 *
	 * Otherwise we show a link instead.
	 */
	const showButton =
		( showFormElements ||
			( ! productHasOptions && productType === 'simple' ) ) &&
		productIsPurchasable;

	return showButton ? (
		<ButtonComponent
			className="wc-block-components-product-add-to-cart-button"
			quantityInCart={ cartQuantity }
			isDisabled={ isDisabled }
			isProcessing={ isProcessing }
			isDone={ addedToCart }
			onClick={ () => {
				dispatchActions.submitForm(
					`woocommerce/single-product/${ product?.id || 0 }`
				);
				dispatchStoreEvent( 'cart-add-item', {
					product,
					listName: parentName,
				} );
			} }
		/>
	) : (
		<LinkComponent
			className="wc-block-components-product-add-to-cart-button"
			href={ addToCartButtonData.url }
			text={
				addToCartButtonData.text ||
				__( 'View Product', 'woocommerce' )
			}
			onClick={ () => {
				dispatchStoreEvent( 'product-view-link', {
					product,
					listName: parentName,
				} );
			} }
		/>
	);
};

/**
 * Button component for non-purchasable products.
 *
 * @param {Object}         props           Incoming props.
 * @param {string}         props.className Css classnames.
 * @param {string}         props.href      Link for button.
 * @param {string}         props.text      Text content for button.
 * @param {function():any} props.onClick   Callback to execute when button is clicked.
 */
const LinkComponent = ( { className, href, text, onClick } ) => {
	return (
		<Button
			className={ className }
			href={ href }
			onClick={ onClick }
			rel="nofollow"
		>
			{ text }
		</Button>
	);
};

/**
 * Button for purchasable products.
 *
 * @param {Object}         props                Incoming props for component
 * @param {string}         props.className      Incoming css class name.
 * @param {number}         props.quantityInCart Quantity of item in cart.
 * @param {boolean}        props.isProcessing   Whether processing action is occurring.
 * @param {boolean}        props.isDisabled     Whether the button is disabled or not.
 * @param {boolean}        props.isDone         Whether processing is done.
 * @param {function():any} props.onClick        Callback to execute when button is clicked.
 */
const ButtonComponent = ( {
	className,
	quantityInCart,
	isProcessing,
	isDisabled,
	isDone,
	onClick,
} ) => {
	return (
		<Button
			className={ className }
			disabled={ isDisabled }
			showSpinner={ isProcessing }
			onClick={ onClick }
		>
			{ isDone && quantityInCart > 0
				? sprintf(
						/* translators: %s number of products in cart. */
						_n(
							'%d in cart',
							'%d in cart',
							quantityInCart,
							'woocommerce'
						),
						quantityInCart
				  )
				: __( 'Add to cart', 'woocommerce' ) }
			{ !! isDone && <Icon icon={ check } /> }
		</Button>
	);
};

export default AddToCartButton;
