import { React, PropTypes, classnames } from '@gravityforms/libraries';
import { IdProvider, useIdContext } from '@gravityforms/react-utils';
import { spacerClasses } from '@gravityforms/utils';
import Button from '../../elements/Button';
import StatusIndicator from '../../elements/StatusIndicator';
import { ARROW_LEFT, ARROW_RIGHT, HOME, END } from '../../utils/keymap';

const { forwardRef, useEffect, useState } = React;

/**
 * @module TabsComponent
 * @description A tabs component.
 *
 * @since 4.7.2
 *
 * @param {object} props Component props.
 * @param {object} ref   Ref to the component.
 *
 * @return {JSX.Element} The tabs component.
 */
const TabsComponent = forwardRef( ( props, ref ) => {
	const {
		automatic,
		customAttributes,
		customClasses,
		iconPrefix,
		spacing,
		tabs,
	} = props;
	const id = useIdContext();
	const [ activeTab, setActiveTab ] = useState( 1 );
	const [ prevActiveTab, setPrevActiveTab ] = useState( 0 );
	const [ crossfade, setCrossfade ] = useState( {} );
	const totalTabs = tabs.length;

	useEffect( () => {
		setCrossfade( {
			[ activeTab ]: {
				reveal: true,
			},
			[ prevActiveTab ]: {
				hide: true,
			},
		} );
		requestAnimationFrame( () => {
			setCrossfade( {
				[ activeTab ]: {
					reveal: true,
					open: true,
				},
				[ prevActiveTab ]: {
					hide: true,
				},
			} );
		} );
		setTimeout( () => {
			setCrossfade( {
				[ activeTab ]: {
					reveal: false,
					open: true,
				},
				[ prevActiveTab ]: {
					hide: false,
				},
			} );
		}, 150 );
	}, [ activeTab, prevActiveTab ] );

	/**
	 * @function getTablist
	 * @description Get the tablist.
	 *
	 * @since 4.7.2
	 *
	 * @return {JSX.Element} The tablist.
	 */
	const getTablist = () => {
		const tablistProps = {
			className: 'gform-tabs__tablist',
			role: 'tablist',
		};

		return (
			<div { ...tablistProps }>
				{ tabs.map( ( tab, zeroIndex ) => {
					const index = zeroIndex + 1;
					const tabId = `${ id }-tab-${ index }`;
					const tabPanelId = `${ id }-tabpanel-${ index }`;
					const isActive = activeTab === index;
					const tabProps = {
						customAttributes: {
							'aria-controls': tabPanelId,
							'aria-selected': isActive ? 'true' : 'false',
							id: tabId,
							onKeyDown: ( event ) => {
								if ( ! [ ARROW_LEFT, ARROW_RIGHT, HOME, END ].includes( event.key ) ) {
									return;
								}

								let nextIndex;
								switch ( event.key ) {
									case ARROW_LEFT:
										nextIndex = index === 1 ? totalTabs : index - 1;
										break;
									case ARROW_RIGHT:
										nextIndex = index === totalTabs ? 1 : index + 1;
										break;
									case HOME:
										nextIndex = 1;
										break;
									case END:
										nextIndex = totalTabs;
										break;
									default:
										break;
								}

								document.getElementById( `${ id }-tab-${ nextIndex }` ).focus();
								if ( activeTab === nextIndex ) {
									return;
								}
								if ( automatic ) {
									setPrevActiveTab( activeTab );
									setActiveTab( nextIndex );
								}
							},
							role: 'tab',
							tabIndex: ! isActive ? '-1' : undefined,
						},
						customClasses: classnames( {
							'gform-tabs__tab': true,
							'gform-tabs__tab--active': isActive,
						} ),
						onClick: () => {
							if ( activeTab === index ) {
								return;
							}
							setPrevActiveTab( activeTab );
							setActiveTab( index );
						},
						label: tab.label,
						size: 'size-height-m',
						type: 'simplified',
					};

					if ( tab.icon ) {
						tabProps.icon = tab.icon;
						tabProps.iconPrefix = iconPrefix;
						tabProps.iconPosition = 'leading';
					}

					if ( tab.status ) {
						const statusProps = {
							hasDot: false,
							label: tab.status,
							size: 'sm',
							status: 'gray',
						};
						tabProps.children = <StatusIndicator { ...statusProps } />;
					}

					return <Button key={ tabId } { ...tabProps } />;
				} ) }
			</div>
		);
	};

	/**
	 * @function getTabPanels
	 * @description Get the tab panels.
	 *
	 * @since 4.7.2
	 *
	 * @return {JSX.Element} The tab panels.
	 */
	const getTabPanels = () => {
		return (
			<div className="gform-tabs__tabpanels">
				{ tabs.map( ( tab, zeroIndex ) => {
					const index = zeroIndex + 1;
					const tabId = `${ id }-tab-${ index }`;
					const tabPanelId = `${ id }-tabpanel-${ index }`;
					const isActive = activeTab === index;
					const tabPanelProps = {
						'aria-labelledby': tabId,
						className: classnames( {
							'gform-tabs__tabpanel': true,
							'gform-tabs__tabpanel--active': isActive,
							'gform-tabs__tabpanel--reveal': crossfade[ index ]?.reveal,
							'gform-tabs__tabpanel--open': crossfade[ index ]?.open,
							'gform-tabs__tabpanel--hide': crossfade[ index ]?.hide,
						} ),
						id: tabPanelId,
						role: 'tabpanel',
						tabIndex: '0',
					};

					return (
						<div key={ tabPanelId } { ...tabPanelProps }>
							{ tab.panel }
						</div>
					);
				} ) }
			</div>
		);
	};

	const componentProps = {
		className: classnames( {
			'gform-tabs': true,
			'gform-tabs--automatic': automatic,
			...spacerClasses( spacing ),
		}, customClasses ),
		...customAttributes,
	};

	return (
		<div { ...componentProps } ref={ ref }>
			{ getTablist() }
			{ getTabPanels() }
		</div>
	);
} );

/**
 * @module Tabs
 * @description A tabs component with id wrapper.
 *
 * @since 4.7.2
 *
 * @param {object}                     props                  The component props.
 * @param {boolean}                    props.automatic        Whether to automatically switch tabs using arrow keys.
 * @param {object}                     props.customAttributes Custom attributes for the component.
 * @param {string|Array|object}        props.customClasses    Custom classes for the component.
 * @param {string}                     props.iconPrefix       The icon prefix for the component.
 * @param {string|number|Array|object} props.spacing          The spacing for the component, as a string, number, array, or object.
 * @param {Array}                      props.tabs             An array of objects for the tabs content.
 *
 * @return {JSX.Element} The tabs component.
 *
 * @example
 * import Tabs from '@gravityforms/components/react/admin/modules/Tabs';
 *
 * return (
 *     <Tabs
 *         automatic={ true }
 *         tabs={ [
 *             {
 *                 label: 'Tab 1',
 *                 panel: 'Tab 1 Content',
 *             },
 *             {
 *                 icon: 'ellipsis',
 *                 label: 'Tab 2',
 *                 panel: 'Tab 2 Content',
 *             },
 *             {
 *                 label: 'Tab 3',
 *                 status: '2',
 *                 panel: 'Tab 3 Content',
 *             },
 *             {
 *                 icon: 'trash',
 *                 label: 'Tab 4',
 *                 status: '97',
 *                 panel: 'Tab 4 Content',
 *             }
 *         ] }
 *     />
 * );
 *
 */
const Tabs = forwardRef( ( props, ref ) => {
	const defaultProps = {
		automatic: true,
		customAttributes: {},
		customClasses: [],
		iconPrefix: 'gform-icon',
		spacing: '',
		tabs: [],
	};
	const combinedProps = { ...defaultProps, ...props };
	const { id: idProp } = combinedProps;
	const idProviderProps = { id: idProp };

	return (
		<IdProvider { ...idProviderProps }>
			<TabsComponent { ...combinedProps } ref={ ref } />
		</IdProvider>
	);
} );

Tabs.propTypes = {
	automatic: PropTypes.bool,
	customAttributes: PropTypes.object,
	customClasses: PropTypes.oneOfType( [
		PropTypes.string,
		PropTypes.array,
		PropTypes.object,
	] ),
	iconPrefix: PropTypes.string,
	spacing: PropTypes.oneOfType( [
		PropTypes.string,
		PropTypes.number,
		PropTypes.array,
		PropTypes.object,
	] ),
	tabs: PropTypes.array,
};

export default Tabs;
