import { useState, useEffect, useCallback, useRef } from "react";
import { fetchIndicatorsStream } from "../services/indicatorService";
/**
 * Normalize item ID to lowercase without braces for consistent matching.
 * Backend returns IDs like {GUID} but frontend uses lowercase without braces.
 */
export function normalizeItemId(id) {
    return id.replace(/[{}]/g, "").toLowerCase();
}
/**
 * Hook for fetching and streaming indicator data with caching support.
 *
 * Streams indicators from the backend as they become available,
 * updating the UI progressively. Supports incremental fetching
 * where only new node IDs are fetched and merged with existing data.
 */
export function useNodeIndicators(options) {
    const { nodeIds, visibleProviders, language, version = 1, enabled = true, existingIndicators = {}, onFetchComplete, sessionId, } = options;
    const [indicators, setIndicators] = useState(existingIndicators);
    const [providers, setProviders] = useState([]);
    const [isLoading, setIsLoading] = useState(false);
    const [error, setError] = useState(null);
    // Use refs to track the current request for cancellation
    const abortControllerRef = useRef(null);
    // Track node IDs being fetched in current request
    const fetchingNodeIdsRef = useRef([]);
    // Track previous language and version to detect changes
    const prevLanguageRef = useRef(language);
    const prevVersionRef = useRef(version);
    // When language or version changes, clear internal state to prevent stale data
    useEffect(() => {
        const languageChanged = prevLanguageRef.current !== language;
        const versionChanged = prevVersionRef.current !== version;
        if (languageChanged || versionChanged) {
            // Abort any in-flight request
            abortControllerRef.current?.abort();
            // Clear internal state
            setIndicators({});
            setError(null);
            prevLanguageRef.current = language;
            prevVersionRef.current = version;
        }
    }, [language, version]);
    const fetchIndicatorsForIds = useCallback(async (idsToFetch, clearExisting = false) => {
        if (!enabled || idsToFetch.length === 0) {
            setIsLoading(false);
            return;
        }
        // Cancel any in-flight request
        abortControllerRef.current?.abort();
        const controller = new AbortController();
        abortControllerRef.current = controller;
        fetchingNodeIdsRef.current = idsToFetch;
        setIsLoading(true);
        setError(null);
        // If clearExisting, reset indicators; otherwise preserve existing
        if (clearExisting) {
            setIndicators({});
        }
        try {
            await fetchIndicatorsStream(idsToFetch, visibleProviders.length > 0 ? visibleProviders : undefined, language, (chunk) => {
                if (controller.signal.aborted)
                    return;
                switch (chunk.type) {
                    case "providers":
                        setProviders(chunk.data);
                        break;
                    case "indicator":
                        // Normalize the item ID to match frontend format
                        const normalizedId = normalizeItemId(chunk.itemId);
                        // Merge single item result immediately as it arrives
                        setIndicators((prev) => ({
                            ...prev,
                            [normalizedId]: {
                                ...prev[normalizedId],
                                [chunk.providerId]: chunk.data,
                            },
                        }));
                        break;
                    case "complete":
                        setIsLoading(false);
                        // Notify caller which IDs were fetched
                        onFetchComplete?.(fetchingNodeIdsRef.current);
                        break;
                    case "error":
                        setError(chunk.message);
                        setIsLoading(false);
                        break;
                }
            }, controller.signal, sessionId);
        }
        catch (err) {
            if (!controller.signal.aborted) {
                const message = err instanceof Error ? err.message : "Failed to fetch indicators";
                setError(message);
                setIsLoading(false);
            }
        }
    }, [visibleProviders, language, enabled, onFetchComplete, sessionId]);
    // Fetch when nodeIds change (incremental - only fetches new IDs)
    useEffect(() => {
        if (nodeIds.length > 0) {
            fetchIndicatorsForIds(nodeIds, false);
        }
        return () => {
            abortControllerRef.current?.abort();
        };
    }, [nodeIds, fetchIndicatorsForIds]);
    // Refresh specific IDs or all visible IDs
    const refresh = useCallback((nodeIdsToRefresh) => {
        const idsToFetch = nodeIdsToRefresh ?? nodeIds;
        if (idsToFetch.length > 0) {
            // Remove refreshed IDs from indicators first, then refetch
            if (nodeIdsToRefresh) {
                setIndicators((prev) => {
                    const next = { ...prev };
                    for (const id of nodeIdsToRefresh) {
                        const normalizedId = normalizeItemId(id);
                        delete next[normalizedId];
                    }
                    return next;
                });
            }
            fetchIndicatorsForIds(idsToFetch, !nodeIdsToRefresh);
        }
    }, [nodeIds, fetchIndicatorsForIds]);
    // Clear all cached indicators
    const clearCache = useCallback(() => {
        setIndicators({});
    }, []);
    return {
        indicators,
        providers,
        isLoading,
        error,
        refresh,
        clearCache,
    };
}
/**
 * Get a single indicator for a specific item and provider.
 */
export function getIndicator(indicators, itemId, providerId) {
    return indicators[itemId]?.[providerId];
}
//# sourceMappingURL=useNodeIndicators.js.map