import { useCallback, useEffect, useReducer, useRef } from 'react';
const initialState = {
    query: '',
    results: [],
    suggestions: [],
    groupedSuggestions: [],
    isLoading: false,
    error: null,
    selectedIndex: 0,
    mode: 'suggestions',
};
function reducer(state, action) {
    switch (action.type) {
        case 'SET_QUERY':
            return { ...state, query: action.payload, selectedIndex: 0 };
        case 'SET_RESULTS':
            return { ...state, results: action.payload, mode: 'results' };
        case 'SET_SUGGESTIONS':
            return { ...state, suggestions: action.payload, mode: 'suggestions' };
        case 'SET_GROUPED_SUGGESTIONS': {
            const flatSuggestions = action.payload.flatMap(group => group.items.map(item => ({ ...item, _source: group.source.id })));
            return { ...state, groupedSuggestions: action.payload, suggestions: flatSuggestions, mode: 'suggestions' };
        }
        case 'SET_LOADING':
            return { ...state, isLoading: action.payload };
        case 'SET_ERROR':
            return { ...state, error: action.payload };
        case 'SET_SELECTED_INDEX':
            return { ...state, selectedIndex: action.payload };
        case 'SELECT_NEXT': {
            const items = state.mode === 'results' ? state.results : state.suggestions;
            const maxIndex = items.length - 1;
            return { ...state, selectedIndex: state.selectedIndex >= maxIndex ? 0 : state.selectedIndex + 1 };
        }
        case 'SELECT_PREV': {
            const items = state.mode === 'results' ? state.results : state.suggestions;
            const maxIndex = items.length - 1;
            return { ...state, selectedIndex: state.selectedIndex <= 0 ? maxIndex : state.selectedIndex - 1 };
        }
        case 'SET_MODE':
            return { ...state, mode: action.payload, selectedIndex: 0 };
        case 'RESET':
            return initialState;
        default:
            return state;
    }
}
export function useDocSearch(options) {
    const { apiEndpoint, apiKey, sources, maxResults = 10, debounceMs = 200, processGroupedResults } = options;
    const searchSources = sources || (apiEndpoint ? [{
            id: 'default',
            name: 'Results',
            endpoint: apiEndpoint,
            apiKey,
            maxResults,
        }] : []);
    const [state, dispatch] = useReducer(reducer, initialState);
    const abortControllersRef = useRef(new Map());
    const debounceTimerRef = useRef(null);
    const defaultTransform = (data, sourceId) => {
        const items = data.data?.suggestions || data.data?.results || data.data?.hits || data.suggestions || data.results || data.hits || [];
        return items.map((item) => {
            const doc = item.document || item;
            // Build hierarchy from nested object or flat dot-notation keys
            const hierarchy = doc.hierarchy || {
                lvl0: doc['hierarchy.lvl0'],
                lvl1: doc['hierarchy.lvl1'],
                lvl2: doc['hierarchy.lvl2'],
                lvl3: doc['hierarchy.lvl3'],
                lvl4: doc['hierarchy.lvl4'],
                lvl5: doc['hierarchy.lvl5'],
            };
            return {
                url: doc.url || doc.route || item.url || '',
                title: (doc.title || '').replace?.(/<\/?mark>/g, '') || '',
                content: (doc.content || doc.description || '').replace?.(/<\/?mark>/g, '')?.substring?.(0, 100) || '',
                description: doc.description || doc.content?.substring?.(0, 100) || '',
                category: doc.category || hierarchy?.lvl0 || '',
                hierarchy,
                route: doc.route,
                parentTitle: doc.parent_title || doc.parentTitle,
                anchor: doc.anchor || item.anchor,
                _source: sourceId,
            };
        });
    };
    const transformPublicSearchResults = useCallback((data, sourceId) => {
        const results = data?.data?.results || [];
        return results.map((item) => {
            const doc = item.document || item;
            return {
                url: doc.url || doc.route || '',
                title: (doc.title || doc.name || '').replace?.(/<\/?mark>/g, '') || '',
                content: (doc.content || doc.description || '').replace?.(/<\/?mark>/g, '')?.substring?.(0, 100) || '',
                description: doc.description || doc.content?.substring?.(0, 100) || '',
                category: doc.category || doc.hierarchy?.lvl0 || '',
                hierarchy: doc.hierarchy,
                route: doc.route,
                parentTitle: doc.parent_title || doc.parentTitle,
                _source: sourceId,
            };
        });
    }, []);
    const fetchFromSource = useCallback(async (source, query, signal) => {
        const minLength = source.minQueryLength ?? 1;
        if (query.length < minLength)
            return [];
        try {
            if (source.storeId) {
                const baseUrl = source.endpoint.replace(/\/$/, '');
                const searchUrl = `${baseUrl}/api/v1/search`;
                const headers = {
                    'Content-Type': 'application/json',
                    'x-storeid': source.storeId,
                    ...(source.storeSecret && { 'x-storesecret': source.storeSecret }),
                };
                const response = await fetch(searchUrl, {
                    method: 'POST',
                    headers,
                    body: JSON.stringify({ q: query.trim() || '*', per_page: source.maxResults || 8 }),
                    signal,
                });
                if (!response.ok)
                    return [];
                const data = await response.json();
                if (source.transformResults) {
                    return source.transformResults(data).map((item) => ({ ...item, _source: source.id }));
                }
                return transformPublicSearchResults(data, source.id);
            }
            const url = new URL(source.endpoint);
            url.searchParams.set('query', query);
            url.searchParams.set('limit', String(source.maxResults || 8));
            const headers = { 'Content-Type': 'application/json' };
            if (source.apiKey)
                headers['X-Docs-API-Key'] = source.apiKey;
            const response = await fetch(url.toString(), { method: 'GET', headers, signal });
            if (!response.ok)
                return [];
            const data = await response.json();
            if (source.transformResults) {
                return source.transformResults(data).map((item) => ({ ...item, _source: source.id }));
            }
            return defaultTransform(data, source.id);
        }
        catch (error) {
            if (error instanceof Error && error.name === 'AbortError')
                throw error;
            return [];
        }
    }, [transformPublicSearchResults]);
    const fetchSuggestions = useCallback(async (query) => {
        if (!query.trim()) {
            dispatch({ type: 'SET_GROUPED_SUGGESTIONS', payload: [] });
            return;
        }
        abortControllersRef.current.forEach(c => c.abort());
        abortControllersRef.current.clear();
        dispatch({ type: 'SET_LOADING', payload: true });
        dispatch({ type: 'SET_ERROR', payload: null });
        try {
            const results = await Promise.all(searchSources.map(async (source) => {
                const controller = new AbortController();
                abortControllersRef.current.set(source.id, controller);
                let items = await fetchFromSource(source, query, controller.signal);
                if (processGroupedResults) {
                    items = processGroupedResults(source.id, items);
                }
                return { source, items };
            }));
            const groupedResults = results.filter(r => r.items.length > 0);
            dispatch({ type: 'SET_GROUPED_SUGGESTIONS', payload: groupedResults });
        }
        catch (error) {
            if (error instanceof Error && error.name === 'AbortError')
                return;
            dispatch({ type: 'SET_ERROR', payload: error instanceof Error ? error.message : 'Search failed' });
        }
        finally {
            dispatch({ type: 'SET_LOADING', payload: false });
        }
    }, [searchSources, fetchFromSource, processGroupedResults]);
    const search = useCallback((q) => fetchSuggestions(q), [fetchSuggestions]);
    const setQuery = useCallback((query) => {
        dispatch({ type: 'SET_QUERY', payload: query });
        if (debounceTimerRef.current)
            clearTimeout(debounceTimerRef.current);
        debounceTimerRef.current = setTimeout(() => fetchSuggestions(query), debounceMs);
    }, [fetchSuggestions, debounceMs]);
    const selectNext = useCallback(() => dispatch({ type: 'SELECT_NEXT' }), []);
    const selectPrev = useCallback(() => dispatch({ type: 'SELECT_PREV' }), []);
    const setSelectedIndex = useCallback((index) => dispatch({ type: 'SET_SELECTED_INDEX', payload: index }), []);
    const reset = useCallback(() => {
        abortControllersRef.current.forEach(c => c.abort());
        abortControllersRef.current.clear();
        if (debounceTimerRef.current)
            clearTimeout(debounceTimerRef.current);
        dispatch({ type: 'RESET' });
    }, []);
    const getSelectedItem = useCallback(() => {
        const items = state.mode === 'results' ? state.results : state.suggestions;
        return items[state.selectedIndex] || null;
    }, [state.mode, state.results, state.suggestions, state.selectedIndex]);
    useEffect(() => {
        return () => {
            abortControllersRef.current.forEach(c => c.abort());
            abortControllersRef.current.clear();
            if (debounceTimerRef.current)
                clearTimeout(debounceTimerRef.current);
        };
    }, []);
    return {
        ...state,
        sources: searchSources,
        setQuery,
        search,
        fetchSuggestions,
        selectNext,
        selectPrev,
        setSelectedIndex,
        reset,
        getSelectedItem,
    };
}
