"use client";
import { useCallback, useEffect, useReducer } from "react";
import { create } from "@bufbuild/protobuf";
import { ListAgentInstancesRequestSchema } from "@stigmer/protos/ai/stigmer/agentic/agentinstance/v1/io_pb";
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
import { useStigmer } from "../hooks";
import { toError } from "../internal/toError";
import { usePersonalEnvironment } from "../environment/usePersonalEnvironment";
import { buildPersonalInstanceInput } from "../agent-instance/buildPersonalInstanceInput";
import { diffEnv } from "../environment/diffEnv";
import { agentSetupReducer, INITIAL_STATE, } from "./agentSetupReducer";
const PERSONAL_LABEL = "stigmer.ai/personal";
const FOR_AGENT_LABEL = "stigmer.ai/for-agent";
/**
 * Re-checks for an existing personal instance immediately before
 * creating one. Narrows the race window when multiple clients
 * (tabs, double-clicks) attempt to create simultaneously.
 */
async function findOrCreatePersonalInstance(stigmer, params) {
    const { org, agentId, agentSlug, agentLabel, environmentRef } = params;
    const recheck = await stigmer.agentInstance.list(create(ListAgentInstancesRequestSchema, {
        org,
        labels: {
            [PERSONAL_LABEL]: "true",
            [FOR_AGENT_LABEL]: agentLabel,
        },
    }));
    if (recheck.items.length > 0) {
        return recheck.items[0];
    }
    return stigmer.agentInstance.create(buildPersonalInstanceInput({ org, agentId, agentSlug, environmentRef }));
}
/**
 * Layer 2 behavior hook that encapsulates the agent selection,
 * personal environment resolution, and secret delivery routing flow.
 *
 * When a user picks an agent in the {@link AgentPicker}, this hook
 * determines whether the agent requires credentials (via its
 * `env` declarations), checks what the user has already provided in their
 * personal environment, and either reports the agent as ready or
 * identifies the missing variables so the caller can render
 * {@link AgentEnvForm}.
 *
 * The hook supports two secret delivery paths via the `saveForFuture`
 * option on {@link submitEnvVars}:
 * - **Saved** — secrets are persisted to the personal environment and
 *   a personal agent instance is created for reuse.
 * - **One-time** — secrets are returned as `runtimeEnv` for a single
 *   execution, with no data persisted.
 *
 * State is managed by a `useReducer` state machine with five phases:
 * `idle → resolving → needsEnvVars → submitting → ready`.
 *
 * Composes {@link usePersonalEnvironment} for personal environment
 * operations and calls the Stigmer client directly for agent and
 * agent instance queries.
 *
 * > **Why not compose `usePersonalAgentInstance`?**
 * > `resolveAgent` is an imperative async callback that needs
 * > immediate results within a single invocation. Hook state
 * > updates require a render cycle. Instance creation is delegated
 * > through the shared `buildPersonalInstanceInput` helper instead.
 *
 * 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, agents whose `env` keys
 *   are fully covered by `poolKeys` + personal env auto-resolve to
 *   `ready` without prompting. Reactive — when `poolKeys` changes,
 *   `needsEnvVars` is re-evaluated.
 *
 * @example
 * ```tsx
 * const { state, resolveAgent, submitEnvVars } = useAgentSetup("acme", pool.availableKeys);
 *
 * const result = await resolveAgent({ org: "acme", slug: "code-reviewer" });
 *
 * if (result.status === "needsEnvVars") {
 *   // Render AgentEnvForm with result.missingVariables
 *   // On form submit:
 *   const ready = await submitEnvVars(formValues, { saveForFuture: true });
 *   // ready.resolution.mode === "saved" | "oneTime"
 * }
 * ```
 */
export function useAgentSetup(org, poolKeys) {
    const stigmer = useStigmer();
    const personalEnv = usePersonalEnvironment(org);
    const [state, dispatch] = useReducer(agentSetupReducer, INITIAL_STATE);
    const clearError = useCallback(() => dispatch({ type: "CLEAR_ERROR" }), []);
    const reset = useCallback(() => dispatch({ type: "RESET" }), []);
    // -------------------------------------------------------------------------
    // resolveAgent
    // -------------------------------------------------------------------------
    const resolveAgent = useCallback(async (ref) => {
        if (!org) {
            throw new Error("useAgentSetup: cannot resolve agent when org is null.");
        }
        dispatch({ type: "RESOLVE_START", agentRef: ref });
        try {
            const agent = await stigmer.agent.getByReference(ref);
            const agentName = agent.metadata?.name ?? ref.slug;
            const envDeclarations = agent.spec?.env;
            // No env declarations — agent is immediately ready (direct mode).
            if (!envDeclarations || Object.keys(envDeclarations).length === 0) {
                const resolution = { mode: "direct" };
                dispatch({
                    type: "RESOLVE_READY",
                    agentRef: ref,
                    agentName,
                    resolution,
                });
                return { status: "ready", agentRef: ref, agentName, resolution };
            }
            // Agent has env declarations — check for existing personal instance.
            const agentLabel = `${ref.org}/${ref.slug}`;
            const instanceList = await stigmer.agentInstance.list(create(ListAgentInstancesRequestSchema, {
                org,
                labels: {
                    [PERSONAL_LABEL]: "true",
                    [FOR_AGENT_LABEL]: agentLabel,
                },
            }));
            if (instanceList.items.length > 0) {
                const resolution = {
                    mode: "saved",
                    instanceId: instanceList.items[0].metadata.id,
                };
                dispatch({
                    type: "RESOLVE_READY",
                    agentRef: ref,
                    agentName,
                    resolution,
                });
                return { status: "ready", agentRef: ref, agentName, resolution };
            }
            // No personal instance — diff against existing env keys + pool.
            const existingKeys = new Set(Object.keys(personalEnv.environment?.spec?.data ?? {}));
            const personalOnlyMissing = diffEnv(envDeclarations, existingKeys);
            const missingVariables = diffEnv(envDeclarations, existingKeys, poolKeys);
            if (personalOnlyMissing.length === 0) {
                // Personal env covers all keys — create personal instance.
                const env = await personalEnv.getOrCreate();
                const envRef = {
                    org,
                    slug: env.metadata.slug,
                    kind: ApiResourceKind.environment,
                };
                const instance = await findOrCreatePersonalInstance(stigmer, {
                    org,
                    agentId: agent.metadata.id,
                    agentSlug: ref.slug,
                    agentLabel,
                    environmentRef: envRef,
                });
                const resolution = {
                    mode: "saved",
                    instanceId: instance.metadata.id,
                };
                dispatch({
                    type: "RESOLVE_READY",
                    agentRef: ref,
                    agentName,
                    resolution,
                });
                return { status: "ready", agentRef: ref, agentName, resolution };
            }
            if (missingVariables.length === 0) {
                // Pool covers remaining keys — use default instance, pool
                // values flow via sessionVariables.toRuntimeEnv() at submit.
                const resolution = { mode: "direct" };
                dispatch({
                    type: "RESOLVE_READY",
                    agentRef: ref,
                    agentName,
                    resolution,
                });
                return { status: "ready", agentRef: ref, agentName, resolution };
            }
            // Missing variables — transition to needsEnvVars.
            dispatch({
                type: "RESOLVE_NEEDS_ENV",
                agentRef: ref,
                agentId: agent.metadata.id,
                agentName,
                missingVariables,
            });
            return {
                status: "needsEnvVars",
                agentRef: ref,
                agentName,
                missingVariables,
            };
        }
        catch (err) {
            dispatch({ type: "ERROR", error: toError(err) });
            throw err;
        }
    }, [org, stigmer, personalEnv, poolKeys]);
    // -------------------------------------------------------------------------
    // Pool re-evaluation — auto-resolve needsEnvVars when pool changes
    // -------------------------------------------------------------------------
    const agentMissingVars = state.status === "needsEnvVars" ? state.missingVariables : null;
    useEffect(() => {
        if (!poolKeys || poolKeys.size === 0)
            return;
        if (!agentMissingVars)
            return;
        const stillMissing = agentMissingVars.filter((v) => !poolKeys.has(v.key));
        dispatch({ type: "POOL_RESOLVE", missingVariables: stillMissing });
    }, [poolKeys, agentMissingVars]);
    // -------------------------------------------------------------------------
    // submitEnvVars
    // -------------------------------------------------------------------------
    const submitEnvVars = useCallback(async (values, options) => {
        if (state.status !== "needsEnvVars") {
            throw new Error("useAgentSetup: submitEnvVars requires state.status === 'needsEnvVars'. " +
                `Current status is '${state.status}'. Call resolveAgent() first ` +
                "and ensure it returned status 'needsEnvVars'.");
        }
        if (!org) {
            throw new Error("useAgentSetup: cannot submit env vars when org is null.");
        }
        const { agentRef, agentId, agentName } = state;
        const saveForFuture = options?.saveForFuture ?? true;
        // ----- One-time path: no API calls, instant result -----
        if (!saveForFuture) {
            const resolution = {
                mode: "oneTime",
                runtimeEnv: values,
            };
            dispatch({
                type: "SUBMIT_READY",
                agentRef,
                agentName,
                resolution,
            });
            return { status: "ready", agentRef, agentName, resolution };
        }
        // ----- Save path: persist to environment + create instance -----
        dispatch({ type: "SUBMIT_START" });
        try {
            const env = await personalEnv.getOrCreate();
            await personalEnv.addVariables(values);
            const envRef = {
                org,
                slug: env.metadata.slug,
                kind: ApiResourceKind.environment,
            };
            const agentLabel = `${agentRef.org}/${agentRef.slug}`;
            const instance = await findOrCreatePersonalInstance(stigmer, {
                org,
                agentId,
                agentSlug: agentRef.slug,
                agentLabel,
                environmentRef: envRef,
            });
            const resolution = {
                mode: "saved",
                instanceId: instance.metadata.id,
            };
            dispatch({
                type: "SUBMIT_READY",
                agentRef,
                agentName,
                resolution,
            });
            return { status: "ready", agentRef, agentName, resolution };
        }
        catch (err) {
            dispatch({ type: "ERROR", error: toError(err) });
            throw err;
        }
    }, [org, stigmer, personalEnv, state]);
    return { state, resolveAgent, submitEnvVars, clearError, reset };
}
//# sourceMappingURL=useAgentSetup.js.map