"use client";
import { useEffect, useMemo, useRef, useState } from "react";
import { toError } from "../internal/toError.js";
import { loadEntryFiles } from "./workspaceListingCache.js";
import { matchWorkspaceFiles } from "./matchWorkspaceFiles.js";
const EMPTY_GROUPS = [];
/**
 * Behavior hook powering workspace-wide filename/path search.
 *
 * Loads each entry's listing through the shared
 * {@link loadEntryFiles} cache (warm from
 * Files browsing → usually free), then ranks matches per entry with the pure
 * {@link matchWorkspaceFiles}. Fetching is keyed by the set of entry ids, so a
 * query change re-ranks the already-loaded listings without re-fetching. Errors
 * are isolated per entry (one failing repo never blanks the rest), and a stale
 * generation's late resolutions are ignored.
 *
 * Meant to be consumed by a component mounted only while Search is active
 * (`WorkspaceFileSearch`), so the Files view incurs no search fetching.
 *
 * @example
 * ```tsx
 * const { groups, totalMatches, isLoading } = useWorkspaceFileSearch({
 *   entries: workspace.entries,
 *   lister: workspaceFileLister,
 *   query,
 * });
 * ```
 */
export function useWorkspaceFileSearch({ entries, lister, query, }) {
    const [states, setStates] = useState({});
    // Latest entries read inside the effect without widening its dependency to the
    // (possibly per-render) array identity — the effect keys on the id set instead.
    const entriesRef = useRef(entries);
    entriesRef.current = entries;
    const entriesKey = entries.map((entry) => entry.id).join("\u0000");
    // Ignore resolutions from a superseded entry set (id set or lister changed).
    const generationRef = useRef(0);
    useEffect(() => {
        if (!lister) {
            setStates({});
            return;
        }
        const currentEntries = entriesRef.current;
        const generation = ++generationRef.current;
        // Seed loading for not-yet-known entries; drop states for removed ones.
        setStates((prev) => {
            const next = {};
            for (const entry of currentEntries) {
                next[entry.id] = prev[entry.id] ?? { status: "loading" };
            }
            return next;
        });
        for (const entry of currentEntries) {
            loadEntryFiles(entry, lister)
                .then((listing) => {
                if (generationRef.current !== generation)
                    return;
                setStates((prev) => ({
                    ...prev,
                    [entry.id]: listing === null
                        ? { status: "unsupported" }
                        : { status: "ready", listing },
                }));
            })
                .catch((err) => {
                if (generationRef.current !== generation)
                    return;
                setStates((prev) => ({
                    ...prev,
                    [entry.id]: { status: "error", error: toError(err) },
                }));
            });
        }
    }, [entriesKey, lister]);
    return useMemo(() => {
        if (!lister) {
            return {
                groups: EMPTY_GROUPS,
                totalMatches: 0,
                isLoading: false,
                isUnsupported: true,
                error: null,
            };
        }
        const groups = [];
        let totalMatches = 0;
        let isLoading = false;
        let firstError = null;
        let unsupportedCount = 0;
        for (const entry of entries) {
            const state = states[entry.id];
            if (!state || state.status === "loading") {
                isLoading = true;
                continue;
            }
            if (state.status === "unsupported") {
                unsupportedCount += 1;
                continue;
            }
            const error = state.status === "error" ? state.error ?? null : null;
            if (error && !firstError)
                firstError = error;
            const truncated = state.listing?.truncated ?? false;
            const matches = state.listing
                ? matchWorkspaceFiles(state.listing.files, query)
                : [];
            totalMatches += matches.length;
            // A group is worth showing only when it carries matches, an error, or a
            // truncation notice — otherwise it is silent.
            if (matches.length > 0 || error || truncated) {
                groups.push({ entry, matches, truncated, error });
            }
        }
        const isUnsupported = entries.length > 0 && unsupportedCount === entries.length;
        return { groups, totalMatches, isLoading, isUnsupported, error: firstError };
    }, [entries, states, query, lister]);
}
//# sourceMappingURL=useWorkspaceFileSearch.js.map