import { parseAgentOutput } from "./parse-agent-output";

function chunk(events: object[]): string {
  return events.map((e) => JSON.stringify(e)).join("\n");
}

describe("parseAgentOutput", () => {
  // 1. toolInput is set on tool_call messages
  it("sets meta.toolInput on tool_call messages", () => {
    const input = chunk([
      {
        type: "assistant",
        message: {
          content: [
            {
              type: "tool_use",
              id: "tu1",
              name: "Read",
              input: { file_path: "/foo.ts" },
            },
          ],
        },
      },
    ]);
    const messages = parseAgentOutput(input);
    const toolCall = messages.find((m) => m.type === "tool_call");
    expect(toolCall).toBeDefined();
    expect(toolCall!.meta?.toolInput).toEqual({ file_path: "/foo.ts" });
  });

  // 2. tool_result with tool_use_id gets meta.toolName and meta.toolInput from registry
  it("correlates tool_result to its tool_use via registry", () => {
    const input = chunk([
      {
        type: "assistant",
        message: {
          content: [
            {
              type: "tool_use",
              id: "tu1",
              name: "Read",
              input: { file_path: "/foo.ts" },
            },
          ],
        },
      },
      {
        type: "user",
        message: {
          content: [
            {
              type: "tool_result",
              tool_use_id: "tu1",
              content: "file contents",
            },
          ],
        },
      },
    ]);
    const messages = parseAgentOutput(input);
    const toolResult = messages.find((m) => m.type === "tool_result");
    expect(toolResult).toBeDefined();
    expect(toolResult!.meta?.toolName).toBe("Read");
    expect(toolResult!.meta?.toolInput).toEqual({ file_path: "/foo.ts" });
  });

  // 3. tool_result with no matching registry entry has no meta.toolName
  it("tool_result with unknown tool_use_id has no meta.toolName", () => {
    const input = chunk([
      {
        type: "user",
        message: {
          content: [
            {
              type: "tool_result",
              tool_use_id: "unknown-id",
              content: "output",
            },
          ],
        },
      },
    ]);
    const messages = parseAgentOutput(input);
    const toolResult = messages.find((m) => m.type === "tool_result");
    expect(toolResult).toBeDefined();
    expect(toolResult!.meta?.toolName).toBeUndefined();
  });

  // 4. tool_result with is_error: true produces type: "error" and meta.isError: true
  it("tool_result with is_error: true produces error message", () => {
    const input = chunk([
      {
        type: "user",
        message: {
          content: [
            {
              type: "tool_result",
              tool_use_id: "tu1",
              is_error: true,
              content: "something went wrong",
            },
          ],
        },
      },
    ]);
    const messages = parseAgentOutput(input);
    const errorMsg = messages.find((m) => m.content === "something went wrong");
    expect(errorMsg).toBeDefined();
    expect(errorMsg!.type).toBe("error");
    expect(errorMsg!.meta?.isError).toBe(true);
  });

  // 5. tool_result from a Task tool_use gets correct meta.toolName and meta.toolInput
  it("tool_result from Task tool_use has correct meta", () => {
    const taskInput = {
      subagent_type: "Explore",
      description: "find files",
      prompt: "search for *.ts",
    };
    const input = chunk([
      {
        type: "assistant",
        message: {
          content: [
            {
              type: "tool_use",
              id: "tu2",
              name: "Task",
              input: taskInput,
            },
          ],
        },
      },
      {
        type: "user",
        message: {
          content: [
            {
              type: "tool_result",
              tool_use_id: "tu2",
              content: "found 10 files",
            },
          ],
        },
      },
    ]);
    const messages = parseAgentOutput(input);
    const toolResult = messages.find((m) => m.type === "tool_result");
    expect(toolResult).toBeDefined();
    expect(toolResult!.meta?.toolName).toBe("Task");
    expect(toolResult!.meta?.toolInput).toEqual(taskInput);
  });

  // 6. Unknown top-level event type produces a system message
  it("unknown top-level event type produces system message", () => {
    const input = chunk([{ type: "future_event_type", data: {} }]);
    const messages = parseAgentOutput(input);
    expect(messages).toHaveLength(1);
    expect(messages[0].type).toBe("system");
    expect(messages[0].content).toBe("[unknown event: future_event_type]");
  });

  // 7. Unknown assistant content block type produces a system message
  it("unknown assistant content block type produces system message", () => {
    const input = chunk([
      {
        type: "assistant",
        message: {
          content: [{ type: "image", data: "base64..." }],
        },
      },
    ]);
    const messages = parseAgentOutput(input);
    expect(messages).toHaveLength(1);
    expect(messages[0].type).toBe("system");
    expect(messages[0].content).toBe("[unsupported content block: image]");
  });

  // 8. Previously passing behavior unchanged
  describe("previously passing behavior", () => {
    it("system event with session_id produces system message", () => {
      const input = chunk([{ type: "system", session_id: "sess-123" }]);
      const messages = parseAgentOutput(input);
      expect(messages).toHaveLength(1);
      expect(messages[0].type).toBe("system");
      expect(messages[0].content).toBe("Session started: sess-123");
    });

    it("assistant text block produces text message", () => {
      const input = chunk([
        {
          type: "assistant",
          message: {
            content: [{ type: "text", text: "Hello, world!" }],
          },
        },
      ]);
      const messages = parseAgentOutput(input);
      expect(messages).toHaveLength(1);
      expect(messages[0].type).toBe("text");
      expect(messages[0].content).toBe("Hello, world!");
    });

    it("assistant tool_use block produces tool_call message with meta.toolName", () => {
      const input = chunk([
        {
          type: "assistant",
          message: {
            content: [
              {
                type: "tool_use",
                id: "tu1",
                name: "Bash",
                input: { command: "ls -la", description: "list files" },
              },
            ],
          },
        },
      ]);
      const messages = parseAgentOutput(input);
      const toolCall = messages.find((m) => m.type === "tool_call");
      expect(toolCall).toBeDefined();
      expect(toolCall!.meta?.toolName).toBe("Bash");
    });

    it("result event with is_error: false produces session_end", () => {
      const input = chunk([
        {
          type: "result",
          is_error: false,
          total_cost_usd: 0.01,
          duration_ms: 5000,
        },
      ]);
      const messages = parseAgentOutput(input);
      expect(messages).toHaveLength(1);
      expect(messages[0].type).toBe("session_end");
      expect(messages[0].content).toBe("Session completed");
    });

    it("result event with is_error: true produces session_end with meta.isError", () => {
      const input = chunk([
        {
          type: "result",
          is_error: true,
          total_cost_usd: 0.01,
          duration_ms: 5000,
        },
      ]);
      const messages = parseAgentOutput(input);
      expect(messages).toHaveLength(1);
      expect(messages[0].type).toBe("session_end");
      expect(messages[0].meta?.isError).toBe(true);
    });

    it("non-JSON line produces error message with raw line as content", () => {
      const rawLine = "This is not JSON at all";
      const messages = parseAgentOutput(rawLine);
      expect(messages).toHaveLength(1);
      expect(messages[0].type).toBe("error");
      expect(messages[0].content).toBe(rawLine);
    });
  });
});
