/**
 * External dependencies
 */
import { ComponentType, Dispatch, SetStateAction } from 'react';
import { __ } from '@wordpress/i18n';
import {
	AlignmentToolbar,
	BlockControls as BlockControlsWrapper,
	MediaReplaceFlow,
} from '@wordpress/block-editor';
import type { BlockAlignment } from '@wordpress/blocks';
import { ToolbarButton, ToolbarGroup } from '@wordpress/components';
import { crop } from '@wordpress/icons';
import { WP_REST_API_Category } from 'wp-types';
import { ProductResponseItem } from '@woocommerce/types';
import TextToolbarButton from '@woocommerce/editor-components/text-toolbar-button';

/**
 * Internal dependencies
 */
import { useBackgroundImage } from './use-background-image';
import { EditorBlock, GenericBlockUIConfig } from './types';

type Media = { id: number; url: string };

interface WithBlockControlsRequiredProps< T > {
	attributes: BlockControlRequiredAttributes &
		EditorBlock< T >[ 'attributes' ];
	setAttributes: ( attrs: Partial< BlockControlRequiredAttributes > ) => void;
	useEditingImage: [ boolean, Dispatch< SetStateAction< boolean > > ];
}

interface WithBlockControlsCategoryProps< T >
	extends WithBlockControlsRequiredProps< T > {
	category: WP_REST_API_Category;
	product: never;
}

interface WithBlockControlsProductProps< T >
	extends WithBlockControlsRequiredProps< T > {
	category: never;
	product: ProductResponseItem;
}

type WithBlockControlsProps< T extends EditorBlock< T > > =
	| ( T & WithBlockControlsCategoryProps< T > )
	| ( T & WithBlockControlsProductProps< T > );

type BlockControlRequiredAttributes = {
	contentAlign: BlockAlignment;
	editMode: boolean;
	mediaId: number;
	mediaSrc: string;
};

interface BlockControlsProps {
	backgroundImageId: number;
	backgroundImageSrc: string;
	contentAlign: BlockAlignment;
	cropLabel: string;
	editLabel: string;
	editMode: boolean;
	isEditingImage: boolean;
	mediaSrc: string;
	setAttributes: ( attrs: Partial< BlockControlRequiredAttributes > ) => void;
	setIsEditingImage: ( value: boolean ) => void;
}

interface BlockControlsConfiguration extends GenericBlockUIConfig {
	cropLabel: string;
	editLabel: string;
}

export const BlockControls = ( {
	backgroundImageId,
	backgroundImageSrc,
	contentAlign,
	cropLabel,
	editLabel,
	editMode,
	isEditingImage,
	mediaSrc,
	setAttributes,
	setIsEditingImage,
}: BlockControlsProps ) => {
	return (
		<BlockControlsWrapper>
			<AlignmentToolbar
				value={ contentAlign }
				onChange={ ( nextAlign: BlockAlignment ) => {
					setAttributes( { contentAlign: nextAlign } );
				} }
			/>
			<ToolbarGroup>
				{ backgroundImageSrc && ! isEditingImage && (
					<ToolbarButton
						onClick={ () => setIsEditingImage( true ) }
						icon={ crop }
						label={ cropLabel }
					/>
				) }
				<MediaReplaceFlow
					mediaId={ backgroundImageId }
					mediaURL={ mediaSrc }
					accept="image/*"
					onSelect={ ( media: Media ) => {
						setAttributes( {
							mediaId: media.id,
							mediaSrc: media.url,
						} );
					} }
					allowedTypes={ [ 'image' ] }
				/>
				{ backgroundImageId && mediaSrc ? (
					<TextToolbarButton
						onClick={ () =>
							setAttributes( { mediaId: 0, mediaSrc: '' } )
						}
					>
						{ __( 'Reset', 'woo-gutenberg-products-block' ) }
					</TextToolbarButton>
				) : null }
			</ToolbarGroup>
			<ToolbarGroup
				controls={ [
					{
						icon: 'edit',
						title: editLabel,
						onClick: () =>
							setAttributes( { editMode: ! editMode } ),
						isActive: editMode,
					},
				] }
			/>
		</BlockControlsWrapper>
	);
};

export const withBlockControls = ( {
	cropLabel,
	editLabel,
}: BlockControlsConfiguration ) => < T extends EditorBlock< T > >(
	Component: ComponentType< T >
) => ( props: WithBlockControlsProps< T > ) => {
	const [ isEditingImage, setIsEditingImage ] = props.useEditingImage;
	const { attributes, category, name, product, setAttributes } = props;
	const { contentAlign, editMode, mediaId, mediaSrc } = attributes;
	const item = category || product;

	const { backgroundImageId, backgroundImageSrc } = useBackgroundImage( {
		item,
		mediaId,
		mediaSrc,
		blockName: name,
	} );

	return (
		<>
			<BlockControls
				backgroundImageId={ backgroundImageId }
				backgroundImageSrc={ backgroundImageSrc }
				contentAlign={ contentAlign }
				cropLabel={ cropLabel }
				editLabel={ editLabel }
				editMode={ editMode }
				isEditingImage={ isEditingImage }
				mediaSrc={ mediaSrc }
				setAttributes={ setAttributes }
				setIsEditingImage={ setIsEditingImage }
			/>
			<Component { ...props } />
		</>
	);
};
