import test from "node:test";
import assert from "node:assert";
import {
  coerceSchemaNumericFields,
  coerceToolSchemas,
  injectEmptyReasoningContentForToolCalls,
  sanitizeToolDescription,
  sanitizeToolDescriptions,
} from "../../open-sse/translator/helpers/schemaCoercion.ts";

test("coerceSchemaNumericFields converts string numbers to actual numbers", () => {
  const schema = {
    type: "object",
    properties: {
      items: {
        type: "array",
        minItems: "1",
        maxItems: "2",
      },
    },
    minimum: "5",
  };

  const result = coerceSchemaNumericFields(schema);

  assert.strictEqual((result as any).minimum, 5);
  (assert as any).strictEqual((result as any).properties.items.minItems, 1);
  (assert as any).strictEqual((result as any).properties.items.maxItems, 2);
});

test("coerceSchemaNumericFields ignores non-numeric strings", () => {
  const schema = {
    minimum: "abc",
    maximum: "10.5",
  };

  const result = coerceSchemaNumericFields(schema);

  (assert as any).strictEqual((result as any).minimum, "abc");
  assert.strictEqual((result as any).maximum, 10.5);
});

test("coerceToolSchemas applies coercion to OpenAI tools", () => {
  const tools = [
    {
      type: "function",
      function: {
        name: "test",
        parameters: {
          properties: { val: { minLength: "2" } },
        },
      },
    },
  ];

  const result = coerceToolSchemas(tools);
  assert.strictEqual(result[0].function.parameters.properties.val.minLength, 2);
});

test("coerceToolSchemas applies coercion to Claude tools", () => {
  const tools = [
    {
      name: "test",
      input_schema: {
        properties: { val: { maxLength: "10" } },
      },
    },
  ];

  const result = coerceToolSchemas(tools);
  assert.strictEqual(result[0].input_schema.properties.val.maxLength, 10);
});

test("sanitizeToolDescription converts null to empty string (OpenAI format)", () => {
  const tool = {
    type: "function",
    function: { name: "test", description: null, parameters: {} },
  };
  const result = sanitizeToolDescription(tool);
  assert.equal((result as any).function.description, "");
});

test("sanitizeToolDescription converts number to string (OpenAI format)", () => {
  const tool = {
    type: "function",
    function: { name: "test", description: 42, parameters: {} },
  };
  const result = sanitizeToolDescription(tool);
  assert.equal((result as any).function.description, "42");
});

test("sanitizeToolDescription handles Claude format", () => {
  const tool = { name: "test", description: null, input_schema: {} };
  const result = sanitizeToolDescription(tool);
  assert.equal((result as any).description, "");
});

test("sanitizeToolDescription preserves valid string descriptions", () => {
  const tool = {
    type: "function",
    function: { name: "test", description: "A useful tool", parameters: {} },
  };
  const result = sanitizeToolDescription(tool);
  assert.equal((result as any).function.description, "A useful tool");
});

test("sanitizeToolDescriptions works on arrays", () => {
  const tools = [
    { name: "test1", description: null, input_schema: {} },
    { type: "function", function: { name: "test2", description: 42, parameters: {} } },
  ];
  const result = sanitizeToolDescriptions(tools);
  assert.strictEqual(result[0].description, "");
  assert.strictEqual(result[1].function.description, "42");
});

test("injectEmptyReasoningContentForToolCalls supports DeepSeek V4 models across providers", () => {
  const messages = [
    { role: "user", content: "hello" },
    { role: "assistant", tool_calls: [{ id: "call_1" }] },
  ];

  for (const provider of ["openrouter", "fireworks", "deepinfra"]) {
    const result = injectEmptyReasoningContentForToolCalls(
      messages,
      provider,
      "accounts/fireworks/models/deepseek-v4-pro"
    ) as Array<{ reasoning_content?: string }>;

    assert.equal(result[1].reasoning_content, "");
  }
});

test("injectEmptyReasoningContentForToolCalls supports Kimi K2 models", () => {
  const messages = [
    { role: "user", content: "hello" },
    { role: "assistant", tool_calls: [{ id: "call_1" }] },
  ];

  for (const model of ["kimi-k2", "kimi-k2.5", "kimi-k2.6", "kimi-k2-thinking"]) {
    const result = injectEmptyReasoningContentForToolCalls(messages, "moonshot", model) as Array<{
      reasoning_content?: string;
    }>;

    assert.equal(result[1].reasoning_content, "", `should inject reasoning_content for ${model}`);
  }
});

test("injectEmptyReasoningContentForToolCalls supports Kimi K2 via Qoder provider", () => {
  const messages = [
    { role: "user", content: "hello" },
    { role: "assistant", tool_calls: [{ id: "call_1" }] },
  ];

  const result = injectEmptyReasoningContentForToolCalls(
    messages,
    "qoder",
    "kimi-k2-thinking"
  ) as Array<{ reasoning_content?: string }>;

  assert.equal(result[1].reasoning_content, "");
});

test("injectEmptyReasoningContentForToolCalls supports all reasoning replay providers", () => {
  const messages = [
    { role: "user", content: "hello" },
    { role: "assistant", tool_calls: [{ id: "call_1" }] },
  ];

  const reasoningProviders = [
    { provider: "deepseek", model: "deepseek-chat" },
    { provider: "opencode-go", model: "some-model" },
    { provider: "nebius", model: "qwq-32b" },
    { provider: "sambanova", model: "qwen3-thinking" },
    { provider: "fireworks", model: "glm-5-thinking" },
    { provider: "together", model: "mimo-v2.5" },
    { provider: "xiaomi-mimo", model: "mimo-v2.5-pro" },
  ];

  for (const { provider, model } of reasoningProviders) {
    const result = injectEmptyReasoningContentForToolCalls(messages, provider, model) as Array<{
      reasoning_content?: string;
    }>;

    assert.equal(
      result[1].reasoning_content,
      "",
      `should inject reasoning_content for ${provider}/${model}`
    );
  }
});

test("injectEmptyReasoningContentForToolCalls skips non-reasoning models", () => {
  const messages = [{ role: "assistant", tool_calls: [{ id: "call_1" }] }];

  const nonReasoningModels = [
    { provider: "openai", model: "gpt-4" },
    { provider: "anthropic", model: "claude-sonnet-4" },
    { provider: "google", model: "gemini-pro" },
    { provider: "groq", model: "llama-3" },
    { provider: "siliconflow", model: "deepseek-r1" },
    { provider: "deepinfra", model: "deepseek-reasoner" },
  ];

  for (const { provider, model } of nonReasoningModels) {
    const result = injectEmptyReasoningContentForToolCalls(messages, provider, model) as Array<{
      reasoning_content?: string;
    }>;

    assert.equal(
      result[0].reasoning_content,
      undefined,
      `should NOT inject reasoning_content for ${provider}/${model}`
    );
  }
});
