"use client";
import { useCallback, useEffect, useMemo, useReducer, useRef } from "react";
import { create } from "@bufbuild/protobuf";
import { GetOAuthGrantStatusInputSchema } from "@stigmer/protos/ai/stigmer/agentic/mcpserver/v1/io_pb";
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
import { useStigmer } from "../hooks";
import { usePersonalEnvironment } from "../environment/usePersonalEnvironment";
import { diffEnv } from "../environment/diffEnv";
import { toError } from "../internal/toError";
import { mcpServerSetupReducer, INITIAL_MCP_SETUP_STATE, toServerKey, } from "./mcpServerSetupReducer";
export { toServerKey } from "./mcpServerSetupReducer";
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
/**
 * Compute the initial enabled tools for a server based on its spec
 * defaults and discovered capabilities.
 *
 * If `default_enabled_tools` is non-empty, uses that subset. Otherwise,
 * enables all discovered tools (empty array when nothing is discovered —
 * equivalent to "all" at runtime).
 */
function computeDefaultEnabledTools(mcpServer, discoveredTools) {
    const defaults = mcpServer.spec?.defaultEnabledTools;
    if (defaults && defaults.length > 0)
        return [...defaults];
    return discoveredTools.map(t => t.name);
}
// ---------------------------------------------------------------------------
// Hook
// ---------------------------------------------------------------------------
/**
 * Layer 2 behavior hook that orchestrates the setup flow for multiple
 * MCP servers selected in the {@link McpServerPicker}.
 *
 * When a user toggles an MCP server ON, this hook fetches the server's
 * full resource, checks its `env` declarations against the personal
 * environment (via {@link diffEnv}), and determines whether credentials are
 * needed. It also extracts discovered tools and approval policies for
 * the tool selector UI.
 *
 * The hook supports two credential delivery paths via the `saveForFuture`
 * option on {@link submitEnvVars}:
 * - **Saved** — secrets are persisted to the personal environment for
 *   reuse across sessions.
 * - **One-time** — secrets are collected as `pendingRuntimeEnv` for a
 *   single execution, with no data persisted.
 *
 * State is managed by `useReducer(mcpServerSetupReducer)` — a per-server
 * state machine with four phases:
 * `loading → needsSetup → submitting → ready`.
 *
 * Composes {@link usePersonalEnvironment} for credential persistence and
 * the Stigmer client for MCP server queries.
 *
 * Mirrors the architecture of {@link useAgentSetup} but adapted for
 * multi-server orchestration (N independent entries vs. single agent).
 *
 * Pass `null` as `org` to disable all operations (stable no-op).
 *
 * @param org - Organization slug. Pass `null` to disable.
 * @param poolKeys - Optional set of env-var keys already available
 *   from the session env pool (manual secrets, one-time env vars from
 *   other components). When provided, servers whose `env` keys
 *   are fully covered by `poolKeys` + personal env auto-resolve to
 *   `ready` without prompting. Reactive — when `poolKeys` changes,
 *   `needsSetup` entries are re-evaluated.
 *
 * @example
 * ```tsx
 * const {
 *   entries,
 *   addServer,
 *   submitEnvVars,
 *   setEnabledTools,
 *   allReady,
 *   usageInputs,
 * } = useMcpServerSetup("acme", pool.availableKeys);
 *
 * // When user toggles a server ON in the picker:
 * await addServer({ org: "acme", slug: "github", kind: ApiResourceKind.mcp_server });
 *
 * // If the entry resolves to "needsSetup":
 * await submitEnvVars(ref, { GITHUB_TOKEN: { value: "ghp_...", isSecret: true } });
 *
 * // Customize tools for a ready server:
 * setEnabledTools(ref, ["create_issue", "list_issues"]);
 *
 * // At session creation:
 * const session = await createSession({ mcpServerUsages: usageInputs });
 * ```
 */
export function useMcpServerSetup(org, poolKeys) {
    const stigmer = useStigmer();
    const personalEnv = usePersonalEnvironment(org);
    const [entries, dispatch] = useReducer(mcpServerSetupReducer, INITIAL_MCP_SETUP_STATE);
    const runtimeEnvRef = useRef({});
    const entriesRef = useRef(entries);
    entriesRef.current = entries;
    // -------------------------------------------------------------------------
    // addServer
    // -------------------------------------------------------------------------
    const addServer = useCallback(async (ref) => {
        if (!org) {
            throw new Error("useMcpServerSetup: cannot add server when org is null.");
        }
        const key = toServerKey(ref);
        dispatch({ type: "ADD_SERVER", key });
        try {
            const mcpServer = await stigmer.mcpServer.getByReference(ref);
            const discoveredTools = mcpServer.status?.discoveredCapabilities?.tools ?? [];
            const toolApprovals = mcpServer.spec?.pinnedToolApprovals ?? [];
            const envDeclarations = mcpServer.spec?.env;
            if (!envDeclarations || Object.keys(envDeclarations).length === 0) {
                dispatch({
                    type: "RESOLVE_READY",
                    key,
                    mcpServer,
                    discoveredTools,
                    toolApprovals,
                    enabledTools: computeDefaultEnabledTools(mcpServer, discoveredTools),
                });
                return;
            }
            const existingKeys = new Set(Object.keys(personalEnv.environment?.spec?.data ?? {}));
            const auth = mcpServer.spec?.auth;
            if (auth?.targetEnvVar && mcpServer.metadata?.id) {
                try {
                    const grantStatus = await stigmer.mcpServer.getOAuthGrantStatus(create(GetOAuthGrantStatusInputSchema, {
                        resourceId: mcpServer.metadata.id,
                        org,
                    }));
                    if (grantStatus.connected) {
                        existingKeys.add(auth.targetEnvVar);
                    }
                }
                catch {
                    // Non-fatal: if grant status lookup fails, the OAuth var stays
                    // in missingVariables and the UI shows the sign-in button.
                }
            }
            const allMissing = diffEnv(envDeclarations, existingKeys, poolKeys);
            const requiredMissing = allMissing.filter((v) => !v.optional);
            if (requiredMissing.length === 0) {
                dispatch({
                    type: "RESOLVE_READY",
                    key,
                    mcpServer,
                    discoveredTools,
                    toolApprovals,
                    enabledTools: computeDefaultEnabledTools(mcpServer, discoveredTools),
                });
                return;
            }
            dispatch({
                type: "RESOLVE_NEEDS_SETUP",
                key,
                mcpServer,
                missingVariables: requiredMissing,
                discoveredTools,
                toolApprovals,
            });
        }
        catch (err) {
            dispatch({ type: "SET_ERROR", key, error: toError(err) });
        }
    }, [org, stigmer, personalEnv, poolKeys]);
    // -------------------------------------------------------------------------
    // removeServer
    // -------------------------------------------------------------------------
    const removeServer = useCallback((ref) => {
        dispatch({ type: "REMOVE_SERVER", key: toServerKey(ref) });
    }, []);
    // -------------------------------------------------------------------------
    // submitEnvVars
    // -------------------------------------------------------------------------
    const submitEnvVars = useCallback(async (ref, values, options) => {
        if (!org) {
            throw new Error("useMcpServerSetup: cannot submit env vars when org is null.");
        }
        const key = toServerKey(ref);
        const entry = entries[key];
        if (!entry || entry.status !== "needsSetup") {
            throw new Error("useMcpServerSetup: submitEnvVars requires the server to be in " +
                `'needsSetup' status. Server '${key}' is ` +
                `${entry ? `in '${entry.status}' status` : "not selected"}. ` +
                "Call addServer() first and wait for it to resolve.");
        }
        const { mcpServer, discoveredTools } = entry;
        const enabledTools = computeDefaultEnabledTools(mcpServer, discoveredTools);
        const saveForFuture = options?.saveForFuture ?? true;
        dispatch({ type: "SUBMIT_START", key });
        if (!saveForFuture) {
            Object.assign(runtimeEnvRef.current, values);
            dispatch({ type: "SUBMIT_DONE", key, enabledTools });
            return;
        }
        try {
            await personalEnv.getOrCreate();
            await personalEnv.addVariables(values);
            dispatch({ type: "SUBMIT_DONE", key, enabledTools });
        }
        catch (err) {
            dispatch({ type: "SUBMIT_FAIL", key, error: toError(err) });
        }
    }, [org, entries, personalEnv]);
    // -------------------------------------------------------------------------
    // setEnabledTools
    // -------------------------------------------------------------------------
    const setEnabledTools = useCallback((ref, tools) => {
        dispatch({
            type: "SET_ENABLED_TOOLS",
            key: toServerKey(ref),
            enabledTools: tools,
        });
    }, []);
    // -------------------------------------------------------------------------
    // clearError / reset
    // -------------------------------------------------------------------------
    const clearError = useCallback((ref) => {
        dispatch({ type: "CLEAR_ERROR", key: toServerKey(ref) });
    }, []);
    const reset = useCallback(() => {
        dispatch({ type: "RESET" });
        runtimeEnvRef.current = {};
    }, []);
    // -------------------------------------------------------------------------
    // Pool re-evaluation — auto-resolve needsSetup entries when pool changes
    // -------------------------------------------------------------------------
    useEffect(() => {
        if (!poolKeys || poolKeys.size === 0)
            return;
        const personalKeys = new Set(Object.keys(personalEnv.environment?.spec?.data ?? {}));
        for (const [key, entry] of Object.entries(entriesRef.current)) {
            if (entry.status !== "needsSetup")
                continue;
            const envDeclarations = entry.mcpServer.spec?.env;
            if (!envDeclarations)
                continue;
            const allMissing = diffEnv(envDeclarations, personalKeys, poolKeys);
            const requiredMissing = allMissing.filter((v) => !v.optional);
            const enabledTools = computeDefaultEnabledTools(entry.mcpServer, entry.discoveredTools);
            dispatch({ type: "POOL_RESOLVE", key, missingVariables: requiredMissing, enabledTools });
        }
    }, [poolKeys, personalEnv.environment]);
    // -------------------------------------------------------------------------
    // Derived state
    // -------------------------------------------------------------------------
    const allReady = useMemo(() => {
        const values = Object.values(entries);
        return values.length === 0 || values.every(e => e.status === "ready");
    }, [entries]);
    const needsSetupCount = useMemo(() => Object.values(entries).filter(e => e.status === "needsSetup").length, [entries]);
    const usageInputs = useMemo(() => {
        const result = [];
        for (const [serverKey, entry] of Object.entries(entries)) {
            if (entry.status !== "ready")
                continue;
            const allNames = entry.discoveredTools.map(t => t.name);
            const isAllEnabled = allNames.length === 0 ||
                (entry.enabledTools.length === allNames.length &&
                    entry.enabledTools.every(name => allNames.includes(name)));
            const separatorIdx = serverKey.indexOf("/");
            result.push({
                mcpServerRef: {
                    org: serverKey.slice(0, separatorIdx),
                    slug: serverKey.slice(separatorIdx + 1),
                    kind: ApiResourceKind.mcp_server,
                },
                enabledTools: isAllEnabled ? undefined : [...entry.enabledTools],
            });
        }
        return result;
    }, [entries]);
    return {
        entries,
        addServer,
        removeServer,
        submitEnvVars,
        setEnabledTools,
        clearError,
        reset,
        allReady,
        needsSetupCount,
        pendingRuntimeEnv: runtimeEnvRef.current,
        usageInputs,
    };
}
//# sourceMappingURL=useMcpServerSetup.js.map