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

import type { PendingExecApproval } from "@/features/agents/approvals/types";
import {
  resolveExecApprovalEventEffects,
  resolveExecApprovalFollowUpIntent,
  shouldTreatExecApprovalResolveErrorAsUnknownId,
} from "@/features/agents/approvals/execApprovalLifecycleWorkflow";
import type { AgentState } from "@/features/agents/state/store";
import { GatewayResponseError, type EventFrame } from "@/lib/gateway/GatewayClient";

const createAgent = (agentId: string, sessionKey: string): AgentState => ({
  agentId,
  name: agentId,
  sessionKey,
  status: "idle",
  sessionCreated: true,
  awaitingUserInput: false,
  hasUnseenActivity: false,
  outputLines: [],
  lastResult: null,
  lastDiff: null,
  runId: null,
  runStartedAt: null,
  streamText: null,
  thinkingTrace: null,
  latestOverride: null,
  latestOverrideKind: null,
  lastAssistantMessageAt: null,
  lastActivityAt: null,
  latestPreview: null,
  lastUserMessage: null,
  draft: "",
  sessionSettingsSynced: true,
  historyLoadedAt: null,
  historyFetchLimit: null,
  historyFetchedCount: null,
  historyMaybeTruncated: false,
  toolCallingEnabled: true,
  showThinkingTraces: true,
  model: "openai/gpt-5",
  thinkingLevel: "medium",
  avatarSeed: agentId,
  avatarUrl: null,
});

const createApproval = (params?: Partial<PendingExecApproval>): PendingExecApproval => ({
  id: "approval-1",
  agentId: "agent-1",
  sessionKey: "agent:agent-1:main",
  command: "npm test",
  cwd: "/repo",
  host: "gateway",
  security: "allowlist",
  ask: "always",
  resolvedPath: "/usr/bin/npm",
  createdAtMs: 1,
  expiresAtMs: 2,
  resolving: false,
  error: null,
  ...params,
});

describe("execApprovalLifecycleWorkflow", () => {
  it("maps requested approval into scoped or unscoped upsert effect", () => {
    const agents = [createAgent("agent-1", "agent:agent-1:main")];
    const scopedEvent: EventFrame = {
      type: "event",
      event: "exec.approval.requested",
      payload: {
        id: "approval-scoped",
        request: {
          command: "npm run test",
          cwd: "/repo",
          host: "gateway",
          security: "allowlist",
          ask: "always",
          agentId: "agent-1",
          resolvedPath: "/usr/bin/npm",
          sessionKey: "agent:agent-1:main",
        },
        createdAtMs: 123,
        expiresAtMs: 456,
      },
    };
    const unscopedEvent: EventFrame = {
      type: "event",
      event: "exec.approval.requested",
      payload: {
        id: "approval-unscoped",
        request: {
          command: "npm run lint",
          cwd: "/repo",
          host: "gateway",
          security: "allowlist",
          ask: "always",
          agentId: null,
          resolvedPath: "/usr/bin/npm",
          sessionKey: "agent:missing:main",
        },
        createdAtMs: 222,
        expiresAtMs: 333,
      },
    };

    const scopedEffects = resolveExecApprovalEventEffects({
      event: scopedEvent,
      agents,
    });
    expect(scopedEffects?.scopedUpserts.map((entry) => entry.agentId)).toEqual(["agent-1"]);
    expect(scopedEffects?.unscopedUpserts).toEqual([]);
    expect(scopedEffects?.markActivityAgentIds).toEqual(["agent-1"]);

    const unscopedEffects = resolveExecApprovalEventEffects({
      event: unscopedEvent,
      agents,
    });
    expect(unscopedEffects?.scopedUpserts).toEqual([]);
    expect(unscopedEffects?.unscopedUpserts).toHaveLength(1);
    expect(unscopedEffects?.markActivityAgentIds).toEqual([]);
  });

  it("maps resolved approval event into remove effects", () => {
    const event: EventFrame = {
      type: "event",
      event: "exec.approval.resolved",
      payload: {
        id: "approval-1",
        decision: "allow-once",
        resolvedBy: "studio",
        ts: 999,
      },
    };

    const effects = resolveExecApprovalEventEffects({
      event,
      agents: [createAgent("agent-1", "agent:agent-1:main")],
    });

    expect(effects).toEqual({
      scopedUpserts: [],
      unscopedUpserts: [],
      removals: ["approval-1"],
      markActivityAgentIds: [],
    });
  });

  it("returns follow-up intent only for allow decisions", () => {
    const agents = [createAgent("agent-1", "agent:agent-1:main")];
    const approval = createApproval({ agentId: null, sessionKey: "agent:agent-1:main" });

    expect(
      resolveExecApprovalFollowUpIntent({
        decision: "allow-once",
        approval,
        agents,
        followUpMessage: "approval granted",
      })
    ).toEqual({
      shouldSend: true,
      agentId: "agent-1",
      sessionKey: "agent:agent-1:main",
      message: "approval granted",
    });

    expect(
      resolveExecApprovalFollowUpIntent({
        decision: "deny",
        approval,
        agents,
        followUpMessage: "approval granted",
      })
    ).toEqual({
      shouldSend: false,
      agentId: null,
      sessionKey: null,
      message: null,
    });
  });

  it("maps unknown approval id gateway error to local removal intent", () => {
    expect(
      shouldTreatExecApprovalResolveErrorAsUnknownId(
        new GatewayResponseError({
          code: "INVALID_REQUEST",
          message: "Unknown approval id",
        })
      )
    ).toBe(true);
    expect(
      shouldTreatExecApprovalResolveErrorAsUnknownId(
        new GatewayResponseError({
          code: "INVALID_REQUEST",
          message: "approval denied by policy",
        })
      )
    ).toBe(false);
  });
});
