import { describe, expect, it } from "vitest";
import { toGenericMessages, type GenericFilePart } from "./toGenericMessages";
import type { ToolModelContentPart } from "../tool/tool-types";

describe("toGenericMessages", () => {
  describe("tool model content", () => {
    const modelContent: readonly ToolModelContentPart[] = [
      { type: "text", text: "Report contents" },
      {
        type: "file",
        data: "JVBERi0xLjQ=",
        mediaType: "application/pdf",
        filename: "report.pdf",
      },
    ];

    it.each([{ content: modelContent }, { content: [] }])(
      "preserves explicit content on a settled result: %j",
      ({ content }) => {
        const result = toGenericMessages([
          {
            role: "assistant",
            content: [
              {
                type: "tool-call",
                toolCallId: "call-1",
                toolName: "report",
                result: { documentId: "report-1" },
                modelContent: content,
              },
            ],
          },
        ]);
        expect(result.at(-1)).toEqual({
          role: "tool",
          content: [
            {
              type: "tool-result",
              toolCallId: "call-1",
              toolName: "report",
              result: { documentId: "report-1" },
              modelContent: content,
            },
          ],
        });
      },
    );

    it.each([{}, { result: "preview", isPreliminary: true }])(
      "does not forward content from an unfinished result: %j",
      (extra) => {
        const result = toGenericMessages([
          {
            role: "assistant",
            content: [
              {
                type: "tool-call",
                toolCallId: "call-1",
                toolName: "report",
                modelContent,
                ...extra,
              },
            ],
          },
        ]);
        expect(result.at(-1)).toEqual({
          role: "tool",
          content: [
            {
              type: "tool-result",
              toolCallId: "call-1",
              toolName: "report",
              result: { error: "Tool call was not completed" },
              isError: true,
            },
          ],
        });
      },
    );
  });

  describe("system messages", () => {
    it("converts text content", () => {
      const result = toGenericMessages([
        {
          role: "system",
          content: [{ type: "text", text: "You are a helpful assistant." }],
        },
      ]);

      expect(result).toEqual([
        { role: "system", content: "You are a helpful assistant." },
      ]);
    });

    it("handles empty text", () => {
      const result = toGenericMessages([
        {
          role: "system",
          content: [{ type: "text", text: "" }],
        },
      ]);

      expect(result).toEqual([]);
    });

    it("handles missing text property", () => {
      const result = toGenericMessages([
        {
          role: "system",
          content: [{ type: "text" }],
        },
      ]);

      expect(result).toEqual([]);
    });

    it("uses first text part when multiple exist", () => {
      const result = toGenericMessages([
        {
          role: "system",
          content: [
            { type: "text", text: "First" },
            { type: "text", text: "Second" },
          ],
        },
      ]);

      expect(result).toEqual([{ role: "system", content: "First" }]);
    });

    it("skips a null part when finding the text", () => {
      const result = toGenericMessages([
        {
          role: "system",
          content: [null, { type: "text", text: "Be brief." }],
        },
      ] as never);

      expect(result).toEqual([{ role: "system", content: "Be brief." }]);
    });
  });

  describe("user messages", () => {
    describe.each(["mp3", "wav"] as const)("legacy %s audio", (format) => {
      it.each([false, true])(
        "preserves audio bytes (attachment: %s)",
        (attachment) => {
          const part = { type: "audio", audio: { data: "UklGRg==", format } };
          const message = {
            role: "user" as const,
            content: attachment ? [] : [part],
            attachments: attachment ? [{ content: [part] }] : [],
          };
          expect(toGenericMessages([message])).toEqual([
            {
              role: "user",
              content: [
                {
                  type: "file",
                  data: "UklGRg==",
                  mediaType: `audio/${format}`,
                },
              ],
            },
          ]);
        },
      );

      it.each([
        "https://example.com/recording",
        "data:audio/FORMAT;base64,UklGRg==",
      ])("preserves an audio URL: %s", (value) => {
        const data = value.replace("FORMAT", format);
        expect(
          toGenericMessages([
            {
              role: "user",
              content: [{ type: "audio", audio: { data, format } }],
            },
          ]),
        ).toEqual([
          {
            role: "user",
            content: [
              {
                type: "file",
                data: new URL(data),
                mediaType: `audio/${format}`,
              },
            ],
          },
        ]);
      });

      it.each([
        {
          name: "a mismatched data URL",
          input: "data:audio/mpeg;base64,QUJD",
          expected: `data:audio/${format};base64,QUJD`,
          isUrl: true,
        },
        {
          name: "a matching data URL",
          input: `data:audio/${format};base64,QUJD`,
          expected: `data:audio/${format};base64,QUJD`,
          isUrl: true,
        },
        {
          name: "raw base64",
          input: "QUJD",
          expected: "QUJD",
          isUrl: false,
        },
        {
          name: "an HTTP URL",
          input: "http://example.com/recording",
          expected: "http://example.com/recording",
          isUrl: true,
        },
        {
          name: "an HTTPS URL",
          input: "https://example.com/recording",
          expected: "https://example.com/recording",
          isUrl: true,
        },
      ])("uses the typed format for $name", ({ input, expected, isUrl }) => {
        expect(
          toGenericMessages([
            {
              role: "user",
              content: [{ type: "audio", audio: { data: input, format } }],
            },
          ]),
        ).toEqual([
          {
            role: "user",
            content: [
              {
                type: "file",
                data: isUrl ? new URL(expected) : expected,
                mediaType: `audio/${format}`,
              },
            ],
          },
        ]);
      });
    });

    it.each([undefined, {}, { data: "UklGRg==" }, { format: "wav" }])(
      "skips an audio part missing its payload or format: %j",
      (audio) => {
        expect(
          toGenericMessages([
            {
              role: "user",
              content: [
                { type: "text", text: "Keep this" },
                { type: "audio", audio },
              ],
            },
          ] as never),
        ).toEqual([
          { role: "user", content: [{ type: "text", text: "Keep this" }] },
        ]);
      },
    );

    it("converts text parts", () => {
      const result = toGenericMessages([
        {
          role: "user",
          content: [{ type: "text", text: "Hello!" }],
        },
      ]);

      expect(result).toEqual([
        { role: "user", content: [{ type: "text", text: "Hello!" }] },
      ]);
    });

    it("converts image parts with media type inference", () => {
      const result = toGenericMessages([
        {
          role: "user",
          content: [{ type: "image", image: "https://example.com/photo.jpg" }],
        },
      ]);

      expect(result).toEqual([
        {
          role: "user",
          content: [
            {
              type: "file",
              data: new URL("https://example.com/photo.jpg"),
              mediaType: "image/jpeg",
            },
          ],
        },
      ]);
    });

    it("converts file parts", () => {
      const result = toGenericMessages([
        {
          role: "user",
          content: [
            {
              type: "file",
              data: "https://example.com/doc.pdf",
              mimeType: "application/pdf",
            },
          ],
        },
      ]);

      expect(result).toEqual([
        {
          role: "user",
          content: [
            {
              type: "file",
              data: new URL("https://example.com/doc.pdf"),
              mediaType: "application/pdf",
            },
          ],
        },
      ]);
    });

    it("preserves zero-byte file parts", () => {
      const result = toGenericMessages([
        {
          role: "user",
          content: [
            {
              type: "file",
              data: "",
              mimeType: "text/plain",
              filename: "empty.txt",
            },
          ],
        },
      ]);

      expect(result).toEqual([
        {
          role: "user",
          content: [
            {
              type: "file",
              data: "",
              mediaType: "text/plain",
              filename: "empty.txt",
            },
          ],
        },
      ]);
    });

    it.each([
      { label: "empty", mimeType: "" },
      { label: "missing", mimeType: undefined },
    ])("falls back when a file MIME type is $label", ({ mimeType }) => {
      const result = toGenericMessages([
        {
          role: "user",
          content: [
            {
              type: "file",
              data: "https://cdn.example.com/untyped-file",
              ...(mimeType !== undefined && { mimeType }),
              filename: "untyped-file",
            },
          ],
        },
      ]);

      expect(result).toEqual([
        {
          role: "user",
          content: [
            {
              type: "file",
              data: new URL("https://cdn.example.com/untyped-file"),
              mediaType: "application/octet-stream",
              filename: "untyped-file",
            },
          ],
        },
      ]);
    });

    it("uses the data URL MIME type when the file MIME type is empty", () => {
      const data = "data:text/plain;base64,";
      const result = toGenericMessages([
        {
          role: "user",
          content: [{ type: "file", data, mimeType: "" }],
        },
      ]);

      expect(result).toEqual([
        {
          role: "user",
          content: [
            {
              type: "file",
              data: new URL(data),
              mediaType: "text/plain",
            },
          ],
        },
      ]);
    });

    it("does not infer a MIME type from a malformed data URL", () => {
      const data = "data:text/plain";
      const result = toGenericMessages([
        {
          role: "user",
          content: [{ type: "file", data, mimeType: "" }],
        },
      ]);

      expect(result).toEqual([
        {
          role: "user",
          content: [
            {
              type: "file",
              data: new URL(data),
              mediaType: "application/octet-stream",
            },
          ],
        },
      ]);
    });

    it("handles attachments", () => {
      const result = toGenericMessages([
        {
          role: "user",
          content: [{ type: "text", text: "See attached" }],
          attachments: [
            {
              content: [
                {
                  type: "file",
                  data: "https://example.com/file.txt",
                  mimeType: "text/plain",
                },
              ],
            },
          ],
        },
      ]);

      expect(result).toEqual([
        {
          role: "user",
          content: [
            { type: "text", text: "See attached" },
            {
              type: "file",
              data: new URL("https://example.com/file.txt"),
              mediaType: "text/plain",
            },
          ],
        },
      ]);
    });

    it("uses an image attachment's media type for extensionless URLs", () => {
      const result = toGenericMessages([
        {
          role: "user",
          content: [],
          attachments: [
            {
              contentType: "image/jpeg",
              content: [
                { type: "image", image: "https://cdn.example.com/image/123" },
              ],
            },
          ],
        },
      ]);

      expect(result).toEqual([
        {
          role: "user",
          content: [
            {
              type: "file",
              data: new URL("https://cdn.example.com/image/123"),
              mediaType: "image/jpeg",
            },
          ],
        },
      ]);
    });

    it("keeps attachment parts when the message has no content", () => {
      const result = toGenericMessages([
        {
          role: "user",
          attachments: [{ content: [{ type: "text", text: "notes" }] }],
        },
      ] as never);

      expect(result).toEqual([
        { role: "user", content: [{ type: "text", text: "notes" }] },
      ]);
    });

    it("skips attachments without content and null attachments", () => {
      const result = toGenericMessages([
        {
          role: "user",
          content: [{ type: "text", text: "See attached" }],
          attachments: [
            { id: "uploading" },
            null,
            { content: [{ type: "text", text: "notes" }] },
          ],
        },
      ] as never);

      expect(result).toEqual([
        {
          role: "user",
          content: [
            { type: "text", text: "See attached" },
            { type: "text", text: "notes" },
          ],
        },
      ]);
    });

    it("lowercases a mixed-case attachment media type", () => {
      const result = toGenericMessages([
        {
          role: "user",
          content: [],
          attachments: [
            {
              contentType: "Image/JPEG",
              content: [
                { type: "image", image: "https://cdn.example.com/image/123" },
              ],
            },
          ],
        },
      ]);

      expect(result[0]?.content[0]).toMatchObject({ mediaType: "image/jpeg" });
    });

    it("falls back past a wildcard attachment media type", () => {
      const result = toGenericMessages([
        {
          role: "user",
          content: [],
          attachments: [
            {
              contentType: "image/*",
              content: [
                { type: "image", image: "https://cdn.example.com/photo.webp" },
              ],
            },
          ],
        },
      ]);

      expect(result[0]?.content[0]).toMatchObject({ mediaType: "image/webp" });
    });

    it("carries a file part filename through", () => {
      const result = toGenericMessages([
        {
          role: "user",
          content: [
            {
              type: "file",
              data: "https://cdn.example.com/a.pdf",
              mimeType: "application/pdf",
              filename: "invoice.pdf",
            },
          ],
        },
      ] as never);

      expect(result).toEqual([
        {
          role: "user",
          content: [
            {
              type: "file",
              data: new URL("https://cdn.example.com/a.pdf"),
              mediaType: "application/pdf",
              filename: "invoice.pdf",
            },
          ],
        },
      ]);
    });

    it("omits filename when the part has none", () => {
      const result = toGenericMessages([
        {
          role: "user",
          content: [
            {
              type: "file",
              data: "https://cdn.example.com/a.pdf",
              mimeType: "application/pdf",
            },
          ],
        },
      ] as never);

      const part = (result[0] as { content: unknown[] }).content[0];
      expect(part).not.toHaveProperty("filename");
    });

    it("filters invalid parts", () => {
      const result = toGenericMessages([
        {
          role: "user",
          content: [
            { type: "text", text: "Valid" },
            { type: "image" }, // missing image property
            { type: "file", data: null } as never,
            { type: "unknown" },
          ],
        },
      ]);

      expect(result).toEqual([
        { role: "user", content: [{ type: "text", text: "Valid" }] },
      ]);
    });

    it("skips null parts in the message and its attachments", () => {
      const result = toGenericMessages([
        {
          role: "user",
          content: [null, { type: "text", text: "Hi" }],
          attachments: [{ content: [null, { type: "text", text: "notes" }] }],
        },
      ] as never);

      expect(result).toEqual([
        {
          role: "user",
          content: [
            { type: "text", text: "Hi" },
            { type: "text", text: "notes" },
          ],
        },
      ]);
    });

    it("handles data URL as URL object", () => {
      const dataUrl =
        "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==";
      const result = toGenericMessages([
        {
          role: "user",
          content: [{ type: "image", image: dataUrl }],
        },
      ]);

      expect(result).toHaveLength(1);
      expect(result[0]!.role).toBe("user");
      const content = (result[0] as { content: unknown[] }).content;
      expect(content[0]).toMatchObject({
        type: "file",
        mediaType: "image/png",
      });
      expect((content[0] as { data: unknown }).data).toBeInstanceOf(URL);
    });

    it("infers a lowercase media type from an uppercase-scheme data URL", () => {
      const dataUrl =
        "DATA:IMAGE/PNG;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==";
      const result = toGenericMessages([
        {
          role: "user",
          content: [{ type: "image", image: dataUrl }],
        },
      ]);

      const content = (result[0] as { content: unknown[] }).content;
      expect(content[0]).toMatchObject({
        type: "file",
        mediaType: "image/png",
      });
    });

    it("handles relative/invalid URL as string", () => {
      const result = toGenericMessages([
        {
          role: "user",
          content: [
            {
              type: "file",
              data: "/relative/path/file.txt",
              mimeType: "text/plain",
            },
          ],
        },
      ]);

      expect(result).toEqual([
        {
          role: "user",
          content: [
            {
              type: "file",
              data: "/relative/path/file.txt",
              mediaType: "text/plain",
            },
          ],
        },
      ]);
    });

    it("produces empty result when all parts are invalid", () => {
      const result = toGenericMessages([
        {
          role: "user",
          content: [{ type: "unknown" }],
        },
      ]);

      expect(result).toEqual([]);
    });
  });

  describe("assistant messages", () => {
    it("converts text parts", () => {
      const result = toGenericMessages([
        {
          role: "assistant",
          content: [{ type: "text", text: "Hello!" }],
        },
      ]);

      expect(result).toEqual([
        { role: "assistant", content: [{ type: "text", text: "Hello!" }] },
      ]);
    });

    it("converts generated file parts", () => {
      const result = toGenericMessages([
        {
          role: "assistant",
          content: [
            {
              type: "file",
              data: "iVBORw0KGgo=",
              mimeType: "image/png",
              filename: "generated.png",
            },
          ],
        },
      ]);

      expect(result).toEqual([
        {
          role: "assistant",
          content: [
            {
              type: "file",
              data: "iVBORw0KGgo=",
              mediaType: "image/png",
              filename: "generated.png",
            },
          ],
        },
      ]);
    });

    it("converts generated image parts", () => {
      const dataUrl = "data:image/webp;base64,UklGRg==";
      const result = toGenericMessages([
        {
          role: "assistant",
          content: [
            { type: "image", image: dataUrl, filename: "generated.webp" },
          ],
        },
      ]);

      expect(result).toHaveLength(1);
      expect(result[0]).toMatchObject({
        role: "assistant",
        content: [
          {
            type: "file",
            mediaType: "image/webp",
            filename: "generated.webp",
          },
        ],
      });
      const content = (result[0] as { content: GenericFilePart[] }).content;
      expect(content[0]?.data).toBeInstanceOf(URL);
      expect(String(content[0]?.data)).toBe(dataUrl);
    });

    it("places generated files after settled tool results", () => {
      const result = toGenericMessages([
        {
          role: "assistant",
          content: [
            {
              type: "tool-call",
              toolCallId: "call_123",
              toolName: "create_image",
              args: {},
              result: { created: true },
            },
            {
              type: "file",
              data: "iVBORw0KGgo=",
              mimeType: "image/png",
              filename: "generated.png",
            },
          ],
        },
      ]);

      expect(result.map((message) => message.role)).toEqual([
        "assistant",
        "tool",
        "assistant",
      ]);
      expect(result[2]).toEqual({
        role: "assistant",
        content: [
          {
            type: "file",
            data: "iVBORw0KGgo=",
            mediaType: "image/png",
            filename: "generated.png",
          },
        ],
      });
    });

    it("skips null parts", () => {
      const result = toGenericMessages([
        {
          role: "assistant",
          content: [null, { type: "text", text: "Hello!" }, null],
        },
      ] as never);

      expect(result).toEqual([
        { role: "assistant", content: [{ type: "text", text: "Hello!" }] },
      ]);
    });

    it("closes out tool calls without results", () => {
      const result = toGenericMessages([
        {
          role: "assistant",
          content: [
            {
              type: "tool-call",
              toolCallId: "call_123",
              toolName: "get_weather",
              args: { city: "London" },
            },
          ],
        },
      ]);

      expect(result).toEqual([
        {
          role: "assistant",
          content: [
            {
              type: "tool-call",
              toolCallId: "call_123",
              toolName: "get_weather",
              args: { city: "London" },
            },
          ],
        },
        {
          role: "tool",
          content: [
            {
              type: "tool-result",
              toolCallId: "call_123",
              toolName: "get_weather",
              result: { error: "Tool call was not completed" },
              isError: true,
            },
          ],
        },
      ]);
    });

    it("does not send a preliminary result to the model", () => {
      const result = toGenericMessages([
        {
          role: "assistant",
          content: [
            {
              type: "tool-call",
              toolCallId: "call_123",
              toolName: "get_weather",
              args: { city: "London" },
              state: "call",
              result: { temperature: 20 },
              isPreliminary: true,
            },
          ],
        },
      ]);

      expect(result.at(-1)).toEqual({
        role: "tool",
        content: [
          {
            type: "tool-result",
            toolCallId: "call_123",
            toolName: "get_weather",
            result: { error: "Tool call was not completed" },
            isError: true,
          },
        ],
      });
    });

    it.each([
      ["a pending approval", { approval: { id: "ap_1" } }],
      [
        "an approved call the host has not run yet",
        {
          approval: { id: "ap_1", approved: true },
        },
      ],
      ["an interrupted call", { interrupt: { type: "human", payload: {} } }],
    ])("leaves %s untouched instead of failing it", (_label, extra) => {
      const result = toGenericMessages([
        {
          role: "assistant",
          content: [
            {
              type: "tool-call",
              toolCallId: "call_123",
              toolName: "get_weather",
              args: { city: "London" },
              ...extra,
            },
          ],
        },
      ]);

      expect(result).toEqual([
        {
          role: "assistant",
          content: [
            {
              type: "tool-call",
              toolCallId: "call_123",
              toolName: "get_weather",
              args: { city: "London" },
            },
          ],
        },
      ]);
    });

    it.each(["running", "requires-action"])(
      "leaves a pending approval untouched while its message is %s",
      (type) => {
        const result = toGenericMessages([
          {
            role: "assistant",
            status: { type },
            content: [
              {
                type: "tool-call",
                toolCallId: "call_123",
                toolName: "get_weather",
                args: { city: "London" },
                approval: { id: "ap_1" },
              },
            ],
          },
        ]);

        expect(result.map((message) => message.role)).toEqual(["assistant"]);
      },
    );

    const awaitingHost = [
      ["a pending approval", { approval: { id: "ap_1" } }],
      [
        "an approved call the host never ran",
        { approval: { id: "ap_1", approved: true } },
      ],
      ["an interrupted call", { interrupt: { type: "human", payload: {} } }],
    ] as const;

    const closedOut = [
      {
        role: "assistant",
        content: [
          {
            type: "tool-call",
            toolCallId: "call_123",
            toolName: "get_weather",
            args: { city: "London" },
          },
        ],
      },
      {
        role: "tool",
        content: [
          {
            type: "tool-result",
            toolCallId: "call_123",
            toolName: "get_weather",
            result: { error: "Tool call was not completed" },
            isError: true,
          },
        ],
      },
    ];

    it.each(awaitingHost)(
      "closes out %s once its message has settled",
      (_label, extra) => {
        const result = toGenericMessages([
          {
            role: "assistant",
            status: { type: "incomplete" },
            content: [
              {
                type: "tool-call",
                toolCallId: "call_123",
                toolName: "get_weather",
                args: { city: "London" },
                ...extra,
              },
            ],
          },
        ]);

        expect(result).toEqual(closedOut);
      },
    );

    it.each(awaitingHost)(
      "closes out %s in a message a later message follows",
      (_label, extra) => {
        const result = toGenericMessages([
          {
            role: "assistant",
            content: [
              {
                type: "tool-call",
                toolCallId: "call_123",
                toolName: "get_weather",
                args: { city: "London" },
                ...extra,
              },
            ],
          },
          { role: "user", content: [{ type: "text", text: "never mind" }] },
        ]);

        expect(result).toEqual([
          ...closedOut,
          { role: "user", content: [{ type: "text", text: "never mind" }] },
        ]);
      },
    );

    it.each([
      ["a cancelled approval", { id: "ap_1", resolution: "cancelled" }],
      ["a denied approval carrying no result", { id: "ap_1", approved: false }],
    ])("closes out %s", (_label, approval) => {
      const result = toGenericMessages([
        {
          role: "assistant",
          content: [
            {
              type: "tool-call",
              toolCallId: "call_123",
              toolName: "get_weather",
              args: { city: "London" },
              approval,
            },
          ],
        },
      ]);

      expect(result).toEqual([
        {
          role: "assistant",
          content: [
            {
              type: "tool-call",
              toolCallId: "call_123",
              toolName: "get_weather",
              args: { city: "London" },
            },
          ],
        },
        {
          role: "tool",
          content: [
            {
              type: "tool-result",
              toolCallId: "call_123",
              toolName: "get_weather",
              result: { error: "Tool call was not completed" },
              isError: true,
            },
          ],
        },
      ]);
    });

    it("treats a settled tool call carrying no result as completed", () => {
      const result = toGenericMessages([
        {
          role: "assistant",
          content: [
            {
              type: "tool-call",
              toolCallId: "call_123",
              toolName: "void_tool",
              args: {},
              state: "result",
            },
          ],
        },
      ]);

      expect(result).toEqual([
        {
          role: "assistant",
          content: [
            {
              type: "tool-call",
              toolCallId: "call_123",
              toolName: "void_tool",
              args: {},
            },
          ],
        },
        {
          role: "tool",
          content: [
            {
              type: "tool-result",
              toolCallId: "call_123",
              toolName: "void_tool",
              result: "<no result>",
            },
          ],
        },
      ]);
    });

    it("closes out a tool call left unresolved before a later message", () => {
      const result = toGenericMessages([
        {
          role: "assistant",
          content: [
            {
              type: "tool-call",
              toolCallId: "call_123",
              toolName: "get_weather",
              args: { city: "London" },
            },
          ],
        },
        {
          role: "user",
          content: [{ type: "text", text: "never mind" }],
        },
      ]);

      expect(result).toEqual([
        {
          role: "assistant",
          content: [
            {
              type: "tool-call",
              toolCallId: "call_123",
              toolName: "get_weather",
              args: { city: "London" },
            },
          ],
        },
        {
          role: "tool",
          content: [
            {
              type: "tool-result",
              toolCallId: "call_123",
              toolName: "get_weather",
              result: { error: "Tool call was not completed" },
              isError: true,
            },
          ],
        },
        {
          role: "user",
          content: [{ type: "text", text: "never mind" }],
        },
      ]);
    });

    it("converts tool calls with results (creates tool message)", () => {
      const result = toGenericMessages([
        {
          role: "assistant",
          content: [
            {
              type: "tool-call",
              toolCallId: "call_123",
              toolName: "get_weather",
              args: { city: "London" },
              result: { temperature: 20, unit: "celsius" },
            },
          ],
        },
      ]);

      expect(result).toEqual([
        {
          role: "assistant",
          content: [
            {
              type: "tool-call",
              toolCallId: "call_123",
              toolName: "get_weather",
              args: { city: "London" },
            },
          ],
        },
        {
          role: "tool",
          content: [
            {
              type: "tool-result",
              toolCallId: "call_123",
              toolName: "get_weather",
              result: { temperature: 20, unit: "celsius" },
            },
          ],
        },
      ]);
    });

    it("includes isError in tool result when present", () => {
      const result = toGenericMessages([
        {
          role: "assistant",
          content: [
            {
              type: "tool-call",
              toolCallId: "call_123",
              toolName: "get_weather",
              args: { city: "London" },
              result: "Error: City not found",
              isError: true,
            },
          ],
        },
      ]);

      expect(result).toEqual([
        {
          role: "assistant",
          content: [
            {
              type: "tool-call",
              toolCallId: "call_123",
              toolName: "get_weather",
              args: { city: "London" },
            },
          ],
        },
        {
          role: "tool",
          content: [
            {
              type: "tool-result",
              toolCallId: "call_123",
              toolName: "get_weather",
              result: "Error: City not found",
              isError: true,
            },
          ],
        },
      ]);
    });

    it("handles text-tool interleaving (flush on text boundary)", () => {
      const result = toGenericMessages([
        {
          role: "assistant",
          content: [
            { type: "text", text: "Let me check the weather." },
            {
              type: "tool-call",
              toolCallId: "call_1",
              toolName: "get_weather",
              args: { city: "London" },
              result: { temp: 20 },
            },
            { type: "text", text: "The weather is nice." },
          ],
        },
      ]);

      // The flush happens BEFORE adding text when there are pending tool results
      // So first assistant message contains both initial text and tool call
      expect(result).toEqual([
        {
          role: "assistant",
          content: [
            { type: "text", text: "Let me check the weather." },
            {
              type: "tool-call",
              toolCallId: "call_1",
              toolName: "get_weather",
              args: { city: "London" },
            },
          ],
        },
        {
          role: "tool",
          content: [
            {
              type: "tool-result",
              toolCallId: "call_1",
              toolName: "get_weather",
              result: { temp: 20 },
            },
          ],
        },
        {
          role: "assistant",
          content: [{ type: "text", text: "The weather is nice." }],
        },
      ]);
    });

    it("handles missing toolCallId", () => {
      const result = toGenericMessages([
        {
          role: "assistant",
          content: [
            {
              type: "tool-call",
              toolName: "get_weather",
              args: { city: "London" },
            },
          ],
        },
      ]);

      // Tool call should be skipped due to missing toolCallId
      expect(result).toEqual([]);
    });

    it("handles missing toolName", () => {
      const result = toGenericMessages([
        {
          role: "assistant",
          content: [
            {
              type: "tool-call",
              toolCallId: "call_123",
              args: { city: "London" },
            },
          ],
        },
      ]);

      // Tool call should be skipped due to missing toolName
      expect(result).toEqual([]);
    });

    it("defaults args to empty object when missing", () => {
      const result = toGenericMessages([
        {
          role: "assistant",
          content: [
            {
              type: "tool-call",
              toolCallId: "call_123",
              toolName: "no_args_tool",
            },
          ],
        },
      ]);

      expect(result).toEqual([
        {
          role: "assistant",
          content: [
            {
              type: "tool-call",
              toolCallId: "call_123",
              toolName: "no_args_tool",
              args: {},
            },
          ],
        },
        {
          role: "tool",
          content: [
            {
              type: "tool-result",
              toolCallId: "call_123",
              toolName: "no_args_tool",
              result: { error: "Tool call was not completed" },
              isError: true,
            },
          ],
        },
      ]);
    });

    it("handles multiple tool calls with mixed results", () => {
      const result = toGenericMessages([
        {
          role: "assistant",
          content: [
            {
              type: "tool-call",
              toolCallId: "call_1",
              toolName: "tool_a",
              args: {},
              result: "result_a",
            },
            {
              type: "tool-call",
              toolCallId: "call_2",
              toolName: "tool_b",
              args: {},
              // no result
            },
            {
              type: "tool-call",
              toolCallId: "call_3",
              toolName: "tool_c",
              args: {},
              result: "result_c",
            },
          ],
        },
      ]);

      expect(result).toEqual([
        {
          role: "assistant",
          content: [
            {
              type: "tool-call",
              toolCallId: "call_1",
              toolName: "tool_a",
              args: {},
            },
            {
              type: "tool-call",
              toolCallId: "call_2",
              toolName: "tool_b",
              args: {},
            },
            {
              type: "tool-call",
              toolCallId: "call_3",
              toolName: "tool_c",
              args: {},
            },
          ],
        },
        {
          role: "tool",
          content: [
            {
              type: "tool-result",
              toolCallId: "call_1",
              toolName: "tool_a",
              result: "result_a",
            },
            {
              type: "tool-result",
              toolCallId: "call_2",
              toolName: "tool_b",
              result: { error: "Tool call was not completed" },
              isError: true,
            },
            {
              type: "tool-result",
              toolCallId: "call_3",
              toolName: "tool_c",
              result: "result_c",
            },
          ],
        },
      ]);
    });
  });

  describe("inferImageMediaType", () => {
    const testMediaType = (url: string, expectedType: string) => {
      const result = toGenericMessages([
        {
          role: "user",
          content: [{ type: "image", image: url }],
        },
      ]);
      const content = (result[0] as { content: { mediaType: string }[] })
        .content;
      expect(content[0]!.mediaType).toBe(expectedType);
    };

    it("maps known extensions to their media types", () => {
      testMediaType("https://example.com/photo.jpg", "image/jpeg");
      testMediaType("https://example.com/photo.jpeg", "image/jpeg");
      testMediaType("https://example.com/photo.png", "image/png");
      testMediaType("https://example.com/photo.gif", "image/gif");
      testMediaType("https://example.com/photo.webp", "image/webp");
      testMediaType("https://example.com/icon.svg", "image/svg+xml");
      testMediaType("https://example.com/photo.avif", "image/avif");
      testMediaType("https://example.com/photo.bmp", "image/bmp");
      testMediaType("https://example.com/favicon.ico", "image/x-icon");
      testMediaType("https://example.com/photo.tiff", "image/tiff");
      testMediaType("https://example.com/photo.tif", "image/tiff");
    });

    it("defaults to image/png for unknown or missing extensions", () => {
      testMediaType("https://example.com/photo.unknown", "image/png");
      testMediaType("https://example.com/photo", "image/png");
    });

    it("ignores query params and casing in the extension", () => {
      testMediaType(
        "https://example.com/photo.jpg?size=large&quality=high",
        "image/jpeg",
      );
      testMediaType("https://example.com/photo.JPG", "image/jpeg");
    });
  });

  describe("toUrlOrString", () => {
    it("converts valid URL to URL object", () => {
      const result = toGenericMessages([
        {
          role: "user",
          content: [{ type: "image", image: "https://example.com/photo.png" }],
        },
      ]);
      const content = (result[0] as { content: { data: unknown }[] }).content;
      expect(content[0]!.data).toBeInstanceOf(URL);
      expect((content[0]!.data as URL).href).toBe(
        "https://example.com/photo.png",
      );
    });

    it("converts data URL to URL object", () => {
      const dataUrl = "data:image/png;base64,abc123";
      const result = toGenericMessages([
        {
          role: "user",
          content: [{ type: "image", image: dataUrl }],
        },
      ]);
      const content = (result[0] as { content: { data: unknown }[] }).content;
      expect(content[0]!.data).toBeInstanceOf(URL);
    });

    it("keeps relative path as string", () => {
      const result = toGenericMessages([
        {
          role: "user",
          content: [
            { type: "file", data: "/path/to/file.txt", mimeType: "text/plain" },
          ],
        },
      ]);
      const content = (result[0] as { content: { data: unknown }[] }).content;
      expect(content[0]!.data).toBe("/path/to/file.txt");
    });

    it("keeps invalid URL as string", () => {
      const result = toGenericMessages([
        {
          role: "user",
          content: [
            { type: "file", data: "not a valid url", mimeType: "text/plain" },
          ],
        },
      ]);
      const content = (result[0] as { content: { data: unknown }[] }).content;
      expect(content[0]!.data).toBe("not a valid url");
    });
  });

  describe("multiple messages", () => {
    it("skips null and undefined messages", () => {
      const result = toGenericMessages([
        null,
        { role: "user", content: [{ type: "text", text: "Hi" }] },
        undefined,
      ] as never);

      expect(result).toEqual([
        { role: "user", content: [{ type: "text", text: "Hi" }] },
      ]);
    });

    it("keeps a pending approval in the last message when a null entry trails it", () => {
      const result = toGenericMessages([
        {
          role: "assistant",
          content: [
            {
              type: "tool-call",
              toolCallId: "call_123",
              toolName: "get_weather",
              args: { city: "London" },
              approval: { id: "ap_1" },
            },
          ],
        },
        null,
      ] as never);

      expect(result).toEqual([
        {
          role: "assistant",
          content: [
            {
              type: "tool-call",
              toolCallId: "call_123",
              toolName: "get_weather",
              args: { city: "London" },
            },
          ],
        },
      ]);
    });

    it("converts a full conversation", () => {
      const result = toGenericMessages([
        {
          role: "system",
          content: [{ type: "text", text: "You are helpful." }],
        },
        {
          role: "user",
          content: [{ type: "text", text: "What is 2+2?" }],
        },
        {
          role: "assistant",
          content: [{ type: "text", text: "2+2 equals 4." }],
        },
        {
          role: "user",
          content: [{ type: "text", text: "Thanks!" }],
        },
      ]);

      expect(result).toEqual([
        { role: "system", content: "You are helpful." },
        { role: "user", content: [{ type: "text", text: "What is 2+2?" }] },
        {
          role: "assistant",
          content: [{ type: "text", text: "2+2 equals 4." }],
        },
        { role: "user", content: [{ type: "text", text: "Thanks!" }] },
      ]);
    });

    it("skips messages without content and keeps the rest", () => {
      const result = toGenericMessages([
        { role: "system" },
        { role: "user" },
        { role: "assistant" },
        { role: "user", content: [{ type: "text", text: "Thanks!" }] },
      ] as never);

      expect(result).toEqual([
        { role: "user", content: [{ type: "text", text: "Thanks!" }] },
      ]);
    });

    it("handles empty messages array", () => {
      const result = toGenericMessages([]);
      expect(result).toEqual([]);
    });
  });
});
