import { beforeEach, describe, expect, it, vi } from "vitest";

const dispatchVoiceToolCallMock = vi.fn();
const toastInfoMock = vi.fn();
const toastSuccessMock = vi.fn();
const logVoiceMetricMock = vi.fn();

vi.mock("@/lib/voice/voice-action-dispatcher", () => ({
  dispatchVoiceToolCall: (...args: unknown[]) => dispatchVoiceToolCallMock(...args),
}));

vi.mock("@/lib/morphy-ux/morphy", () => ({
  morphyToast: {
    info: (...args: unknown[]) => toastInfoMock(...args),
    success: (...args: unknown[]) => toastSuccessMock(...args),
  },
}));

vi.mock("@/lib/voice/voice-telemetry", () => ({
  logVoiceMetric: (...args: unknown[]) => logVoiceMetricMock(...args),
}));

import { executeVoiceResponse } from "@/lib/voice/voice-response-executor";

const originalGroundedExecutionFlag =
  process.env.NEXT_PUBLIC_VOICE_V2_GROUNDED_ACTION_EXECUTION_ENABLED;

function baseInput() {
  return {
    userId: "user_1",
    vaultOwnerToken: "vault_token",
    vaultKey: "vault_key",
    currentRoute: "/kai/dashboard",
    currentScreen: "dashboard",
    router: {
      push: vi.fn(),
    },
    handleBack: vi.fn(),
    executeKaiCommand: vi.fn(() => ({ status: "executed" as const })),
    setAnalysisParams: vi.fn(),
  };
}

describe("executeVoiceResponse", () => {
  beforeEach(() => {
    dispatchVoiceToolCallMock.mockReset();
    dispatchVoiceToolCallMock.mockImplementation(async ({ toolCall }: { toolCall: { tool_name: string } }) => ({
      status: "executed",
      toolName: toolCall.tool_name,
    }));
    toastInfoMock.mockReset();
    toastSuccessMock.mockReset();
    logVoiceMetricMock.mockReset();
    if (originalGroundedExecutionFlag === undefined) {
      delete process.env.NEXT_PUBLIC_VOICE_V2_GROUNDED_ACTION_EXECUTION_ENABLED;
    } else {
      process.env.NEXT_PUBLIC_VOICE_V2_GROUNDED_ACTION_EXECUTION_ENABLED =
        originalGroundedExecutionFlag;
    }
  });

  it("dispatches execute response through voice tool dispatcher", async () => {
    const result = await executeVoiceResponse({
      ...baseInput(),
      response: {
        kind: "execute",
        message: "Starting analysis for NVDA.",
        speak: true,
        tool_call: {
          tool_name: "execute_kai_command",
          args: {
            command: "analyze",
            params: {
              symbol: "NVDA",
            },
          },
        },
      },
    });

    expect(dispatchVoiceToolCallMock).toHaveBeenCalledTimes(1);
    expect(result).toEqual({
      shortTermMemoryWrite: true,
      toolName: "execute_kai_command",
      ticker: "NVDA",
      responseKind: "execute",
      actionResult: {
        status: "succeeded",
        actionId: null,
        routeBefore: "/kai/dashboard",
        routeAfter: null,
        screenBefore: "dashboard",
        screenAfter: null,
        resultSummary: "Executed the requested voice action.",
        data: undefined,
      },
    });
  });

  it("blocks destructive grounded actions and asks for manual completion", async () => {
    const result = await executeVoiceResponse({
      ...baseInput(),
      response: {
        kind: "speak_only",
        message: "Please do that yourself in the app.",
        speak: true,
      },
      groundedPlan: {
        status: "manual_only",
        actionId: "profile.delete_account",
        actionLabel: "Delete Account",
        destructive: true,
        message: "Please do that yourself in the app.",
        resolutionSource: "transcript",
        execution: {
          mode: "manual_only",
          steps: [
            {
              type: "prompt",
              message: "Please do that yourself in the app.",
              reason: "destructive_action_policy",
            },
          ],
        },
      },
    });

    expect(dispatchVoiceToolCallMock).not.toHaveBeenCalled();
    expect(toastInfoMock).toHaveBeenCalledWith("Please do that yourself in the app.");
    expect(result).toEqual({
      shortTermMemoryWrite: false,
      toolName: null,
      ticker: null,
      responseKind: "speak_only",
      actionResult: {
        status: "blocked",
        actionId: "profile.delete_account",
        routeBefore: "/kai/dashboard",
        routeAfter: null,
        screenBefore: "dashboard",
        screenAfter: null,
        resultSummary: "Please do that yourself in the app.",
        data: {
          executionMode: "manual_only",
          policy: "manual_only",
        },
      },
    });
  });

  it("does not execute grounded or legacy actions when execution is disallowed", async () => {
    const result = await executeVoiceResponse({
      ...baseInput(),
      executionAllowed: false,
      response: {
        kind: "execute",
        message: "Resuming active analysis.",
        speak: true,
        tool_call: {
          tool_name: "resume_active_analysis",
          args: {},
        },
      },
      groundedPlan: {
        status: "resolved",
        actionId: "analysis.resume_active",
        actionLabel: "Resume Active Analysis Run",
        destructive: false,
        message: null,
        resolutionSource: "canonical",
        execution: {
          mode: "navigate_then_action",
          steps: [
            {
              type: "navigate",
              href: "/kai/analysis",
              reason: "hidden_action_navigation_prerequisite",
            },
          ],
        },
      },
    });

    expect(dispatchVoiceToolCallMock).not.toHaveBeenCalled();
    expect(result).toEqual({
      shortTermMemoryWrite: false,
      toolName: null,
      ticker: null,
      responseKind: "execute",
      actionResult: {
        status: "blocked",
        actionId: "analysis.resume_active",
        routeBefore: "/kai/dashboard",
        routeAfter: null,
        screenBefore: "dashboard",
        screenAfter: null,
        resultSummary: "Execution was disallowed by the backend for this response.",
        data: {
          responseKind: "execute",
          groundedStatus: "resolved",
        },
      },
    });
  });

  it("defers execution when the planner requires confirmation", async () => {
    const setPendingConfirmation = vi.fn();
    const result = await executeVoiceResponse({
      ...baseInput(),
      needsConfirmation: true,
      setPendingConfirmation,
      response: {
        kind: "execute",
        message: "Do you want me to cancel the active analysis?",
        speak: true,
        tool_call: {
          tool_name: "cancel_active_analysis",
          args: {
            confirm: false,
          },
        },
      },
    });

    expect(dispatchVoiceToolCallMock).not.toHaveBeenCalled();
    expect(setPendingConfirmation).toHaveBeenCalledWith(
      expect.objectContaining({
        kind: "cancel_active_analysis",
        prompt: "Do you want me to cancel the active analysis?",
      })
    );
    expect(result).toEqual({
      shortTermMemoryWrite: false,
      toolName: null,
      ticker: null,
      responseKind: "execute",
      actionResult: {
        status: "noop",
        actionId: null,
        routeBefore: "/kai/dashboard",
        routeAfter: null,
        screenBefore: "dashboard",
        screenAfter: null,
        resultSummary: "Waiting for confirmation before running the requested action.",
        data: {
          toolName: "cancel_active_analysis",
        },
      },
    });
  });

  it("defers the RIA persona-switch workflow at confirmation before navigating", async () => {
    const input = baseInput();
    const setPendingConfirmation = vi.fn();
    const result = await executeVoiceResponse({
      ...input,
      activePersona: "investor",
      setPendingConfirmation,
      response: {
        kind: "speak_only",
        message: "Opening RIA workspace.",
        speak: true,
      },
      groundedPlan: {
        status: "resolved",
        actionId: "route.ria_home",
        actionLabel: "Open RIA Home",
        destructive: false,
        message: null,
        resolutionSource: "canonical",
        execution: {
          mode: "navigate_then_action",
          steps: [
            {
              type: "tool_call",
              toolCall: {
                tool_name: "switch_persona",
                args: {
                  target_persona: "ria",
                },
              },
              reason: "workflow_persona_switch",
              confirmationRequired: true,
              settlementTarget: {
                persona: "ria",
              },
            },
            {
              type: "navigate",
              href: "/ria",
              reason: "workflow_route_switch",
              settlementTarget: {
                route: "/ria",
                screen: "ria_home",
              },
            },
          ],
        },
      },
    });

    expect(setPendingConfirmation).toHaveBeenCalledWith(
      expect.objectContaining({
        kind: "switch_persona",
        toolCall: {
          tool_name: "switch_persona",
          args: {
            target_persona: "ria",
          },
        },
        prompt: "Switch to the RIA workspace?",
        transcript: "switch to ria",
      })
    );
    expect(input.router.push).not.toHaveBeenCalled();
    expect(dispatchVoiceToolCallMock).not.toHaveBeenCalled();
    expect(result).toEqual({
      shortTermMemoryWrite: false,
      toolName: null,
      ticker: null,
      responseKind: "speak_only",
      actionResult: {
        status: "noop",
        actionId: "route.ria_home",
        routeBefore: "/kai/dashboard",
        routeAfter: null,
        screenBefore: "dashboard",
        screenAfter: null,
        resultSummary: "Waiting for confirmation before running the next Kai action step.",
        data: {
          executionMode: "navigate_then_action",
          toolName: "switch_persona",
        },
      },
    });
  });

  it("executes hidden action plans as navigation followed by tool dispatch", async () => {
    const input = baseInput();
    const result = await executeVoiceResponse({
      ...input,
      response: {
        kind: "execute",
        message: "Resuming active analysis.",
        speak: true,
        tool_call: {
          tool_name: "resume_active_analysis",
          args: {},
        },
      },
      groundedPlan: {
        status: "resolved",
        actionId: "analysis.resume_active",
        actionLabel: "Resume Active Analysis Run",
        destructive: false,
        message: null,
        resolutionSource: "canonical",
        execution: {
          mode: "navigate_then_action",
          steps: [
            {
              type: "navigate",
              href: "/kai/analysis",
              reason: "hidden_action_navigation_prerequisite",
            },
            {
              type: "tool_call",
              toolCall: {
                tool_name: "resume_active_analysis",
                args: {},
              },
              reason: "wired_tool_after_navigation",
            },
          ],
        },
      },
    });

    expect(input.router.push).toHaveBeenCalledWith("/kai/analysis");
    expect(dispatchVoiceToolCallMock).toHaveBeenCalledTimes(1);
    expect(input.router.push.mock.invocationCallOrder[0]).toBeLessThan(
      dispatchVoiceToolCallMock.mock.invocationCallOrder[0]
    );
    expect(result).toEqual({
      shortTermMemoryWrite: true,
      toolName: "resume_active_analysis",
      ticker: null,
      responseKind: "execute",
      actionResult: {
        status: "succeeded",
        actionId: "analysis.resume_active",
        routeBefore: "/kai/dashboard",
        routeAfter: "/kai/analysis",
        screenBefore: "dashboard",
        screenAfter: null,
        resultSummary: "Navigated to /kai/analysis.",
        data: {
          executionMode: "navigate_then_action",
          navigated: true,
          toolName: "resume_active_analysis",
        },
      },
    });
  });

  it("shows unavailable grounded action message without dispatch", async () => {
    const result = await executeVoiceResponse({
      ...baseInput(),
      response: {
        kind: "speak_only",
        message: "I can’t do that right now.",
        speak: true,
      },
      groundedPlan: {
        status: "unavailable",
        actionId: "command.optimize_legacy",
        actionLabel: "Legacy Optimize Voice Command",
        destructive: false,
        message: "I can’t do that right now.",
        resolutionSource: "response",
        execution: {
          mode: "unavailable",
          steps: [
            {
              type: "prompt",
              message: "I can’t do that right now.",
              reason: "legacy_unavailable",
            },
          ],
        },
      },
    });

    expect(dispatchVoiceToolCallMock).not.toHaveBeenCalled();
    expect(toastInfoMock).toHaveBeenCalledWith("I can’t do that right now.");
    expect(result).toEqual({
      shortTermMemoryWrite: false,
      toolName: null,
      ticker: null,
      responseKind: "speak_only",
      actionResult: {
        status: "blocked",
        actionId: "command.optimize_legacy",
        routeBefore: "/kai/dashboard",
        routeAfter: null,
        screenBefore: "dashboard",
        screenAfter: null,
        resultSummary: "I can’t do that right now.",
        data: {
          executionMode: "unavailable",
          policy: "unavailable",
        },
      },
    });
  });

  it("executes grounded navigation for canonical speak-only route intents", async () => {
    const input = baseInput();
    const result = await executeVoiceResponse({
      ...input,
      response: {
        kind: "speak_only",
        message: "Opening Gmail.",
        speak: true,
      },
      groundedPlan: {
        status: "resolved",
        actionId: "route.profile_gmail_panel",
        actionLabel: "Open Gmail Connector Panel",
        destructive: false,
        message: null,
        resolutionSource: "canonical",
        execution: {
          mode: "navigate_only",
          steps: [
            {
              type: "navigate",
              href: "/profile?panel=gmail",
              reason: "route_bound_action",
            },
          ],
        },
      },
    });

    expect(input.router.push).toHaveBeenCalledWith("/profile?panel=gmail");
    expect(dispatchVoiceToolCallMock).not.toHaveBeenCalled();
    expect(result).toEqual({
      shortTermMemoryWrite: true,
      toolName: "navigate",
      ticker: null,
      responseKind: "speak_only",
      actionResult: {
        status: "succeeded",
        actionId: "route.profile_gmail_panel",
        routeBefore: "/kai/dashboard",
        routeAfter: "/profile?panel=gmail",
        screenBefore: "dashboard",
        screenAfter: null,
        resultSummary: "Navigated to /profile?panel=gmail.",
        data: {
          executionMode: "navigate_only",
          navigated: true,
          toolName: null,
        },
      },
    });
  });

  it("does not silently execute speak-only grounded navigation without canonical resolution", async () => {
    const input = baseInput();
    const emitTelemetry = vi.fn();
    const result = await executeVoiceResponse({
      ...input,
      emitTelemetry,
      response: {
        kind: "speak_only",
        message: "Opening Gmail.",
        speak: true,
      },
      groundedPlan: {
        status: "resolved",
        actionId: "route.profile_gmail_panel",
        actionLabel: "Open Gmail Connector Panel",
        destructive: false,
        message: null,
        resolutionSource: "transcript",
        execution: {
          mode: "navigate_only",
          steps: [
            {
              type: "navigate",
              href: "/profile?panel=gmail",
              reason: "route_bound_action",
            },
          ],
        },
      },
    });

    expect(input.router.push).not.toHaveBeenCalled();
    expect(dispatchVoiceToolCallMock).not.toHaveBeenCalled();
    expect(emitTelemetry).toHaveBeenCalledWith(
      "speak_only_execution_skipped_missing_canonical",
      expect.objectContaining({
        action_id: "route.profile_gmail_panel",
        resolution_source: "transcript",
      })
    );
    expect(result).toEqual({
      shortTermMemoryWrite: false,
      toolName: null,
      ticker: null,
      responseKind: "speak_only",
      actionResult: {
        status: "noop",
        actionId: "route.profile_gmail_panel",
        routeBefore: "/kai/dashboard",
        routeAfter: null,
        screenBefore: "dashboard",
        screenAfter: null,
        resultSummary: "I couldn't complete that action from the current Kai voice plan.",
        data: {
          executionMode: "navigate_only",
          resolutionSource: "transcript",
          compatibilityFallbackRequired: true,
        },
      },
    });
  });

  it("allows explicit speak-only compatibility fallback when requested", async () => {
    const input = baseInput();
    const emitTelemetry = vi.fn();
    const result = await executeVoiceResponse({
      ...input,
      emitTelemetry,
      allowSpeakOnlyCompatibilityFallback: true,
      response: {
        kind: "speak_only",
        message: "Opening Gmail.",
        speak: true,
      },
      groundedPlan: {
        status: "resolved",
        actionId: "route.profile_gmail_panel",
        actionLabel: "Open Gmail Connector Panel",
        destructive: false,
        message: null,
        resolutionSource: "transcript",
        execution: {
          mode: "navigate_only",
          steps: [
            {
              type: "navigate",
              href: "/profile?panel=gmail",
              reason: "route_bound_action",
            },
          ],
        },
      },
    });

    expect(input.router.push).toHaveBeenCalledWith("/profile?panel=gmail");
    expect(emitTelemetry).toHaveBeenCalledWith(
      "speak_only_execution_compatibility_fallback_used",
      expect.objectContaining({
        action_id: "route.profile_gmail_panel",
        resolution_source: "transcript",
      })
    );
    expect(result.actionResult.status).toBe("succeeded");
    expect(result.actionResult.routeAfter).toBe("/profile?panel=gmail");
  });

  it("falls back to legacy execute path when grounded execution rollout flag is disabled", async () => {
    process.env.NEXT_PUBLIC_VOICE_V2_GROUNDED_ACTION_EXECUTION_ENABLED = "0";
    const input = baseInput();
    const result = await executeVoiceResponse({
      ...input,
      response: {
        kind: "execute",
        message: "Resuming active analysis.",
        speak: true,
        tool_call: {
          tool_name: "resume_active_analysis",
          args: {},
        },
      },
      groundedPlan: {
        status: "resolved",
        actionId: "analysis.resume_active",
        actionLabel: "Resume Active Analysis Run",
        destructive: false,
        message: null,
        resolutionSource: "canonical",
        execution: {
          mode: "navigate_then_action",
          steps: [
            {
              type: "navigate",
              href: "/kai/analysis",
              reason: "hidden_action_navigation_prerequisite",
            },
            {
              type: "tool_call",
              toolCall: {
                tool_name: "resume_active_analysis",
                args: {},
              },
              reason: "wired_tool_after_navigation",
            },
          ],
        },
      },
    });

    expect(input.router.push).not.toHaveBeenCalled();
    expect(dispatchVoiceToolCallMock).toHaveBeenCalledTimes(1);
    expect(result).toEqual({
      shortTermMemoryWrite: true,
      toolName: "resume_active_analysis",
      ticker: null,
      responseKind: "execute",
      actionResult: {
        status: "succeeded",
        actionId: null,
        routeBefore: "/kai/dashboard",
        routeAfter: null,
        screenBefore: "dashboard",
        screenAfter: null,
        resultSummary: "Executed the requested voice action.",
        data: undefined,
      },
    });
  });

  it("does not write short-term memory for stt_unusable clarify", async () => {
    const result = await executeVoiceResponse({
      ...baseInput(),
      response: {
        kind: "clarify",
        reason: "stt_unusable",
        message: "I couldn’t understand what you said, please repeat.",
        speak: true,
      },
    });

    expect(toastInfoMock).toHaveBeenCalledTimes(1);
    expect(result.shortTermMemoryWrite).toBe(false);
    expect(result.toolName).toBeNull();
  });

  it("suppresses duplicate notifications when the voice UI already presents the reply", async () => {
    const result = await executeVoiceResponse({
      ...baseInput(),
      suppressNotifications: true,
      response: {
        kind: "clarify",
        reason: "stt_unusable",
        message: "I couldn’t understand what you said, please repeat.",
        speak: true,
      },
    });

    expect(toastInfoMock).not.toHaveBeenCalled();
    expect(result.shortTermMemoryWrite).toBe(false);
    expect(result.toolName).toBeNull();
  });

  it("keeps ticker_ambiguous clarify in fallback flow", async () => {
    const result = await executeVoiceResponse({
      ...baseInput(),
      response: {
        kind: "clarify",
        reason: "ticker_ambiguous",
        message: "Did you mean NVDA or AMD?",
        speak: true,
      },
      groundedPlan: {
        status: "ambiguous",
        actionId: null,
        actionLabel: null,
        destructive: false,
        message: "Did you mean NVDA or AMD?",
        resolutionSource: "none",
        execution: {
          mode: "ambiguous",
          steps: [],
        },
      },
    });

    expect(dispatchVoiceToolCallMock).not.toHaveBeenCalled();
    expect(toastInfoMock).toHaveBeenCalledWith("Did you mean NVDA or AMD?");
    expect(result).toEqual({
      shortTermMemoryWrite: true,
      toolName: "clarify",
      ticker: null,
      responseKind: "clarify",
      actionResult: {
        status: "noop",
        actionId: null,
        routeBefore: "/kai/dashboard",
        routeAfter: null,
        screenBefore: "dashboard",
        screenAfter: null,
        resultSummary: "Did you mean NVDA or AMD?",
        data: {
          reason: "ticker_ambiguous",
        },
      },
    });
  });

  it("surfaces portfolio_required blocked responses without collapsing into clarify", async () => {
    const result = await executeVoiceResponse({
      ...baseInput(),
      response: {
        kind: "blocked",
        reason: "portfolio_required",
        message: "Import your portfolio before starting stock analysis.",
        speak: true,
      },
    });

    expect(dispatchVoiceToolCallMock).not.toHaveBeenCalled();
    expect(toastInfoMock).toHaveBeenCalledWith(
      "Import your portfolio before starting stock analysis."
    );
    expect(result).toEqual({
      shortTermMemoryWrite: false,
      toolName: null,
      ticker: null,
      responseKind: "blocked",
      actionResult: {
        status: "blocked",
        actionId: null,
        routeBefore: "/kai/dashboard",
        routeAfter: null,
        screenBefore: "dashboard",
        screenAfter: null,
        resultSummary: "Import your portfolio before starting stock analysis.",
        data: {
          reason: "portfolio_required",
        },
      },
    });
  });

  it("returns already_running as short-term memory eligible", async () => {
    const result = await executeVoiceResponse({
      ...baseInput(),
      response: {
        kind: "already_running",
        task: "analysis",
        ticker: "AAPL",
        run_id: "run_1",
        message: "Analysis is already running for AAPL.",
        speak: true,
      },
    });

    expect(result).toEqual({
      shortTermMemoryWrite: true,
      toolName: "already_running",
      ticker: "AAPL",
      responseKind: "already_running",
      actionResult: {
        status: "noop",
        actionId: null,
        routeBefore: "/kai/dashboard",
        routeAfter: null,
        screenBefore: "dashboard",
        screenAfter: null,
        resultSummary: "Analysis is already running for AAPL.",
        data: {
          task: "analysis",
          ticker: "AAPL",
          runId: "run_1",
        },
      },
    });
  });

  it("treats background_started as non-blocking and memory-eligible", async () => {
    const result = await executeVoiceResponse({
      ...baseInput(),
      response: {
        kind: "background_started",
        task: "analysis",
        ticker: "MSFT",
        run_id: "run_2",
        message: "Started analysis for MSFT in background.",
        speak: true,
      },
    });

    expect(toastSuccessMock).toHaveBeenCalledTimes(1);
    expect(result).toEqual({
      shortTermMemoryWrite: true,
      toolName: "background_started",
      ticker: "MSFT",
      responseKind: "background_started",
      actionResult: {
        status: "started",
        actionId: null,
        routeBefore: "/kai/dashboard",
        routeAfter: null,
        screenBefore: "dashboard",
        screenAfter: null,
        resultSummary: "Started analysis for MSFT in background.",
        data: {
          task: "analysis",
          ticker: "MSFT",
          runId: "run_2",
        },
      },
    });
  });

  it("suppresses background-started success toasts when voice compact UI is active", async () => {
    const result = await executeVoiceResponse({
      ...baseInput(),
      suppressNotifications: true,
      response: {
        kind: "background_started",
        task: "analysis",
        ticker: "MSFT",
        run_id: "run_2",
        message: "Started analysis for MSFT in background.",
        speak: true,
      },
    });

    expect(toastSuccessMock).not.toHaveBeenCalled();
    expect(result).toEqual({
      shortTermMemoryWrite: true,
      toolName: "background_started",
      ticker: "MSFT",
      responseKind: "background_started",
      actionResult: {
        status: "started",
        actionId: null,
        routeBefore: "/kai/dashboard",
        routeAfter: null,
        screenBefore: "dashboard",
        screenAfter: null,
        resultSummary: "Started analysis for MSFT in background.",
        data: {
          task: "analysis",
          ticker: "MSFT",
          runId: "run_2",
        },
      },
    });
  });

  it("does not mark grounded execution successful when tool dispatch is blocked", async () => {
    dispatchVoiceToolCallMock.mockResolvedValueOnce({
      status: "blocked",
      toolName: "resume_active_analysis",
      reason: "missing_vault_token",
    });
    const emitTelemetry = vi.fn();

    const result = await executeVoiceResponse({
      ...baseInput(),
      turnId: "vturn_1",
      responseId: "vrsp_1",
      emitTelemetry,
      response: {
        kind: "execute",
        message: "Resuming active analysis.",
        speak: true,
        tool_call: {
          tool_name: "resume_active_analysis",
          args: {},
        },
      },
      groundedPlan: {
        status: "resolved",
        actionId: "analysis.resume_active",
        actionLabel: "Resume Active Analysis Run",
        destructive: false,
        message: null,
        resolutionSource: "canonical",
        execution: {
          mode: "navigate_then_action",
          steps: [
            {
              type: "navigate",
              href: "/kai/analysis",
              reason: "hidden_action_navigation_prerequisite",
            },
            {
              type: "tool_call",
              toolCall: {
                tool_name: "resume_active_analysis",
                args: {},
              },
              reason: "wired_tool_after_navigation",
            },
          ],
        },
      },
    });

    expect(result).toEqual({
      shortTermMemoryWrite: false,
      toolName: null,
      ticker: null,
      responseKind: "execute",
      actionResult: {
        status: "blocked",
        actionId: "analysis.resume_active",
        routeBefore: "/kai/dashboard",
        routeAfter: "/kai/analysis",
        screenBefore: "dashboard",
        screenAfter: null,
        resultSummary: "The requested grounded voice action was blocked.",
        data: {
          executionMode: "navigate_then_action",
          navigated: true,
        },
      },
    });
    expect(emitTelemetry).not.toHaveBeenCalledWith(
      "grounded_execution_success",
      expect.anything()
    );
    expect(logVoiceMetricMock).not.toHaveBeenCalledWith(
      expect.objectContaining({
        metric: "execution_grounded_execution_success",
      })
    );
  });

  it("does not write legacy execute memory when dispatch returns invalid", async () => {
    dispatchVoiceToolCallMock.mockResolvedValueOnce({
      status: "invalid",
      toolName: "execute_kai_command",
      reason: "missing_symbol",
    });
    const emitTelemetry = vi.fn();

    const result = await executeVoiceResponse({
      ...baseInput(),
      turnId: "vturn_2",
      responseId: "vrsp_2",
      emitTelemetry,
      response: {
        kind: "execute",
        message: "Starting analysis.",
        speak: true,
        tool_call: {
          tool_name: "execute_kai_command",
          args: {
            command: "analyze",
            params: {},
          },
        },
      },
    });

    expect(result).toEqual({
      shortTermMemoryWrite: false,
      toolName: null,
      ticker: null,
      responseKind: "execute",
      actionResult: {
        status: "invalid",
        actionId: null,
        routeBefore: "/kai/dashboard",
        routeAfter: null,
        screenBefore: "dashboard",
        screenAfter: null,
        resultSummary: "The requested voice action was invalid.",
        data: undefined,
      },
    });
    expect(emitTelemetry).not.toHaveBeenCalledWith(
      "legacy_execute_success",
      expect.anything()
    );
  });
});
