"use client";
import { useCallback, useEffect, useReducer, useRef } from "react";
import { create } from "@bufbuild/protobuf";
import { RunnerSendCommandInputSchema, ListDirectoryRequestSchema, } from "@stigmer/protos/ai/stigmer/agentic/runner/v1/io_pb";
import { useStigmer } from "../hooks";
import { toError } from "../internal/toError";
const CACHE_MAX_AGE_MS = 30_000;
function reducer(state, action) {
    switch (action.type) {
        case "NAVIGATE":
            return {
                ...state,
                requestedPath: action.path,
                isLoading: true,
                error: null,
            };
        case "SUCCESS": {
            const nextCache = new Map(state.cache);
            nextCache.set(action.resolvedPath, {
                entries: action.entries,
                fetchedAt: Date.now(),
            });
            return {
                ...state,
                currentPath: action.resolvedPath,
                entries: action.entries,
                homeDirectory: action.homeDirectory || state.homeDirectory,
                currentDirectory: action.currentDirectory || state.currentDirectory,
                isLoading: false,
                error: null,
                cache: nextCache,
            };
        }
        case "CACHE_HIT":
            return {
                ...state,
                currentPath: action.resolvedPath,
                entries: action.entries,
                isLoading: false,
                error: null,
                requestedPath: action.resolvedPath,
            };
        case "FAILURE":
            return { ...state, isLoading: false, error: action.error };
        case "TOGGLE_HIDDEN":
            return { ...state, showHidden: !state.showHidden };
    }
}
const INITIAL_STATE = {
    currentPath: "",
    entries: [],
    homeDirectory: "",
    currentDirectory: "",
    showHidden: false,
    isLoading: false,
    error: null,
    requestedPath: "",
    cache: new Map(),
};
// ---------------------------------------------------------------------------
// Path utilities
// ---------------------------------------------------------------------------
function buildSegments(path) {
    if (!path)
        return [];
    const segments = [{ name: "/", path: "/" }];
    const parts = path.split("/").filter(Boolean);
    let accumulated = "";
    for (const part of parts) {
        accumulated += `/${part}`;
        segments.push({ name: part, path: accumulated });
    }
    return segments;
}
function parentPath(path) {
    if (!path || path === "/")
        return "/";
    const idx = path.lastIndexOf("/");
    return idx <= 0 ? "/" : path.slice(0, idx);
}
// ---------------------------------------------------------------------------
// Hook
// ---------------------------------------------------------------------------
/**
 * Behavior hook that drives a filesystem browser against a connected runner.
 *
 * Sends `ListDirectory` commands via the runner's bidi stream (through the
 * `sendCommand` unary RPC) and manages navigation state, breadcrumbs,
 * loading/error handling, and hidden file filtering.
 *
 * Designed for composition with {@link RunnerFileBrowser} but usable
 * standalone by platform builders who want custom rendering.
 *
 * @param runnerId - ID of the runner to browse. When `null`, the hook
 *   is inert (no requests are made, entries are empty).
 *
 * @example
 * ```tsx
 * function MyFilePicker({ runnerId }: { runnerId: string }) {
 *   const browser = useRunnerFileBrowser(runnerId);
 *
 *   return (
 *     <div>
 *       <p>Path: {browser.currentPath}</p>
 *       {browser.entries
 *         .filter(e => e.isDirectory)
 *         .map(e => (
 *           <button key={e.name} onClick={() => browser.navigateTo(e.name)}>
 *             {e.name}
 *           </button>
 *         ))}
 *     </div>
 *   );
 * }
 * ```
 */
export function useRunnerFileBrowser(runnerId) {
    const stigmer = useStigmer();
    const [state, dispatch] = useReducer(reducer, INITIAL_STATE);
    const requestIdRef = useRef(0);
    const cacheRef = useRef(state.cache);
    cacheRef.current = state.cache;
    const fetchDirectory = useCallback(async (path) => {
        if (!runnerId)
            return;
        const cached = cacheRef.current.get(path);
        if (cached && Date.now() - cached.fetchedAt < CACHE_MAX_AGE_MS) {
            dispatch({ type: "CACHE_HIT", resolvedPath: path, entries: cached.entries });
            return;
        }
        const id = ++requestIdRef.current;
        dispatch({ type: "NAVIGATE", path });
        try {
            const response = await stigmer.runner.sendCommand(create(RunnerSendCommandInputSchema, {
                runnerId,
                command: {
                    case: "listDirectory",
                    value: create(ListDirectoryRequestSchema, { path }),
                },
            }));
            if (id !== requestIdRef.current)
                return;
            if (response.result.case === "error") {
                dispatch({
                    type: "FAILURE",
                    error: new Error(response.result.value.message),
                });
                return;
            }
            if (response.result.case === "listDirectory") {
                const listing = response.result.value;
                dispatch({
                    type: "SUCCESS",
                    resolvedPath: listing.resolvedPath,
                    entries: listing.entries,
                    homeDirectory: listing.homeDirectory,
                    currentDirectory: listing.currentDirectory,
                });
            }
        }
        catch (err) {
            if (id !== requestIdRef.current)
                return;
            dispatch({ type: "FAILURE", error: toError(err) });
        }
    }, [runnerId, stigmer]);
    // Initial load: fetch home directory when runnerId becomes available.
    const initializedForRef = useRef(null);
    useEffect(() => {
        if (!runnerId) {
            initializedForRef.current = null;
            return;
        }
        if (initializedForRef.current !== runnerId) {
            initializedForRef.current = runnerId;
            fetchDirectory("");
        }
    }, [runnerId, fetchDirectory]);
    const navigateTo = useCallback((name) => {
        const next = state.currentPath === "/"
            ? `/${name}`
            : `${state.currentPath}/${name}`;
        fetchDirectory(next);
    }, [state.currentPath, fetchDirectory]);
    const navigateToPath = useCallback((path) => fetchDirectory(path), [fetchDirectory]);
    const navigateUp = useCallback(() => fetchDirectory(parentPath(state.currentPath)), [state.currentPath, fetchDirectory]);
    const navigateHome = useCallback(() => fetchDirectory(state.homeDirectory || "~"), [state.homeDirectory, fetchDirectory]);
    const navigateCwd = useCallback(() => {
        if (state.currentDirectory)
            fetchDirectory(state.currentDirectory);
    }, [state.currentDirectory, fetchDirectory]);
    const retry = useCallback(() => fetchDirectory(state.requestedPath), [state.requestedPath, fetchDirectory]);
    const toggleHidden = useCallback(() => dispatch({ type: "TOGGLE_HIDDEN" }), []);
    const segments = buildSegments(state.currentPath);
    const isAtRoot = state.currentPath === "/";
    return {
        currentPath: state.currentPath,
        entries: state.entries,
        segments,
        homeDirectory: state.homeDirectory,
        currentDirectory: state.currentDirectory,
        showHidden: state.showHidden,
        toggleHidden,
        isLoading: state.isLoading,
        error: state.error,
        navigateTo,
        navigateToPath,
        navigateUp,
        navigateHome,
        navigateCwd,
        retry,
        isAtRoot,
    };
}
//# sourceMappingURL=useRunnerFileBrowser.js.map