import React, { useEffect, useId, useMemo, useRef, useState } from 'react';
import { __ } from '@wordpress/i18n';
import Icon from '../icons/Icon';
import { useEscape, useOnClickOutside } from './hooks';

/**
 * Searchable single-select (ARIA combobox + listbox).
 *
 * @param {Array<{value:*, label:string, description?:string, disabled?:boolean}>} options
 * @param {*}        value
 * @param {Function} onChange   (value) => void
 * @param {string}   [placeholder]
 * @param {string}   [searchPlaceholder]
 * @param {boolean}  [clearable]
 */
export default function Combobox({
	options = [],
	value,
	onChange,
	placeholder = __('Select…', 'pointly-booking'),
	searchPlaceholder = __('Search…', 'pointly-booking'),
	emptyText = __('No results', 'pointly-booking'),
	clearable = false,
	disabled = false,
	id,
	className = '',
	'aria-required': _ariaRequired, // not allowed on a button; the visible * and label carry it
	...aria
}) {
	const autoId = useId().replace(/:/g, '');
	const listId = `bp-lb-${autoId}`;
	const rootRef = useRef(null);
	const controlRef = useRef(null);
	const searchRef = useRef(null);
	const [open, setOpen] = useState(false);
	const [query, setQuery] = useState('');
	const [active, setActive] = useState(0);

	const selected = options.find((o) => String(o.value) === String(value));
	const filtered = useMemo(() => {
		const q = query.trim().toLowerCase();
		if (!q) return options;
		return options.filter((o) => `${o.label} ${o.description || ''}`.toLowerCase().includes(q));
	}, [options, query]);

	useEffect(() => {
		if (!open) return;
		setQuery('');
		const idx = Math.max(0, options.findIndex((o) => String(o.value) === String(value)));
		setActive(idx);
		window.requestAnimationFrame(() => searchRef.current && searchRef.current.focus());
		// eslint-disable-next-line react-hooks/exhaustive-deps
	}, [open]);

	useEffect(() => {
		if (active >= filtered.length) setActive(Math.max(0, filtered.length - 1));
	}, [filtered.length, active]);

	const close = (focusControl = true) => {
		setOpen(false);
		if (focusControl && controlRef.current) controlRef.current.focus();
	};
	useEscape(() => close(), open);
	useOnClickOutside(rootRef, () => setOpen(false), open);

	const choose = (opt) => {
		if (!opt || opt.disabled) return;
		if (onChange) onChange(opt.value);
		close();
	};

	const onSearchKey = (e) => {
		if (e.key === 'ArrowDown') {
			e.preventDefault();
			setActive((i) => Math.min(filtered.length - 1, i + 1));
		} else if (e.key === 'ArrowUp') {
			e.preventDefault();
			setActive((i) => Math.max(0, i - 1));
		} else if (e.key === 'Home') {
			e.preventDefault();
			setActive(0);
		} else if (e.key === 'End') {
			e.preventDefault();
			setActive(filtered.length - 1);
		} else if (e.key === 'Enter') {
			e.preventDefault();
			choose(filtered[active]);
		} else if (e.key === 'Tab') {
			setOpen(false);
		}
	};

	useEffect(() => {
		if (!open) return;
		const el = document.getElementById(`${listId}-opt-${active}`);
		if (el && el.scrollIntoView) el.scrollIntoView({ block: 'nearest' });
	}, [active, open, listId]);

	return (
		<div ref={rootRef} className={['bp-combobox', open ? 'is-open' : '', className].filter(Boolean).join(' ')}>
			<button
				ref={controlRef}
				id={id}
				type="button"
				className="bp-combobox__control"
				aria-haspopup="listbox"
				aria-expanded={open ? 'true' : 'false'}
				aria-controls={open ? listId : undefined}
				disabled={disabled}
				onClick={() => setOpen((o) => !o)}
				onKeyDown={(e) => {
					if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
						e.preventDefault();
						setOpen(true);
					}
				}}
				{...aria}
			>
				<span className={['bp-combobox__value', selected ? '' : 'bp-combobox__placeholder'].join(' ')}>
					{selected ? selected.label : placeholder}
				</span>
				{clearable && selected ? (
					<span
						role="button"
						tabIndex={-1}
						className="bp-combobox__chevron"
						aria-label={__('Clear selection', 'pointly-booking')}
						onClick={(e) => {
							e.stopPropagation();
							if (onChange) onChange(null);
						}}
					>
						<Icon name="x" />
					</span>
				) : (
					<Icon name="chevrons-up-down" className="bp-combobox__chevron" />
				)}
			</button>

			{open ? (
				<div className="bp-popover">
					<div className="bp-combobox__search">
						<input
							ref={searchRef}
							type="text"
							className="bp-input bp-input--sm"
							role="combobox"
							aria-expanded="true"
							aria-controls={listId}
							aria-autocomplete="list"
							aria-activedescendant={filtered[active] ? `${listId}-opt-${active}` : undefined}
							aria-label={searchPlaceholder}
							placeholder={searchPlaceholder}
							value={query}
							onChange={(e) => {
								setQuery(e.target.value);
								setActive(0);
							}}
							onKeyDown={onSearchKey}
						/>
					</div>
					<ul id={listId} className="bp-listbox" role="listbox">
						{filtered.map((o, i) => {
							const isSel = String(o.value) === String(value);
							return (
								<li
									key={String(o.value)}
									id={`${listId}-opt-${i}`}
									role="option"
									aria-selected={isSel ? 'true' : 'false'}
									aria-disabled={o.disabled ? 'true' : undefined}
									className={['bp-option', i === active ? 'is-active' : ''].filter(Boolean).join(' ')}
									onMouseEnter={() => setActive(i)}
									onMouseDown={(e) => e.preventDefault()}
									onClick={() => choose(o)}
								>
									<span>
										{o.label}
										{o.description ? <span className="bp-check__desc">{o.description}</span> : null}
									</span>
									{isSel ? <Icon name="check" className="bp-option__check" /> : null}
								</li>
							);
						})}
						{!filtered.length ? <li className="bp-listbox__empty">{emptyText}</li> : null}
					</ul>
				</div>
			) : null}
		</div>
	);
}
