import { React, ReactDND, PropTypes, classnames } from '@gravityforms/libraries';
import { sprintf } from '@gravityforms/utils';
import itemTypes from './item-types';
import KanbanLoader from './KanbanLoader';
import Box from '../../elements/Box';
import Card from '../Cards/Card';
import Droplist from '../Droplist';

const { useDrag } = ReactDND;

const NEEDS_I18N_LABEL = 'Needs i18n';

/**
 * @module KanbanCard
 * @description A component that represents a card in a Kanban board.
 *
 * @since 5.8.4
 *
 * @param {object}                     props                  Component props.
 * @param {JSX.Element|null}           props.children         The content of the card.
 * @param {Array<object>}              props.columns          The columns in the Kanban board.
 * @param {string}                     props.columnId         The ID of the column this card belongs to.
 * @param {object}                     props.customAttributes Custom attributes for the card.
 * @param {string|Array|object}        props.customClasses    Custom classes for the card.
 * @param {JSX.Element|null}           props.footerContent    The content to display in the card footer.
 * @param {JSX.Element|null}           props.headerContent    The content to display in the card header.
 * @param {boolean}                    props.hoveredAbove     Whether the card is being hovered above.
 * @param {boolean}                    props.hoveredBelow     Whether the card is being hovered below.
 * @param {object}                     props.i18n             I18n strings for the Kanban component.
 * @param {string}                     props.id               The ID of the card.
 * @param {number}                     props.index            The index of the card within its column.
 * @param {boolean}                    props.isFirstCard      Whether this card is the first in its column.
 * @param {boolean}                    props.isLastCard       Whether this card is the last in its column.
 * @param {boolean}                    props.isLoading        Whether the card is in a loading state.
 * @param {string}                     props.kanbanId         The ID for the Kanban board.
 * @param {Function}                   props.moveCard         Function to move the card to a different position or column.
 * @param {Function}                   props.onDelete         Callback when the card is deleted.
 * @param {Function}                   props.onDragEnd        Callback when the drag operation ends.
 * @param {Function}                   props.onEdit           Callback when the card is edited.
 * @param {boolean}                    props.showActions      Whether to show the action menu on the card.
 * @param {string|number|Array|object} props.spacing          Spacing for the card.
 *
 * @return {JSX.Element} The Kanban Card component.
 */
const KanbanCard = ( {
	children = null,
	columns = [],
	columnId = '',
	customAttributes = {},
	customClasses = [],
	footerContent = null,
	headerContent = null,
	hoveredAbove = false,
	hoveredBelow = false,
	i18n = {},
	id = '',
	index = 0,
	isFirstCard = false,
	isLastCard = false,
	isLoading = false,
	kanbanId = '',
	moveCard = () => {},
	onDelete = () => {},
	onDragEnd = () => {},
	onEdit = () => {},
	showActions = true,
	spacing = 0,
} ) => {
	const cardItemType = `${ itemTypes.KANBAN_CARD }_${ kanbanId }`;

	const [ { isDragging }, drag, preview ] = useDrag( {
		type: cardItemType,
		item: { id, index, columnId },
		collect: ( monitor ) => ( {
			isDragging: monitor.isDragging(),
		} ),
		end: () => {
			onDragEnd();
		},
	} );

	const droplistProps = {
		align: 'right',
		closeOnClick: true,
		id: `${ id }-droplist`,
		listItems: [
			{
				triggerAttributes: {
					id: `${ id }-move-to-column`,
					iconBefore: 'switch-horizontal',
					iconAfter: 'chevron-right',
					label: i18n?.moveToColumn || NEEDS_I18N_LABEL,
				},
				listItems: columns.map( ( column ) => ( {
					key: `${ id }-move-to-column-${ column.id }`,
					props: {
						element: 'button',
						iconAfter: column.id === columnId ? 'check-mark-alt' : undefined,
						label: column.label,
						customAttributes: {
							id: `${ id }-move-to-column-${ column.id }`,
							onClick: () => {
								if ( column.id === columnId ) {
									return;
								}
								moveCard( columnId, column.id, index, column.count, id );
							},
						},
					},
				} ) ),
			},
			{
				key: `${ id }-move-up`,
				props: {
					element: 'button',
					id: `${ id }-move-up`,
					label: i18n?.moveUp || NEEDS_I18N_LABEL,
					iconBefore: 'arrow-sm-up',
					customAttributes: {
						disabled: isFirstCard,
						onClick: () => {
							if ( isFirstCard ) {
								return;
							}
							moveCard( columnId, columnId, index, index - 1, id );
						},
					},
				},
			},
			{
				key: `${ id }-move-down`,
				props: {
					element: 'button',
					id: `${ id }-move-down`,
					label: i18n?.moveDown || NEEDS_I18N_LABEL,
					iconBefore: 'arrow-sm-down',
					customAttributes: {
						disabled: isLastCard,
						onClick: () => {
							if ( isLastCard ) {
								return;
							}
							moveCard( columnId, columnId, index, index + 1, id );
						},
					},
				},
			},
			{
				key: `${ id }-edit-card`,
				props: {
					element: 'button',
					id: `${ id }-edit-card`,
					label: i18n?.editCard || NEEDS_I18N_LABEL,
					iconBefore: 'edit',
					customAttributes: {
						onClick: () => {
							onEdit( id );
						},
					},
				},
			},
			{
				key: `${ id }-delete-card`,
				props: {
					element: 'button',
					id: `${ id }-delete-card`,
					label: i18n?.deleteCard || NEEDS_I18N_LABEL,
					iconBefore: 'trash',
					style: 'error',
					customAttributes: {
						onClick: () => {
							onDelete( id );
						},
					},
				},
			},
		],
		stackNestedGroups: true,
		triggerAttributes: {
			icon: 'dots-horizontal',
			iconPrefix: 'gravity-component-icon',
			size: 'size-height-m',
			title: i18n?.cardActionsTrigger ? sprintf( i18n.cardActionsTrigger, id ) : NEEDS_I18N_LABEL,
			type: 'icon-grey',
		},
		width: 230,
	};

	const cardProps = {
		afterCard: (
			<Box customClasses={ [ 'gform-kanban__card-footer' ] } tagName="footer">
				{
					isLoading
						? <KanbanLoader />
						: footerContent
				}
			</Box>
		),
		beforeCard: (
			<Box customClasses={ [ 'gform-kanban__card-header' ] } display="flex" tagName="header" ref={ drag }>
				{
					isLoading
						? <KanbanLoader />
						: <>
							{ headerContent }
							{ showActions && <Droplist { ...droplistProps } /> }
						</>
				}
			</Box>
		),
		spacing,
		style: 'kanban',
		...customAttributes,
		customClasses: classnames( {
			'gform-kanban__card': true,
			'gform-kanban__card--is-dragging': isDragging,
			'gform-kanban__card--drop-above': hoveredAbove,
			'gform-kanban__card--drop-below': hoveredBelow,
			'gform-kanban__card--loading': isLoading,
		}, customClasses ),
	};

	return (
		<Card { ...cardProps } ref={ preview }>
			<Box customClasses={ [ 'gform-kanban__card-content' ] }>
				{
					isLoading
						? <KanbanLoader />
						: children
				}
			</Box>
		</Card>
	);
};

KanbanCard.propTypes = {
	children: PropTypes.oneOfType( [
		PropTypes.arrayOf( PropTypes.node ),
		PropTypes.node,
	] ),
	columns: PropTypes.arrayOf( PropTypes.shape( {
		count: PropTypes.number,
		id: PropTypes.string,
		label: PropTypes.string,
	} ) ),
	columnId: PropTypes.string,
	customAttributes: PropTypes.object,
	customClasses: PropTypes.oneOfType( [
		PropTypes.string,
		PropTypes.array,
		PropTypes.object,
	] ),
	footerContent: PropTypes.oneOfType( [
		PropTypes.arrayOf( PropTypes.node ),
		PropTypes.node,
	] ),
	headerContent: PropTypes.oneOfType( [
		PropTypes.arrayOf( PropTypes.node ),
		PropTypes.node,
	] ),
	hoveredAbove: PropTypes.bool,
	hoveredBelow: PropTypes.bool,
	i18n: PropTypes.object,
	id: PropTypes.string,
	index: PropTypes.number,
	isFirstCard: PropTypes.bool,
	isLastCard: PropTypes.bool,
	isLoading: PropTypes.bool,
	kanbanId: PropTypes.string,
	moveCard: PropTypes.func,
	onDelete: PropTypes.func,
	onDragEnd: PropTypes.func,
	onEdit: PropTypes.func,
	showActions: PropTypes.bool,
	spacing: PropTypes.oneOfType( [
		PropTypes.string,
		PropTypes.number,
		PropTypes.array,
		PropTypes.object,
	] ),
};

KanbanCard.displayName = 'KanbanCard';

export default KanbanCard;
