import { CheckIcon, MagnifyingGlassIcon } from '@heroicons/react/24/outline';
import React, { useEffect, useRef, useState } from 'react';
import PHXUseDebounce from '../Func/useDebounce';
import ErrorMessage from '../../commons/ErrorMessage';
import { PHXClientQueryV3 } from '../Func/GRPC/PHXGrpcClientV3';
import { requestMaxDuration } from '../../utils/constants';
import { unaccentValue } from '../../helpers/helpers';
import EmptySearchResult from './EmptySearchResult';
export function classNames(...classes) {
    return classes.filter(Boolean).join(' ');
}
const PHXSearchResultListV3 = ({ defaultInputText = '', disabled, error, errorMessageCustom, errorType, filterArray, getSearchResult, handleCustomRender, handleQueryValue, isArray, isFillInput, label, listSelected, name, placeholder, queryValueType = 'default', register, search, setValue, isUnaccentSearchValue = false, isClearAfterSelect = false, }) => {
    const [searchValue, setSearchValue] = useState(defaultInputText);
    const [isSearchLoading, setIsSearchLoading] = useState(false);
    const [isSearching, setIsSearching] = useState(false);
    const [searchDataResult, setSearchDataResult] = useState([]);
    // Refs for improved control
    const searchIdRef = useRef(0);
    const lastFetchedQueryRef = useRef('');
    const lastFetchedAtRef = useRef(0);
    const lastUserEditAtRef = useRef(0);
    const filterQuery = (query, value) => {
        const formatValue = isUnaccentSearchValue ? unaccentValue(value) : value;
        return query.replace(/%@value%/g, `""%${formatValue.trim()}%""`);
    };
    // fetchSearchData now accepts the query string to avoid closure issues
    const fetchSearchData = async (value) => {
        try {
            const searchQuery = search ? filterQuery(search.query, value) : '';
            const res = await PHXClientQueryV3({
                query: searchQuery,
            });
            return res;
        }
        catch (e) {
            console.log(e);
            return null;
        }
    };
    const getData = async (option) => {
        try {
            const startTime = performance.now();
            const result = await option();
            if (!result)
                return null;
            const data = result.data;
            const endTime = performance.now();
            const duration = endTime - startTime;
            if (duration < requestMaxDuration) {
                await new Promise((r) => setTimeout(r, 300));
            }
            return data;
        }
        catch (e) {
            console.error(e);
            return null;
        }
    };
    const searchChange = (e) => {
        const value = e.target.value.replace(/\\/g, '');
        setSearchValue(value);
        setIsSearching(value !== '');
        lastUserEditAtRef.current = Date.now(); // mark user edit time
    };
    const debouncedSearch = PHXUseDebounce(searchValue, 300);
    // keep formatDynamicFields & message & handleClickSearchItem as-is
    const formatDynamicFields = (dataParams) => {
        const formattedValues = [];
        const searchData = {};
        filterArray.forEach((item) => {
            if (item.keyParent) {
                const parentData = dataParams[item.keyParent] || {};
                searchData[item.keyChild] =
                    (parentData.length > 0 ? parentData[0][item.keyChild] : parentData[item.keyChild]) || '';
            }
            else {
                searchData[item.keyChild] = dataParams[item.keyChild];
            }
        });
        for (const field in searchData) {
            if (searchData[field] !== null && searchData[field] !== undefined) {
                formattedValues.push(`${searchData[field]}`
                    .toLowerCase()
                    .split(' ')
                    .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
                    .join(' '));
            }
        }
        return formattedValues.join(' - ');
    };
    const handleClickSearchItem = (item) => {
        getSearchResult(item);
        setIsSearching(false);
        const formatted = formatDynamicFields(item);
        if (isArray || isClearAfterSelect) {
            setSearchValue('');
            if (isClearAfterSelect) {
                setValue(name, '');
            }
        }
        else if (isFillInput && handleCustomRender) {
            setSearchValue(handleCustomRender(item));
        }
        else {
            setSearchValue(formatted);
        }
    };
    const message = (type) => {
        let errorMessage = '';
        switch (type) {
            case 'required-field':
                errorMessage = 'Vui lòng nhập thông tin';
                break;
            case 'validate-input-field':
                errorMessage = 'Vui lòng nhập thông tin hợp lệ (độ dài 1 - 60 kí tự)';
                break;
            case 'duplicate-field':
                errorMessage = 'Thông tin đã tồn tại';
                break;
            case 'validate-phone-number':
                errorMessage = 'Số điện thoại chưa hợp lệ';
                break;
            case 'validate-input-email':
                errorMessage = 'Định dạng email chưa đúng, vui lòng nhập lại';
                break;
            case 'custom-message':
                errorMessage = errorMessageCustom;
                break;
            default:
                break;
        }
        return errorMessage;
    };
    // Improved search function that accepts the debounced query
    const handleSearch = async (query) => {
        const currentId = ++searchIdRef.current;
        setIsSearchLoading(true);
        const data = await getData(() => fetchSearchData(query));
        // if fetch failed or was null
        if (!data) {
            // only clear loading if still the la request
            if (currentId === searchIdRef.current)
                setIsSearchLoading(false);
            return;
        }
        // If another search has started meanwhile, drop this result
        if (currentId !== searchIdRef.current) {
            return;
        }
        const dataTable = search.keyResult;
        const result = queryValueType === 'default' ? data[dataTable] : handleQueryValue(data[dataTable]);
        let finalData = result;
        if (listSelected && listSelected.length > 0) {
            finalData = result.map((item) => {
                let isSelected;
                switch (name) {
                    case 'user_assigned_tuition':
                        isSelected = listSelected.some((selectedItem) => selectedItem.profile_student.user_code === item.profile_student.user_code);
                        break;
                    default:
                        isSelected = listSelected.some((selectedItem) => selectedItem.id === item.id);
                        break;
                }
                return isSelected ? { ...item, is_selected: true } : item;
            });
        }
        setSearchDataResult(finalData);
        lastFetchedQueryRef.current = query;
        lastFetchedAtRef.current = Date.now();
        // only clear loading if this is still the latest request
        if (currentId === searchIdRef.current)
            setIsSearchLoading(false);
    };
    // NOTE: removed the old effect that set isSearchLoading(true) on every searchValue change.
    // We now rely on the debounced effect to start/stop loading.
    useEffect(() => {
        // If debouncedSearch is empty -> clear results and stop loading
        if (!debouncedSearch || debouncedSearch.trim() === '') {
            setSearchDataResult([]);
            setIsSearchLoading(false);
            return;
        }
        // If we already fetched this exact query and user hasn't edited since last fetch, skip fetching
        const userEditedAfterLastFetch = lastUserEditAtRef.current > lastFetchedAtRef.current;
        const needFetch = debouncedSearch !== lastFetchedQueryRef.current || userEditedAfterLastFetch;
        if (!needFetch) {
            // nothing to do
            return;
        }
        // run search for this debounced value
        handleSearch(debouncedSearch);
        setValue(name, debouncedSearch);
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [debouncedSearch]);
    return (React.createElement("div", { className: 'relative w-full' },
        React.createElement("div", { className: 'relative' },
            label && React.createElement("label", { className: 'mb-1 block text-xs font-normal text-gray-700' }, label),
            React.createElement("div", { className: 'flex items-center' },
                React.createElement("div", { className: 'relative w-full' },
                    React.createElement("span", { className: 'absolute inset-y-0 left-0 flex items-center pl-3 text-gray-500' },
                        React.createElement(MagnifyingGlassIcon, { className: 'h-4 w-4' })),
                    React.createElement("input", { autoComplete: 'off', className: classNames('block w-full rounded-lg border-[0.5px] border-gray-500 px-3 py-1.5 pl-9 pr-3 text-xs font-normal shadow-sm  focus:border-gray-500 focus:outline-none focus:outline-offset-1 focus:outline-indigo-500 focus:ring-transparent', error ? 'border-red-800 bg-red-50 hover:bg-red-50 focus:border-red-800 focus:bg-red-50' : '', disabled ? 'bg-neutral-200 border-none cursor-not-allowed' : ''), name: name, onInput: searchChange, placeholder: placeholder, value: isArray || isFillInput ? searchValue : undefined, disabled: disabled, ...register, type: 'text' })))),
        error && errorType ? React.createElement(ErrorMessage, { message: message(errorType) }) : null,
        React.createElement("div", { className: 'absolute z-50 w-full drop-shadow-xl' }, isSearching && (searchDataResult === null || searchDataResult === void 0 ? void 0 : searchDataResult.length) > 0 ? (React.createElement("div", { className: 'my-2 min-w-full pb-2' },
            React.createElement("div", { className: 'max-h-[300px] divide-gray-200  overflow-y-auto rounded-xl border border-gray-100 bg-white px-2 py-1' }, searchDataResult.map((item) => (
            // @ts-ignore
            React.createElement("button", { key: item.id, "aria-expanded": 'false', "aria-haspopup": 'listbox', "aria-labelledby": 'headlessui-combobox-label-:rk: headlessui-combobox-button-:rm:', className: `${item.is_selected ? 'bg-gray-200 font-medium text-gray-900' : 'hover:bg-gray-100'} inset-y-0 right-0 my-1 mt-1 flex w-full items-center rounded-md px-2 py-[6px] pr-4 font-sans text-xs text-gray-600 transition-colors  focus:outline-none`, "data-headlessui-state": '', disabled: item.is_selected, onClick: () => handleClickSearchItem(item), tabIndex: -1, type: 'button' },
                React.createElement("div", { className: 'flex w-full justify-between' },
                    handleCustomRender ? React.createElement("p", null, handleCustomRender(item)) : React.createElement("p", null, formatDynamicFields(item)),
                    item.is_selected ? React.createElement(CheckIcon, { className: 'h-4 w-4' }) : null))))))) : (React.createElement(React.Fragment, null, isSearching && React.createElement(EmptySearchResult, { isSearchLoading: isSearchLoading }))))));
};
export default PHXSearchResultListV3;
//# sourceMappingURL=SearchResultList.js.map