import { React, PropTypes, classnames } from '@gravityforms/libraries';
import { spacerClasses } from '@gravityforms/utils';
import RingLoader from '../../modules/Loaders/RingLoader';
import Tooltip from '../../modules/Tooltip';

const { forwardRef, useEffect, useRef, useState } = React;

/**
 * @module Label
 * @description A label component.
 *
 * @since 1.1.18
 *
 * @param {object}                     props                  Component props.
 * @param {JSX.Element|string}         props.children         React element children.
 * @param {object}                     props.customAttributes Custom attributes for the component.
 * @param {string|Array|object}        props.customClasses    Custom classes for the component.
 * @param {string}                     props.htmlFor          The for attribute for the label.
 * @param {string}                     props.icon             The icon for the label tooltip.
 * @param {string}                     props.iconPrefix       The icon prefix for the label.
 * @param {boolean}                    props.isVisible        If label is visible (true) or hidden (false).
 * @param {string}                     props.label            Label text.
 * @param {object}                     props.loaderProps      Props passed to the RingLoader when loading. All RingLoader props can be overridden.
 * @param {boolean}                    props.loading          If true, displays a RingLoader inline with the label text.
 * @param {number}                     props.loadingDelay     Delay in ms before the loading indicator appears. Prevents flash for fast operations. Default 0.
 * @param {string}                     props.loadingText      Optional text to display in place of label while loading. If empty, the label text remains.
 * @param {boolean}                    props.locked           If the label is locked (disables form element and puts an icon with optional tooltip next to label).
 * @param {string}                     props.lockedMessage    The message to display when the label is locked, in a tooltip. (optional)
 * @param {string}                     props.size             The font size for the label.
 * @param {string|number|Array|object} props.spacing          The spacing for the component, as a string, number, array, or object.
 * @param {string}                     props.tagName          The HTML tag to use for the label element (e.g., 'label', 'legend').
 * @param {string}                     props.weight           The font weight for the label.
 * @param {object|null}                ref                    Ref to the component.
 *
 * @return {JSX.Element} The Label component.
 *
 * @example
 * import Label from '@gravityforms/components/react/admin/elements/Label';
 *
 * return <Label htmlFor="input-id">{ 'Input label' }</Label>;
 *
 */
const Label = forwardRef( ( {
	children = null,
	customAttributes = {},
	customClasses = [],
	htmlFor = '',
	icon = 'lock',
	iconPrefix = 'gravity-component-icon',
	isVisible = true,
	label = '',
	loaderProps = {
		customClasses: 'gform-label__loader',
		lineWeight: 2,
		size: 16,
		spacing: [ 0, 2, 0, 0 ],
	},
	loading = false,
	loadingDelay = 0,
	loadingText = '',
	locked = false,
	lockedMessage = '',
	size = 'text-sm',
	spacing = '',
	tagName = 'label',
	weight = 'medium',
}, ref ) => {
	const [ delayedLoading, setDelayedLoading ] = useState( false );
	const timerRef = useRef( null );

	useEffect( () => {
		if ( loading && loadingDelay > 0 ) {
			timerRef.current = setTimeout( () => {
				setDelayedLoading( true );
			}, loadingDelay );
		} else {
			clearTimeout( timerRef.current );
			setDelayedLoading( loading );
		}

		return () => clearTimeout( timerRef.current );
	}, [ loading, loadingDelay ] );

	const isLoading = loadingDelay > 0 ? delayedLoading : loading;

	if ( ! label && ! children && ! ( isLoading && loadingText ) ) {
		return null;
	}

	const componentProps = {
		className: classnames( {
			'gform-label': true,
			'gform-label--loading': isLoading,
			'gform-legend': tagName === 'legend',
			[ `gform-typography--size-${ size }` ]: true,
			[ `gform-typography--weight-${ weight }` ]: true,
			'gform-visually-hidden': ! isVisible,
			...spacerClasses( spacing ),
		}, customClasses ),
		ref,
		...customAttributes,
	};

	if ( tagName === 'label' && htmlFor ) {
		componentProps.htmlFor = htmlFor;
	}

	const tooltipProps = {
		content: lockedMessage,
		icon,
		iconPrefix,
		iconPreset: 'status-locked',
		maxWidth: 300,
		position: 'right',
		spacing: [ 0, 0, 0, 2 ],
		tagName: 'span',
		theme: 'port',
	};

	const Tag = tagName;

	return (
		<Tag { ...componentProps }>
			{ isLoading && <RingLoader { ...loaderProps } /> }
			{ isLoading && loadingText ? loadingText : label }
			{ children }
			{ locked && <Tooltip { ...tooltipProps } /> }
		</Tag>
	);
} );

Label.propTypes = {
	children: PropTypes.oneOfType( [
		PropTypes.arrayOf( PropTypes.node ),
		PropTypes.node,
	] ),
	customAttributes: PropTypes.object,
	customClasses: PropTypes.oneOfType( [
		PropTypes.string,
		PropTypes.array,
		PropTypes.object,
	] ),
	htmlFor: PropTypes.string,
	iconPrefix: PropTypes.string,
	isVisible: PropTypes.bool,
	label: PropTypes.string,
	loaderProps: PropTypes.object,
	loading: PropTypes.bool,
	loadingDelay: PropTypes.number,
	loadingText: PropTypes.string,
	locked: PropTypes.bool,
	lockedMessage: PropTypes.string,
	size: PropTypes.string,
	spacing: PropTypes.oneOfType( [
		PropTypes.string,
		PropTypes.number,
		PropTypes.array,
		PropTypes.object,
	] ),
	tagName: PropTypes.string,
	weight: PropTypes.string,
};

Label.displayName = 'Label';

export default Label;
