import assert from "node:assert/strict";
import { test } from "bun:test";

import { composerFromState, workbenchReducer } from "./workbench-reducer";
import { messageFromAgentResponse } from "./conversation";
import type { ChatMessage } from "../types/domain";
import type { WorkbenchState } from "./workbench-state";

test("composer stays unlocked and routes running turn input to steer", () => {
  const state = baseState({
    workspace: workspace("cnv_active", "trc_active"),
    messages: [agentMessage({ conversationId: "cnv_active", traceId: "trc_active", status: "running" })],
    chatPending: true
  });

  const composer = composerFromState(state, "cnv_active");

  assert.equal(composer.disabled, false);
  assert.equal(composer.submitMode, "steer");
  assert.equal(composer.route, "/v1/agent/chat/steer");
  assert.equal(composer.targetTraceId, "trc_active");
  assert.equal(composer.conversationId, "cnv_active");
});

test("composer does not carry a running trace into another session", () => {
  const state = baseState({
    workspace: workspace("cnv_other", "trc_active"),
    currentRequest: { traceId: "trc_active", conversationId: "cnv_active", sessionId: "ses_active", threadId: "thread_active", status: "running" },
    messages: [agentMessage({ conversationId: "cnv_other", traceId: "trc_other_done", status: "completed" })],
    chatPending: true
  });

  const composer = composerFromState(state, "cnv_other");

  assert.equal(composer.disabled, false);
  assert.equal(composer.submitMode, "turn");
  assert.equal(composer.targetTraceId, null);
});

test("conversation selection scopes chatPending to the selected session messages", () => {
  const state = baseState({
    workspace: workspace("cnv_active", "trc_active"),
    currentRequest: { traceId: "trc_active", conversationId: "cnv_active", sessionId: "ses_active", threadId: "thread_active", status: "running" },
    messages: [agentMessage({ conversationId: "cnv_active", traceId: "trc_active", status: "running" })],
    chatPending: true
  });

  const selected = workbenchReducer(state, {
    type: "conversation:select",
    conversation: {
      conversationId: "cnv_done",
      sessionId: "ses_done",
      messages: [agentMessage({ conversationId: "cnv_done", traceId: "trc_done", status: "completed" })]
    },
    workspace: workspace("cnv_done", null)
  });

  assert.equal(selected.chatPending, false);
  assert.equal(selected.currentRequest, null);
  assert.equal(composerFromState(selected, "cnv_done").submitMode, "turn");
});

test("steer inserts the user guidance before the running agent message", () => {
  const running = agentMessage({ conversationId: "cnv_active", traceId: "trc_active", status: "running" });
  const state = baseState({
    workspace: workspace("cnv_active", "trc_active"),
    messages: [agentMessage({ conversationId: "cnv_active", traceId: "trc_done", status: "completed" }), running],
    chatPending: true
  });
  const steer: ChatMessage = {
    id: "msg_steer",
    role: "user",
    title: "用户引导",
    text: "补充 steer",
    status: "sent",
    createdAt: "2026-06-05T00:00:00.000Z",
    traceId: "trc_steer_active",
    conversationId: "cnv_active",
    sessionId: "ses_active",
    threadId: "thread_active"
  };

  const next = workbenchReducer(state, {
    type: "message:steer",
    user: steer,
    targetMessageId: running.id,
    request: { traceId: "trc_active", conversationId: "cnv_active", sessionId: "ses_active", threadId: "thread_active" }
  });

  assert.deepEqual(next.messages.map((message) => message.id), ["msg_trc_done", "msg_steer", "msg_trc_active"]);
  assert.equal(next.currentRequest?.traceId, "trc_active");
  assert.equal(next.messages.filter((message) => message.role === "agent" && message.traceId === "trc_active").length, 1);
});

test("pending still appends a user and agent placeholder when no running card is visible", () => {
  const state = baseState({ workspace: workspace("cnv_active", "trc_active"), messages: [], chatPending: false });
  const user: ChatMessage = {
    id: "msg_steer",
    role: "user",
    title: "用户引导",
    text: "补充 steer",
    status: "sent",
    createdAt: "2026-06-05T00:00:00.000Z",
    traceId: "trc_steer_active",
    conversationId: "cnv_active",
    sessionId: "ses_active",
    threadId: "thread_active"
  };
  const pending = agentMessage({ conversationId: "cnv_active", traceId: "trc_active", status: "running" });

  const next = workbenchReducer(state, {
    type: "message:pending",
    user,
    pending,
    request: { traceId: "trc_active", conversationId: "cnv_active", sessionId: "ses_active", threadId: "thread_active" }
  });

  assert.deepEqual(next.messages.map((message) => message.id), ["msg_steer", "msg_trc_active"]);
  assert.equal(next.currentRequest?.traceId, "trc_active");
});

test("conversation selection restores steer mode when switching back to a running session", () => {
  const state = baseState({ workspace: workspace("cnv_done", null), messages: [], chatPending: false });

  const selected = workbenchReducer(state, {
    type: "conversation:select",
    conversation: {
      conversationId: "cnv_active",
      sessionId: "ses_active",
      threadId: "thread_active",
      messages: [agentMessage({ conversationId: "cnv_active", traceId: "trc_active", status: "running" })]
    },
    workspace: workspace("cnv_active", "trc_active")
  });

  const composer = composerFromState(selected, "cnv_active");
  assert.equal(selected.chatPending, true);
  assert.equal(composer.submitMode, "steer");
  assert.equal(composer.targetTraceId, "trc_active");
});

test("steer submit completion keeps the target turn steerable", () => {
  const state = baseState({
    workspace: workspace("cnv_active", "trc_active"),
    currentRequest: { traceId: "trc_active", conversationId: "cnv_active", sessionId: "ses_active", threadId: "thread_active", status: "running" },
    messages: [agentMessage({ conversationId: "cnv_active", traceId: "trc_active", status: "running" })],
    chatPending: true
  });

  const next = workbenchReducer(state, { type: "chat:steer-done" });
  const composer = composerFromState(next, "cnv_active");

  assert.equal(next.chatPending, false);
  assert.equal(next.currentRequest?.status, "running");
  assert.equal(composer.submitMode, "steer");
  assert.equal(composer.targetTraceId, "trc_active");
});

test("composer routes to steer from workspace active status even before running message is restored", () => {
  const state = baseState({
    workspace: workspace("cnv_active", "trc_active"),
    messages: [],
    chatPending: false
  });

  const composer = composerFromState(state, "cnv_active");

  assert.equal(composer.disabled, false);
  assert.equal(composer.submitMode, "steer");
  assert.equal(composer.targetTraceId, "trc_active");
});

test("composer disables only when no session or workspace exists", () => {
  const composer = composerFromState(baseState({ workspace: null, messages: [] }), null);
  assert.equal(composer.disabled, true);
  assert.equal(composer.disabledReason, "session_required");
  assert.equal(composer.submitMode, "turn");
});

test("completed AgentRun trace uses terminal assistant text instead of still-processing fallback", () => {
  const pending = agentMessage({ conversationId: "cnv_done", traceId: "trc_done", status: "running" });
  pending.text = "正在处理这次 Code Agent 请求；复杂问题可能需要几分钟。";

  const completed = messageFromAgentResponse(pending.id, pending, {
    status: "completed",
    traceId: "trc_done",
    events: [
      { label: "agentrun:assistant:message", type: "assistant", status: "completed", final: true, replyAuthority: true, message: "最终回答已经生成。" },
      { label: "agentrun:result:completed", type: "result", status: "completed", terminal: true, message: "AgentRun result is ready for HWLAB short-connection polling." }
    ]
  });

  assert.equal(completed.status, "completed");
  assert.equal(completed.text, "最终回答已经生成。");
  assert.equal(completed.text.includes("仍在处理"), false);
});

function baseState(patch: Partial<WorkbenchState>): WorkbenchState {
  return {
    workspace: workspace("cnv_active", null),
    conversations: [],
    messages: [],
    providerProfile: "deepseek",
    codeAgentTimeoutMs: 180_000,
    gatewayShellTimeoutMs: 120_000,
    live: null,
    codeAgentAvailability: null,
    loading: false,
    chatPending: false,
    currentRequest: null,
    error: null,
    ...patch
  };
}

function workspace(selectedConversationId: string, activeTraceId: string | null): WorkbenchState["workspace"] {
  return {
    workspaceId: "wsp_test",
    revision: 1,
    selectedConversationId,
    selectedAgentSessionId: "ses_test",
    activeTraceId,
    workspace: {
      selectedConversationId,
      selectedAgentSessionId: "ses_test",
      threadId: "thread_test",
      activeTraceId,
      sessionStatus: activeTraceId ? "running" : "completed"
    }
  };
}

function agentMessage(input: { conversationId: string; traceId: string; status: ChatMessage["status"] }): ChatMessage {
  return {
    id: `msg_${input.traceId}`,
    role: "agent",
    title: "Code Agent",
    text: "message",
    status: input.status,
    createdAt: "2026-06-04T00:00:00.000Z",
    traceId: input.traceId,
    conversationId: input.conversationId,
    sessionId: input.conversationId.replace("cnv", "ses"),
    threadId: input.conversationId.replace("cnv", "thread")
  };
}
