import { useState, useEffect, useRef } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { search as apiSearch, resolveValues } from '../api';

/**
 * Multi-select with lazy, paginated search. For product / category / class it
 * queries the REST endpoint page-by-page (never loading the whole catalog). For
 * zone / method, pass `staticOptions` and it filters locally.
 */
export default function AsyncMultiSelect( { type, values = [], onChange, staticOptions = null, placeholder } ) {
	const [ open, setOpen ] = useState( false );
	const [ q, setQ ] = useState( '' );
	const [ results, setResults ] = useState( [] );
	const [ more, setMore ] = useState( false );
	const [ page, setPage ] = useState( 1 );
	const [ loading, setLoading ] = useState( false );
	const [ labels, setLabels ] = useState( {} );
	const boxRef = useRef();
	const timer = useRef();

	const mergeLabels = ( items ) =>
		setLabels( ( prev ) => {
			const m = { ...prev };
			( items || [] ).forEach( ( o ) => { m[ o.value ] = o.label; } );
			return m;
		} );

	// Resolve labels for the currently-selected values (so chips show names).
	useEffect( () => {
		if ( staticOptions ) {
			const m = {};
			staticOptions.forEach( ( o ) => { m[ o.value ] = o.label; } );
			setLabels( m );
			return;
		}
		const unknown = values.filter( ( v ) => ! ( v in labels ) );
		if ( unknown.length ) {
			resolveValues( type, unknown ).then( ( r ) => mergeLabels( r.results ) ).catch( () => {} );
		}
		// eslint-disable-next-line react-hooks/exhaustive-deps
	}, [ values.join( ',' ), type ] );

	// Debounced search when the menu is open (async types only).
	useEffect( () => {
		if ( staticOptions || ! open ) return undefined;
		clearTimeout( timer.current );
		timer.current = setTimeout( () => {
			setLoading( true );
			setPage( 1 );
			apiSearch( type, q, 1 ).then( ( r ) => {
				setResults( r.results || [] );
				setMore( !! r.more );
				mergeLabels( r.results );
				setLoading( false );
			} ).catch( () => setLoading( false ) );
		}, 300 );
		return () => clearTimeout( timer.current );
		// eslint-disable-next-line react-hooks/exhaustive-deps
	}, [ q, open, type ] );

	// Close on outside click.
	useEffect( () => {
		const h = ( e ) => { if ( boxRef.current && ! boxRef.current.contains( e.target ) ) setOpen( false ); };
		document.addEventListener( 'mousedown', h );
		return () => document.removeEventListener( 'mousedown', h );
	}, [] );

	const loadMore = () => {
		const next = page + 1;
		setLoading( true );
		apiSearch( type, q, next ).then( ( r ) => {
			setResults( ( prev ) => [ ...prev, ...( r.results || [] ) ] );
			setMore( !! r.more );
			setPage( next );
			mergeLabels( r.results );
			setLoading( false );
		} ).catch( () => setLoading( false ) );
	};

	const source = staticOptions
		? staticOptions.filter( ( o ) => o.label.toLowerCase().includes( q.toLowerCase() ) )
		: results;
	const available = source.filter( ( o ) => ! values.includes( o.value ) );

	const add = ( v ) => { if ( ! values.includes( v ) ) onChange( [ ...values, v ] ); };
	const removeV = ( v ) => onChange( values.filter( ( x ) => x !== v ) );

	return (
		<div className="edfw-ams" ref={ boxRef }>
			<div className="edfw-ams__control" onClick={ () => setOpen( true ) }>
				{ values.length === 0 && <span className="edfw-ams__ph">{ placeholder || __( 'Any', 'wpactpro-estimated-delivery-date' ) }</span> }
				{ values.map( ( v ) => (
					<span className="edfw-ams__chip" key={ v }>
						{ labels[ v ] || v }
						<button type="button" onClick={ ( e ) => { e.stopPropagation(); removeV( v ); } } aria-label={ __( 'Remove', 'wpactpro-estimated-delivery-date' ) }>✕</button>
					</span>
				) ) }
			</div>
			{ open && (
				<div className="edfw-ams__menu">
					<input
						className="edfw-ams__search"
						autoFocus
						placeholder={ __( 'Search…', 'wpactpro-estimated-delivery-date' ) }
						value={ q }
						onChange={ ( e ) => setQ( e.target.value ) }
					/>
					<div className="edfw-ams__list">
						{ available.map( ( o ) => (
							<button type="button" className="edfw-ams__opt" key={ o.value } onClick={ () => add( o.value ) }>{ o.label }</button>
						) ) }
						{ loading && <div className="edfw-ams__hint">{ __( 'Loading…', 'wpactpro-estimated-delivery-date' ) }</div> }
						{ ! loading && available.length === 0 && <div className="edfw-ams__hint">{ __( 'No matches', 'wpactpro-estimated-delivery-date' ) }</div> }
						{ ! staticOptions && more && ! loading && (
							<button type="button" className="edfw-ams__more" onClick={ loadMore }>{ __( 'Load more', 'wpactpro-estimated-delivery-date' ) }</button>
						) }
					</div>
				</div>
			) }
		</div>
	);
}
