/**
 * E2E Test: Killswitch
 *
 * Tests the emergency stop mechanism for terminating agents.
 * Verifies single agent kill, kill-all, and cleanup operations.
 *
 * Covers issue #227 (ORCH-126)
 */
import { describe, it, expect, beforeEach, vi } from "vitest";
import { KillswitchService } from "../../src/killswitch/killswitch.service";
import { AgentSpawnerService } from "../../src/spawner/agent-spawner.service";
import { AgentsController } from "../../src/api/agents/agents.controller";
import { QueueService } from "../../src/queue/queue.service";
import { AgentLifecycleService } from "../../src/spawner/agent-lifecycle.service";
import { ConfigService } from "@nestjs/config";

describe("E2E: Killswitch", () => {
  let controller: AgentsController;
  let spawnerService: AgentSpawnerService;
  let killswitchService: KillswitchService;

  const mockConfigService = {
    get: vi.fn((key: string, defaultValue?: unknown) => {
      const config: Record<string, unknown> = {
        "orchestrator.claude.apiKey": "test-api-key",
      };
      return config[key] ?? defaultValue;
    }),
  };

  beforeEach(() => {
    vi.clearAllMocks();

    spawnerService = new AgentSpawnerService(mockConfigService as unknown as ConfigService);

    killswitchService = {
      killAgent: vi.fn().mockResolvedValue(undefined),
      killAllAgents: vi.fn().mockResolvedValue({
        total: 3,
        killed: 3,
        failed: 0,
      }),
    } as unknown as KillswitchService;

    const queueService = {
      addTask: vi.fn().mockResolvedValue(undefined),
    } as unknown as QueueService;

    const lifecycleService = {
      getAgentLifecycleState: vi.fn(),
    } as unknown as AgentLifecycleService;

    controller = new AgentsController(
      queueService,
      spawnerService,
      lifecycleService,
      killswitchService
    );
  });

  describe("Single agent kill", () => {
    it("should kill a single agent by ID", async () => {
      // Spawn an agent first
      const spawnResult = await controller.spawn({
        taskId: "kill-test-001",
        agentType: "worker",
        context: {
          repository: "https://git.example.com/repo.git",
          branch: "main",
          workItems: ["US-001"],
        },
      });

      // Kill the agent
      const result = await controller.killAgent(spawnResult.agentId);

      expect(result.message).toContain("killed successfully");
      expect(killswitchService.killAgent).toHaveBeenCalledWith(spawnResult.agentId);
    });

    it("should handle kill of non-existent agent gracefully", async () => {
      (killswitchService.killAgent as ReturnType<typeof vi.fn>).mockRejectedValue(
        new Error("Agent not found")
      );

      await expect(controller.killAgent("non-existent")).rejects.toThrow("Agent not found");
    });
  });

  describe("Kill all agents", () => {
    it("should kill all active agents", async () => {
      // Spawn multiple agents to verify they exist before kill-all
      const spawned = [];
      for (let i = 0; i < 3; i++) {
        const result = await controller.spawn({
          taskId: `kill-all-test-${String(i)}`,
          agentType: "worker",
          context: {
            repository: "https://git.example.com/repo.git",
            branch: "main",
            workItems: [`US-${String(i)}`],
          },
        });
        spawned.push(result);
      }

      // Verify agents were spawned
      expect(spawnerService.listAgentSessions()).toHaveLength(3);

      // Kill all (mock returns hardcoded result matching spawn count)
      const result = await controller.killAllAgents();

      expect(result.total).toBe(3);
      expect(result.killed).toBe(3);
      expect(result.failed).toBe(0);
      expect(killswitchService.killAllAgents).toHaveBeenCalled();
    });

    it("should report partial failures in kill-all", async () => {
      (killswitchService.killAllAgents as ReturnType<typeof vi.fn>).mockResolvedValue({
        total: 3,
        killed: 2,
        failed: 1,
        errors: ["Agent abc123 unresponsive"],
      });

      const result = await controller.killAllAgents();

      expect(result.total).toBe(3);
      expect(result.killed).toBe(2);
      expect(result.failed).toBe(1);
      expect(result.errors).toContain("Agent abc123 unresponsive");
    });
  });

  describe("Kill during lifecycle states", () => {
    it("should be able to kill agent in spawning state", async () => {
      const spawnResult = await controller.spawn({
        taskId: "kill-spawning-test",
        agentType: "worker",
        context: {
          repository: "https://git.example.com/repo.git",
          branch: "main",
          workItems: ["US-001"],
        },
      });

      // Verify agent is spawning
      const agents = spawnerService.listAgentSessions();
      const agent = agents.find((a) => a.agentId === spawnResult.agentId);
      expect(agent?.state).toBe("spawning");

      // Kill should succeed even in spawning state
      const result = await controller.killAgent(spawnResult.agentId);
      expect(result.message).toContain("killed successfully");
    });
  });
});
