import { useEffect, useRef } from "react";
import { getBackendLatestSeq, isStreamStalled, shouldReloadAfterDiagnostics, STREAM_STALLED_AFTER_MS, } from "../agentDiagnostics";
import { emitAgentRecoveryEvent } from "../agentRecoveryEventBus";
import { MACHINE_CAPACITY_REASON, isInactiveServerExecutionStatus, normalizeServerExecutionStatus, } from "../agentMessageHelpers";
import { updateAgentWatchdogStatus } from "../agentWatchdogStatusRegistry";
import { forceEditorSocketReconnect } from "../../client/hooks/useEditorWebSocket";
import { selectIsWatchdogActive } from "./agentSessionSelectors";
import { loadAgentRunDiagnostics } from "./useAgentSessionSync";
export function useAgentStreamWatchdog(input) {
    const { currentAgentId, sessionId, socketConnectionVersion, effectiveIsVisible, isExecuting, isStopGuardActive, isAgentThinking, isSubmitting, isConnecting, isWaitingForResponse, hasActiveStreaming, lastRunStatusReason, isStoppingRef, lastNonHeartbeatUpdateAtRef, lastStallReloadAtRef, lastSeqRef, currentRunIdRef, seenMessageIdsRef, appendToolUiEvent, loadAgent, normalizeRunId, settleCompletedRun, } = input;
    const previousWatchdogActiveRef = useRef(false);
    const isWatchdogActive = selectIsWatchdogActive({
        isStopGuardActive,
        isExecuting,
        isAgentThinking,
        isSubmitting,
        isConnecting,
        isWaitingForResponse,
        hasActiveStreaming,
    });
    useEffect(() => {
        if (isWatchdogActive && !previousWatchdogActiveRef.current) {
            lastNonHeartbeatUpdateAtRef.current = Date.now();
        }
        previousWatchdogActiveRef.current = isWatchdogActive;
    }, [isWatchdogActive, lastNonHeartbeatUpdateAtRef]);
    useEffect(() => {
        if (isWatchdogActive) {
            lastNonHeartbeatUpdateAtRef.current = Date.now();
        }
    }, [socketConnectionVersion, isWatchdogActive, lastNonHeartbeatUpdateAtRef]);
    useEffect(() => {
        lastNonHeartbeatUpdateAtRef.current = 0;
        lastStallReloadAtRef.current = 0;
    }, [currentAgentId, lastNonHeartbeatUpdateAtRef, lastStallReloadAtRef]);
    useEffect(() => {
        if (!currentAgentId)
            return;
        updateAgentWatchdogStatus(currentAgentId, {
            isExecuting,
            isWatchdogActive,
            isVisible: effectiveIsVisible,
            isStopping: isStoppingRef.current,
            isWaitingForCapacity: lastRunStatusReason === MACHINE_CAPACITY_REASON,
            stalledThresholdMs: STREAM_STALLED_AFTER_MS,
        });
    }, [
        currentAgentId,
        isExecuting,
        isWatchdogActive,
        effectiveIsVisible,
        lastRunStatusReason,
        isStoppingRef,
    ]);
    useEffect(() => {
        if (!effectiveIsVisible || !isWatchdogActive || !currentAgentId) {
            return;
        }
        let disposed = false;
        const agentIdAtMount = currentAgentId;
        lastNonHeartbeatUpdateAtRef.current = Date.now();
        updateAgentWatchdogStatus(agentIdAtMount, {
            running: true,
            startedAt: Date.now(),
            stalledThresholdMs: STREAM_STALLED_AFTER_MS,
        });
        const logRecoveryEvent = (kind, payload) => {
            try {
                appendToolUiEvent(`recovery:${kind}`, JSON.stringify({ agentId: agentIdAtMount, ...payload }));
            }
            catch {
                // Telemetry must never throw out of the watchdog path.
            }
            try {
                emitAgentRecoveryEvent({
                    agentId: agentIdAtMount,
                    kind,
                    payload,
                    timestamp: new Date().toISOString(),
                });
            }
            catch {
                // Telemetry must never throw out of the watchdog path.
            }
        };
        const tick = async () => {
            updateAgentWatchdogStatus(agentIdAtMount, {
                running: true,
                lastTickAt: Date.now(),
                isExecuting,
                isWatchdogActive: true,
                isVisible: true,
                isStopping: isStoppingRef.current,
                isWaitingForCapacity: lastRunStatusReason === MACHINE_CAPACITY_REASON,
                lastNonHeartbeatUpdateAt: lastNonHeartbeatUpdateAtRef.current,
                stalledThresholdMs: STREAM_STALLED_AFTER_MS,
            });
            if (!isStreamStalled({
                isExecuting: true,
                isVisible: true,
                isStopping: isStoppingRef.current,
                isWaitingForCapacity: lastRunStatusReason === MACHINE_CAPACITY_REASON,
                lastUpdateAt: lastNonHeartbeatUpdateAtRef.current,
                now: Date.now(),
            })) {
                return;
            }
            logRecoveryEvent("stall-check", {
                silentMs: Date.now() - lastNonHeartbeatUpdateAtRef.current,
                localSeq: lastSeqRef.current,
            });
            let diagnostics;
            try {
                diagnostics = await loadAgentRunDiagnostics(agentIdAtMount, sessionId);
            }
            catch (error) {
                console.warn("[AgentTerminal] Stall watchdog failed to fetch diagnostics", error);
                logRecoveryEvent("diagnostics-failed", {
                    message: error instanceof Error ? error.message : String(error),
                });
                lastNonHeartbeatUpdateAtRef.current = Date.now();
                return;
            }
            if (disposed || agentIdAtMount !== currentAgentId) {
                return;
            }
            const backendSeq = getBackendLatestSeq(diagnostics);
            const localSeq = lastSeqRef.current;
            if (isInactiveServerExecutionStatus(diagnostics.execution?.status)) {
                const diagnosticRunId = diagnostics.execution?.runId ?? null;
                const normalizedDiagnosticRunId = normalizeRunId(diagnosticRunId);
                if (currentRunIdRef.current !== null &&
                    normalizedDiagnosticRunId !== null &&
                    currentRunIdRef.current !== normalizedDiagnosticRunId) {
                    logRecoveryEvent("finished-run-stale", {
                        status: diagnostics.execution?.status,
                        diagnosticRunId,
                        currentRunId: currentRunIdRef.current,
                        backendSeq,
                        localSeq,
                    });
                    lastNonHeartbeatUpdateAtRef.current = Date.now();
                    return;
                }
                logRecoveryEvent("finished-run-reconcile", {
                    status: diagnostics.execution?.status,
                    backendSeq,
                    localSeq,
                });
                try {
                    await loadAgent();
                }
                catch (error) {
                    console.warn("[AgentTerminal] Finished-run reconcile loadAgent() failed", error);
                }
                if (disposed || agentIdAtMount !== currentAgentId) {
                    return;
                }
                settleCompletedRun(normalizeServerExecutionStatus(diagnostics.execution?.status) ===
                    "cancelled"
                    ? "cancelled"
                    : "completed", diagnosticRunId);
                lastNonHeartbeatUpdateAtRef.current = Date.now();
                return;
            }
            if (!shouldReloadAfterDiagnostics({ localSeq, backendSeq })) {
                lastNonHeartbeatUpdateAtRef.current = Date.now();
                return;
            }
            const sinceLastReload = Date.now() - lastStallReloadAtRef.current;
            if (lastStallReloadAtRef.current > 0 && sinceLastReload < 30_000) {
                logRecoveryEvent("reload-cooldown", { sinceLastReload });
                lastNonHeartbeatUpdateAtRef.current = Date.now();
                return;
            }
            lastStallReloadAtRef.current = Date.now();
            logRecoveryEvent("reload-fired", { backendSeq, localSeq });
            lastSeqRef.current = 0;
            seenMessageIdsRef.current.clear();
            try {
                await loadAgent();
            }
            catch (error) {
                console.warn("[AgentTerminal] Stall reload loadAgent() failed", error);
            }
            if (disposed || agentIdAtMount !== currentAgentId) {
                return;
            }
            forceEditorSocketReconnect("agent-stream-stalled");
            lastNonHeartbeatUpdateAtRef.current = Date.now();
        };
        const intervalId = window.setInterval(() => {
            void tick();
        }, 5_000);
        return () => {
            disposed = true;
            window.clearInterval(intervalId);
            updateAgentWatchdogStatus(agentIdAtMount, {
                running: false,
                stoppedAt: Date.now(),
            });
        };
    }, [
        appendToolUiEvent,
        currentAgentId,
        currentRunIdRef,
        effectiveIsVisible,
        hasActiveStreaming,
        isAgentThinking,
        isConnecting,
        isExecuting,
        isStoppingRef,
        isStopGuardActive,
        isSubmitting,
        isWaitingForResponse,
        isWatchdogActive,
        lastNonHeartbeatUpdateAtRef,
        lastRunStatusReason,
        lastSeqRef,
        lastStallReloadAtRef,
        loadAgent,
        normalizeRunId,
        seenMessageIdsRef,
        sessionId,
        settleCompletedRun,
    ]);
}
//# sourceMappingURL=useAgentStreamWatchdog.js.map