/**
 * F35.1 — local model server launch state in {@link useAgents}.
 *
 * Drives the hook behind the real {@link DIProvider} with an in-memory
 * {@link MockSystemGateway} to emit the two F35 domain events, and verifies they
 * fold into `modelServerStatusByServer` (keyed by serverId) and
 * `launchFailureByAgent` (keyed by agentId).
 *
 * Cases:
 *  - `modelServerStatusChanged` → status by server id (probing → starting → ready)
 *  - `agentLaunchFailed` → actionable failure by agent id
 *  - `agentLaunched` clears a prior launch failure for that agent
 *  - a `failed` status is retained per server (independent of the agent map)
 */

import { describe, it, expect } from "vitest";
import { act, renderHook } from "@testing-library/react";

import type { DomainEvent } from "@/domain";
import type { Gateways } from "@/ports";
import { MockSystemGateway } from "@/adapters/mock";
import { DIProvider } from "@/app/di";
import { useAgents } from "./useAgents";

const PROJECT_ID = "proj-modelserver-001";
const AGENT = "agent-oc";
const SERVER = "srv-42";

function setup() {
  const system = new MockSystemGateway();
  const agent = {
    listAgents: async () => [],
    listLiveAgents: async () => [],
  };
  const profile = { listProfiles: async () => [] };
  const gateways = { system, agent, profile } as unknown as Gateways;
  const wrapper = ({ children }: { children: React.ReactNode }) => (
    <DIProvider gateways={gateways}>{children}</DIProvider>
  );
  const view = renderHook(() => useAgents(PROJECT_ID), { wrapper });
  return { system, view };
}

async function emit(system: MockSystemGateway, event: DomainEvent) {
  await act(async () => {
    system.emit(event);
    await Promise.resolve();
  });
}

describe("useAgents — local model server launch state (F35.1)", () => {
  it("folds modelServerStatusChanged into modelServerStatusByServer by serverId", async () => {
    const { system, view } = setup();

    await emit(system, {
      type: "modelServerStatusChanged",
      serverId: SERVER,
      status: { state: "probing" },
    });
    expect(view.result.current.modelServerStatusByServer[SERVER]).toEqual({
      state: "probing",
    });

    await emit(system, {
      type: "modelServerStatusChanged",
      serverId: SERVER,
      status: { state: "starting" },
    });
    await emit(system, {
      type: "modelServerStatusChanged",
      serverId: SERVER,
      status: { state: "ready", reused: true },
    });
    expect(view.result.current.modelServerStatusByServer[SERVER]).toEqual({
      state: "ready",
      reused: true,
    });
  });

  it("folds agentLaunchFailed into launchFailureByAgent by agentId", async () => {
    const { system, view } = setup();
    await emit(system, {
      type: "agentLaunchFailed",
      agentId: AGENT,
      cause: "model_server",
      code: "SERVER_UNREACHABLE",
      message: "llama-server did not become ready on :8080",
    });
    expect(view.result.current.launchFailureByAgent[AGENT]).toEqual({
      cause: "model_server",
      code: "SERVER_UNREACHABLE",
      message: "llama-server did not become ready on :8080",
    });
  });

  it("a successful agentLaunched clears the agent's prior launch failure", async () => {
    const { system, view } = setup();
    await emit(system, {
      type: "agentLaunchFailed",
      agentId: AGENT,
      cause: "model_server",
      code: "SERVER_UNREACHABLE",
      message: "boom",
    });
    expect(view.result.current.launchFailureByAgent[AGENT]).toBeTruthy();

    await emit(system, {
      type: "agentLaunched",
      agentId: AGENT,
      sessionId: "sess-1",
    });
    expect(view.result.current.launchFailureByAgent[AGENT]).toBeUndefined();
  });

  it("keeps a per-server failed status independent of the agent failure map", async () => {
    const { system, view } = setup();
    await emit(system, {
      type: "modelServerStatusChanged",
      serverId: SERVER,
      status: { state: "failed", code: "SPAWN", message: "binary not found" },
    });
    expect(view.result.current.modelServerStatusByServer[SERVER]).toEqual({
      state: "failed",
      code: "SPAWN",
      message: "binary not found",
    });
    expect(view.result.current.launchFailureByAgent[AGENT]).toBeUndefined();
  });
});
