/**
 * External dependencies.
 */
import { createHigherOrderComponent } from '@wordpress/compose';
import { InspectorControls } from '@wordpress/block-editor';
import { select, dispatch } from '@wordpress/data';
import {
	__experimentalToolsPanel as ToolsPanel,
	__experimentalToolsPanelItem as ToolsPanelItem,
} from '@wordpress/components';
import { useState } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import Select from 'react-select'

/**
 * Internal dependencies.
 */
import { isAllowedBlock } from './helpers';
import getClassOptions from './options';

/**
 * Animation Options Panel
 *
 * Provides UI controls for managing block animations, allowing users to select
 * animation types, preview animations, and modify animation settings.
 *
 * @since x.x.x
 *
 * @param {Object} props - Component props.
 * @return {Element}     - Global style options panel component.
 */
const GlobalStyleSettings = ( props ) => {
	const {
		clientId,
		name,
		attributes: {
			spectraGSClasses = [],
		},
	} = props;

	const arrayToReactSelectValue = spectraGSClasses.map( gsClass => ( {
		label: gsClass,
		value: gsClass,
	} ) );

	const [ selectedClasses, setSelectedClasses ] = useState( arrayToReactSelectValue )

	// Map block names to their default class names
	const blockDefaultClassMapping = {
		'spectra/button': 'default-button',
		'spectra/buttons': 'default-buttons',
		'spectra/container': 'default-container',
		'spectra/content': 'default-content',
		'spectra/google-map': 'default-google-map',
		'spectra/icon': 'default-icon',
		'spectra/icons': 'default-icons',
		'spectra/separator': 'default-separator',
		'spectra/list': 'default-list',
		'spectra/list-child-item': 'default-list-child-item',
		'spectra/list-child-icon': 'default-list-child-icon',
		'spectra/tabs': 'default-tabs',
		'spectra/tabs-child-tab-wrapper': 'default-tabs-child-tab-wrapper',
		'spectra/tabs-child-tab-button': 'default-tabs-child-tab-button',
		'spectra/tabs-child-tabpanel': 'default-tabs-child-tabpanel',
		'spectra/accordion': 'default-accordion',
		'spectra/accordion-child-item': 'default-accordion-child-item',
		'spectra/accordion-child-header': 'default-accordion-child-header',
		'spectra/accordion-child-details': 'default-accordion-child-details',
		'spectra/accordion-child-header-content': 'default-accordion-child-header-content',
		'spectra/accordion-child-header-icon': 'default-accordion-child-header-icon',
		'spectra/slider': 'default-slider',
		'spectra/slider-child': 'default-slider-child',
		'spectra/countdown': 'default-countdown',
		'spectra/countdown-child-day': 'default-countdown-child-day',
		'spectra/countdown-child-hour': 'default-countdown-child-hour',
		'spectra/countdown-child-minute': 'default-countdown-child-minute',
		'spectra/countdown-child-second': 'default-countdown-child-second',
		'spectra/countdown-child-number': 'default-countdown-child-number',
		'spectra/countdown-child-label': 'default-countdown-child-label',
		'spectra/countdown-child-separator': 'default-countdown-child-separator',
		'spectra/countdown-child-expiry-wrapper': 'default-countdown-child-expiry-wrapper',
		'spectra/modal': 'default-modal',
		'spectra/modal-child-trigger': 'default-modal-child-trigger',
		'spectra/modal-child-button': 'default-modal-child-button',
		'spectra/modal-child-content': 'default-modal-child-content',
		'spectra/modal-child-icon': 'default-modal-child-icon',
		'spectra/modal-popup': 'default-modal-popup',
		'spectra/modal-child-popup-close-icon': 'default-modal-child-popup-close-icon',
		'spectra/modal-popup-content': 'default-modal-popup-content',
		'spectra-pro/loop-builder': 'default-loop-builder',
		'spectra-pro/loop-builder-child-search': 'default-loop-builder-child-search',
		'spectra-pro/loop-builder-child-filter': 'default-loop-builder-child-filter',
		'spectra-pro/loop-builder-child-filter-select': 'default-loop-builder-child-filter-select',
		'spectra-pro/loop-builder-child-filter-checkbox': 'default-loop-builder-child-filter-checkbox',
		'spectra-pro/loop-builder-child-filter-button': 'default-loop-builder-child-filter-button',
		'spectra-pro/loop-builder-child-reset-all-button': 'default-loop-builder-child-reset-all-button',
		'spectra-pro/loop-builder-child-sort': 'default-loop-builder-child-sort',
		'spectra-pro/loop-builder-child-template': 'default-loop-builder-child-template',
		'spectra-pro/loop-builder-child-pagination': 'default-loop-builder-child-pagination',
		'spectra-pro/loop-builder-child-pagination-previous-button': 'default-loop-builder-child-pagination-previous-button',
		'spectra-pro/loop-builder-child-pagination-page-numbers-button': 'default-loop-builder-child-pagination-page-numbers-button',
		'spectra-pro/loop-builder-child-pagination-next-button': 'default-loop-builder-child-pagination-next-button',
		'spectra-pro/loop-builder-child-no-results': 'default-loop-builder-child-no-results',
	};

	// Get the default class for this block type
	const defaultClassForBlock = blockDefaultClassMapping[ name ] || null;

	// Get the class options
	let classOptions = getClassOptions();

	// If this block has a default class, add it to the top of the options
	if ( defaultClassForBlock ) {
		// Create the default class option
		const defaultOption = {
			label: 'Block Default',
			options: [
				{
					value: defaultClassForBlock,
					label: defaultClassForBlock,
				}
			]
		};
		
		// Prepend the default option to the class options
		classOptions = [ defaultOption, ...classOptions ];
	}

	return (
		<>
			<ToolsPanelItem
				label={ __( 'Classes', 'spectra-pro' ) }
				isShownByDefault
				hasValue={ () => !! spectraGSClasses }
			>
				<Select
					isMulti
					closeMenuOnSelect={ false }
					value={ selectedClasses }
					options={ classOptions }
					filterOption={ ( option, inputValue ) => {
						// Convert both strings to lowercase for case-insensitive matching
						const optionText = option?.label?.toLowerCase();
						const searchInput = inputValue?.toLowerCase();

						let lastIndex = -1;
						// Check each character in the search input
						for ( const char of searchInput ) {
							// Find next occurrence of char after lastIndex
							const index = optionText.indexOf( char, lastIndex + 1 );
							
							// If character isn't found or appears before previous match, return false
							if ( index === -1 || index <= lastIndex ) {
								return false;
							}
							
							// Update lastIndex for next iteration
							lastIndex = index;
						}

						return true;
					} }
					onChange={ ( value, actionMeta ) => {
						const { getBlock } = select( 'core/block-editor' );
						const { updateBlock } = dispatch( 'core/block-editor' );

						const currentBlock = getBlock( clientId );

						let currentClasses = currentBlock.attributes?.className || '';
						let requiredClasses = '';
						const requiredClassArray = [];

						value.forEach( item => {
							if ( currentClasses.includes( item.value ) ) {
								return;
							}
							requiredClasses += item.value + ' ';
							requiredClassArray.push( item.value );
						} );

						// Run the change required based on the action taken.
						switch ( actionMeta?.action ) {
							case 'remove-value':
								// If a value is removed, then remove it from the attribute and the classlist.
								const removedValue = actionMeta.removedValue.value;
								requiredClasses = requiredClasses.replace( removedValue, '' );
								const indexOfValue = requiredClassArray.indexOf( removedValue );
								if ( indexOfValue > -1 ) {
									requiredClassArray.splice( indexOfValue, 1 );
								}
								break;
							case 'select-option':
								// If a value is added, update the attribute and add it to the classlist if it doesn't already exist.
								currentClasses += ' ' + requiredClasses;
								currentClasses = currentClasses.trim();
								break;
						}

						updateBlock( clientId, {
							attributes: {
								spectraGSClasses: [ ...requiredClassArray ],
							}
						} );
						
						setSelectedClasses( value );
					} }
					styles={ {
						menu: ( base ) => ( {
							...base,
							zIndex: 9999,
						} ),
					} }
				/>
			</ToolsPanelItem>
		</>
	);
};

/**
 * Higher-order component to add animation controls to block settings.
 *
 * @since x.x.x
 *
 * @param {Function} BlockEdit - Original block edit component.
 * @return {Function} Enhanced block edit component with animation controls.
 */
const addGSControls = createHigherOrderComponent( ( BlockEdit ) => ( ( props ) => {
	if ( ! isAllowedBlock( { name: props.name } ) ) {
		return <BlockEdit { ...props } />;
	}

	const { setAttributes } = props;

	const resetAll = () => {
		setAttributes( {
			spectraGSClasses: []
		} );
	};

	return (
		<>
			<BlockEdit { ...props } />
			<InspectorControls>
				<ToolsPanel label={ __( 'Global Styles', 'spectra-pro' ) } resetAll={ resetAll }>
					<GlobalStyleSettings { ...props } />
				</ToolsPanel>
			</InspectorControls>
		</>
	);
} ), 'addGSControls' );

export default addGSControls;