package openai

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"iop/packages/go/config"
	iop "iop/proto/gen/iop"
)

func TestChatCompletionsDispatchesConfiguredOllamaTarget(t *testing.T) {
	fake := &fakeRunService{events: make(chan *iop.RunEvent, 3)}
	fake.events <- &iop.RunEvent{Type: "delta", Delta: "hello"}
	fake.events <- &iop.RunEvent{Type: "delta", Delta: " world"}
	fake.events <- &iop.RunEvent{Type: "complete", Usage: &iop.Usage{InputTokens: 2, OutputTokens: 2}}

	srv := NewServer(config.EdgeOpenAIConf{
		Adapter:    "ollama",
		Target:     "llama-fixed",
		SessionID:  "cline",
		TimeoutSec: 15,
	}, fake, nil)
	req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
		"model":"client-model",
		"messages":[{"role":"system","content":"brief"},{"role":"user","content":"say hello"}]
	}`))
	w := httptest.NewRecorder()

	srv.handleChatCompletions(w, req)

	if w.Code != http.StatusOK {
		t.Fatalf("status: got %d body=%s", w.Code, w.Body.String())
	}
	if fake.req.Adapter != "ollama" || fake.req.Target != "llama-fixed" {
		t.Fatalf("dispatch target mismatch: %+v", fake.req)
	}
	if fake.req.SessionID != "cline" || fake.req.TimeoutSec != 15 {
		t.Fatalf("execution config mismatch: %+v", fake.req)
	}
	if fake.req.ModelGroupKey != "client-model" {
		t.Fatalf("model group key: got %q, want client-model", fake.req.ModelGroupKey)
	}
	if !strings.Contains(fake.req.Prompt, "system: brief") || !strings.Contains(fake.req.Prompt, "user: say hello") {
		t.Fatalf("prompt did not include messages: %q", fake.req.Prompt)
	}

	var resp chatCompletionResponse
	if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
		t.Fatalf("decode response: %v", err)
	}
	if resp.Choices[0].Message.Content != "hello world" {
		t.Fatalf("content: got %q", resp.Choices[0].Message.Content)
	}
	if resp.Usage == nil || resp.Usage.TotalTokens != 4 {
		t.Fatalf("usage: %+v", resp.Usage)
	}
}

func TestChatCompletionsUsesRequestModelWhenNoConfiguredTarget(t *testing.T) {
	fake := &fakeRunService{events: make(chan *iop.RunEvent, 2)}
	fake.events <- &iop.RunEvent{Type: "delta", Delta: "ok"}
	fake.events <- &iop.RunEvent{Type: "complete"}

	srv := NewServer(config.EdgeOpenAIConf{Adapter: "ollama"}, fake, nil)
	req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
		"model":"from-request",
		"messages":[{"role":"user","content":"hi"}]
	}`))
	w := httptest.NewRecorder()

	srv.handleChatCompletions(w, req)

	if w.Code != http.StatusOK {
		t.Fatalf("status: got %d body=%s", w.Code, w.Body.String())
	}
	if fake.req.Target != "from-request" {
		t.Fatalf("target: got %q", fake.req.Target)
	}
}

func TestChatCompletionsPreservesProviderFinishReason(t *testing.T) {
	fake := &fakeRunService{events: make(chan *iop.RunEvent, 2)}
	fake.events <- &iop.RunEvent{Type: "delta", Delta: "truncated"}
	fake.events <- &iop.RunEvent{Type: "complete", Metadata: map[string]string{"finish_reason": "length"}}

	srv := NewServer(config.EdgeOpenAIConf{Adapter: "ollama"}, fake, nil)
	req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
		"metadata":{},
		"model":"qwen3.6:35b",
		"messages":[{"role":"user","content":"hi"}]
	}`))
	w := httptest.NewRecorder()

	srv.handleChatCompletions(w, req)

	if w.Code != http.StatusOK {
		t.Fatalf("status: got %d body=%s", w.Code, w.Body.String())
	}
	var resp chatCompletionResponse
	if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
		t.Fatalf("decode response: %v", err)
	}
	if resp.Choices[0].FinishReason != "length" {
		t.Fatalf("finish_reason: got %q, want length", resp.Choices[0].FinishReason)
	}
}

func TestChatCompletionsPassesStandardOptions(t *testing.T) {
	fake := &fakeRunService{events: make(chan *iop.RunEvent, 2)}
	fake.events <- &iop.RunEvent{Type: "delta", Delta: "ok"}
	fake.events <- &iop.RunEvent{Type: "complete"}

	srv := NewServer(config.EdgeOpenAIConf{Adapter: "ollama"}, fake, nil)
	req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
		"model":"from-request",
		"messages":[{"role":"user","content":"hi"}],
		"max_tokens":12,
		"temperature":0.1,
		"top_p":0.9,
		"stop":["END"],
		"response_format":{"type":"json_object"},
		"tools":[{"type":"function","function":{"name":"lookup"}}]
	}`))
	w := httptest.NewRecorder()

	srv.handleChatCompletions(w, req)

	if w.Code != http.StatusOK {
		t.Fatalf("status: got %d body=%s", w.Code, w.Body.String())
	}
	options, ok := fake.req.Input["options"].(map[string]any)
	if !ok {
		t.Fatalf("options not passed: %+v", fake.req.Input)
	}
	if options["temperature"].(float64) != 0.1 || options["top_p"].(float64) != 0.9 {
		t.Fatalf("unexpected options: %+v", options)
	}
	if options["max_tokens"].(int) != 12 {
		t.Fatalf("max_tokens not passed: %+v", options)
	}
	if _, ok := options["stop"]; !ok {
		t.Fatalf("stop not passed: %+v", options)
	}
	if _, ok := options["response_format"]; !ok {
		t.Fatalf("response_format not passed: %+v", options)
	}
	if tools, ok := fake.req.Input["tools"].([]any); !ok || len(tools) != 1 {
		t.Fatalf("tools not passed: %+v", fake.req.Input["tools"])
	}
	if _, ok := fake.req.Input["tool_choice"]; ok {
		t.Fatalf("tool_choice must be omitted when the client omits it: %+v", fake.req.Input["tool_choice"])
	}
}

func TestChatCompletionsAcceptsStoreNoOp(t *testing.T) {
	fake := &fakeRunService{events: make(chan *iop.RunEvent, 2)}
	fake.events <- &iop.RunEvent{Type: "delta", Delta: "ok"}
	fake.events <- &iop.RunEvent{Type: "complete"}

	srv := NewServer(config.EdgeOpenAIConf{Adapter: "ollama"}, fake, nil)
	req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
		"model":"from-request",
		"messages":[{"role":"user","content":"hi"}],
		"store":false
	}`))
	w := httptest.NewRecorder()

	srv.handleChatCompletions(w, req)

	if w.Code != http.StatusOK {
		t.Fatalf("status: got %d body=%s", w.Code, w.Body.String())
	}
	if _, ok := fake.req.Input["store"]; ok {
		t.Fatalf("store must be accepted as a client compatibility no-op: %+v", fake.req.Input)
	}
}

func TestChatCompletionsOmitsProviderToolChoiceAuto(t *testing.T) {
	fake := &fakeRunService{events: make(chan *iop.RunEvent, 2)}
	fake.events <- &iop.RunEvent{Type: "delta", Delta: "ok"}
	fake.events <- &iop.RunEvent{Type: "complete"}

	srv := NewServer(config.EdgeOpenAIConf{Adapter: "ollama"}, fake, nil)
	req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
		"metadata":{},
		"model":"qwen3.6:35b",
		"messages":[{"role":"user","content":"hi"}],
		"tools":[{"type":"function","function":{"name":"lookup"}}],
		"tool_choice":"auto",
		"parallel_tool_calls":true,
		"stream_options":{"include_usage":true}
	}`))
	w := httptest.NewRecorder()

	srv.handleChatCompletions(w, req)

	if w.Code != http.StatusOK {
		t.Fatalf("status: got %d body=%s", w.Code, w.Body.String())
	}
	if _, ok := fake.req.Input["tool_choice"]; ok {
		t.Fatalf("tool_choice auto should be omitted for provider defaults: %+v", fake.req.Input["tool_choice"])
	}
	if _, ok := fake.req.Input["parallel_tool_calls"]; ok {
		t.Fatalf("parallel_tool_calls must not be forwarded: %+v", fake.req.Input)
	}
	if _, ok := fake.req.Input["stream_options"]; ok {
		t.Fatalf("stream_options must not be forwarded: %+v", fake.req.Input)
	}
}

func TestChatCompletionsPassesProviderForcedToolChoice(t *testing.T) {
	fake := &fakeRunService{events: make(chan *iop.RunEvent, 2)}
	fake.events <- &iop.RunEvent{Type: "delta", Delta: "ok"}
	fake.events <- &iop.RunEvent{Type: "complete"}

	srv := NewServer(config.EdgeOpenAIConf{Adapter: "ollama"}, fake, nil)
	req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
		"metadata":{},
		"model":"qwen3.6:35b",
		"messages":[{"role":"user","content":"hi"}],
		"tools":[{"type":"function","function":{"name":"lookup"}}],
		"tool_choice":{"type":"function","function":{"name":"lookup"}}
	}`))
	w := httptest.NewRecorder()

	srv.handleChatCompletions(w, req)

	if w.Code != http.StatusOK {
		t.Fatalf("status: got %d body=%s", w.Code, w.Body.String())
	}
	choice, ok := fake.req.Input["tool_choice"].(map[string]any)
	if !ok {
		t.Fatalf("tool_choice not forwarded as object: %+v", fake.req.Input["tool_choice"])
	}
	if choice["type"] != "function" {
		t.Fatalf("tool_choice type: %+v", choice)
	}
}

func TestChatCompletionsDowngradesCLIToolChoiceAutoToNone(t *testing.T) {
	fake := &fakeRunService{events: make(chan *iop.RunEvent, 2)}
	fake.events <- &iop.RunEvent{Type: "delta", Delta: "ok"}
	fake.events <- &iop.RunEvent{Type: "complete"}

	srv := NewServer(config.EdgeOpenAIConf{Adapter: "cli", Target: "codex"}, fake, nil)
	req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
		"model":"codex-cli",
		"messages":[{"role":"user","content":"hi"}],
		"tools":[{"type":"function","function":{"name":"lookup"}}],
		"tool_choice":"auto"
	}`))
	w := httptest.NewRecorder()

	srv.handleChatCompletions(w, req)

	if w.Code != http.StatusOK {
		t.Fatalf("status: got %d body=%s", w.Code, w.Body.String())
	}
	if fake.req.Input["tool_choice"] != "none" {
		t.Fatalf("tool_choice: got %+v, want none", fake.req.Input["tool_choice"])
	}
}

func TestChatCompletionsPassesThroughNativeToolCallsFromProviderRoute(t *testing.T) {
	fake := &fakeRunService{events: make(chan *iop.RunEvent, 1)}
	fake.events <- &iop.RunEvent{
		Type: "complete",
		Metadata: map[string]string{
			"finish_reason":                "tool_calls",
			runtimeMetadataOpenAIToolCalls: `[{"id":"call_1","type":"function","function":{"name":"run_commands","arguments":"{\"commands\":[\"git status\"]}"}}]`,
		},
	}

	srv := NewServer(config.EdgeOpenAIConf{Adapter: "ollama"}, fake, nil)
	req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
		"metadata":{},
		"model":"qwen3.6:35b",
		"messages":[{"role":"user","content":"status"}],
		"tools":[{"type":"function","function":{"name":"run_commands"}}],
		"tool_choice":"auto"
	}`))
	w := httptest.NewRecorder()

	srv.handleChatCompletions(w, req)

	if w.Code != http.StatusOK {
		t.Fatalf("status: got %d body=%s", w.Code, w.Body.String())
	}
	var resp chatCompletionResponse
	if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
		t.Fatalf("decode response: %v", err)
	}
	choice := resp.Choices[0]
	if choice.FinishReason != "tool_calls" {
		t.Fatalf("finish_reason: got %q, want tool_calls", choice.FinishReason)
	}
	if len(choice.Message.ToolCalls) != 1 {
		t.Fatalf("tool_calls len: got %d", len(choice.Message.ToolCalls))
	}
	call := choice.Message.ToolCalls[0].(map[string]any)
	if call["id"] != "call_1" {
		t.Fatalf("tool call id: got %+v", call["id"])
	}
}

func TestChatCompletionsNativeToolCallPreservesLeadingProse(t *testing.T) {
	fake := &fakeRunService{events: make(chan *iop.RunEvent, 2)}
	fake.events <- &iop.RunEvent{Type: "delta", Delta: "선행 prose\n\n<tool_call><function=run_commands><parameter=commands>[\"git status\"]</parameter></function></tool_call>"}
	fake.events <- &iop.RunEvent{
		Type: "complete",
		Metadata: map[string]string{
			"finish_reason":                "tool_calls",
			runtimeMetadataOpenAIToolCalls: `[{"id":"call_1","type":"function","function":{"name":"run_commands","arguments":"{\"commands\":[\"git status\"]}"}}]`,
		},
	}

	srv := NewServer(config.EdgeOpenAIConf{Adapter: "ollama"}, fake, nil)
	req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
		"metadata":{},
		"model":"qwen3.6:35b",
		"messages":[{"role":"user","content":"status"}],
		"tools":[{"type":"function","function":{"name":"run_commands"}}],
		"tool_choice":"auto"
	}`))
	w := httptest.NewRecorder()

	srv.handleChatCompletions(w, req)

	if w.Code != http.StatusOK {
		t.Fatalf("status: got %d body=%s", w.Code, w.Body.String())
	}
	body := w.Body.String()
	if strings.Contains(body, "<tool_call") || strings.Contains(body, `\u003ctool_call`) {
		t.Fatalf("raw tool_call leaked in response: %s", body)
	}
	var resp chatCompletionResponse
	if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
		t.Fatalf("decode response: %v", err)
	}
	choice := resp.Choices[0]
	if choice.Message.Content != "선행 prose" {
		t.Fatalf("content: got %q", choice.Message.Content)
	}
	if choice.FinishReason != "tool_calls" || len(choice.Message.ToolCalls) != 1 {
		t.Fatalf("expected native tool call: %+v", choice)
	}
	call := choice.Message.ToolCalls[0].(map[string]any)
	if call["id"] != "call_1" {
		t.Fatalf("tool call id: got %+v", call["id"])
	}
}

func TestChatCompletionsNormalizesNativeToolCallArgumentsForProviderRoute(t *testing.T) {
	fake := &fakeRunService{events: make(chan *iop.RunEvent, 1)}
	fake.events <- &iop.RunEvent{
		Type: "complete",
		Metadata: map[string]string{
			"finish_reason":                "tool_calls",
			runtimeMetadataOpenAIToolCalls: `[{"id":"call_1","type":"function","function":{"name":"run_commands","arguments":"{\"commands\":\"[{\\\"command\\\":\\\"cd /config/workspace/iop && git status\\\"}]\"}"}}]`,
		},
	}

	srv := NewServer(config.EdgeOpenAIConf{Adapter: "ollama"}, fake, nil)
	req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
		"metadata":{},
		"model":"qwen3.6:35b",
		"messages":[{"role":"user","content":"status"}],
		"tools":[{"type":"function","function":{"name":"run_commands","parameters":{"type":"object","properties":{"commands":{"type":"array","items":{"type":"string"}}},"required":["commands"]}}}],
		"tool_choice":"auto"
	}`))
	w := httptest.NewRecorder()

	srv.handleChatCompletions(w, req)

	if w.Code != http.StatusOK {
		t.Fatalf("status: got %d body=%s", w.Code, w.Body.String())
	}
	var resp chatCompletionResponse
	if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
		t.Fatalf("decode response: %v", err)
	}
	call := resp.Choices[0].Message.ToolCalls[0].(map[string]any)
	fn := call["function"].(map[string]any)
	var args map[string][]string
	if err := json.Unmarshal([]byte(fn["arguments"].(string)), &args); err != nil {
		t.Fatalf("arguments JSON: %v", err)
	}
	if len(args["commands"]) != 1 || args["commands"][0] != "cd /config/workspace/iop && git status" {
		t.Fatalf("commands args: %+v", args["commands"])
	}
}

func TestChatCompletionsRetriesMalformedToolCallBeforeResponse(t *testing.T) {
	fake := &fakeRunService{
		eventRuns: []chan *iop.RunEvent{
			bufferedRunEvents(&iop.RunEvent{
				Type: "complete",
				Metadata: map[string]string{
					"finish_reason":                "tool_calls",
					runtimeMetadataOpenAIToolCalls: `[{"id":"call_bad","type":"function","function":{"name":"run_commands","arguments":"{\"commands\":\"git status\"}"}}]`,
				},
			}),
			bufferedRunEvents(&iop.RunEvent{
				Type: "complete",
				Metadata: map[string]string{
					"finish_reason":                "tool_calls",
					runtimeMetadataOpenAIToolCalls: `[{"id":"call_good","type":"function","function":{"name":"run_commands","arguments":"{\"commands\":[\"git status\"]}"}}]`,
				},
			}),
		},
		runIDs: []string{"run-bad", "run-good"},
	}

	srv := NewServer(config.EdgeOpenAIConf{Adapter: "ollama"}, fake, nil)
	req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
		"metadata":{},
		"model":"qwen3.6:35b",
		"messages":[{"role":"user","content":"status"}],
		"tools":[{"type":"function","function":{"name":"run_commands","parameters":{"type":"object","properties":{"commands":{"type":"array","items":{"type":"string"}}},"required":["commands"],"additionalProperties":false}}}],
		"tool_choice":"auto"
	}`))
	w := httptest.NewRecorder()

	srv.handleChatCompletions(w, req)

	if w.Code != http.StatusOK {
		t.Fatalf("status: got %d body=%s", w.Code, w.Body.String())
	}
	reqs := fake.reqsSnapshot()
	if len(reqs) != 2 {
		t.Fatalf("submit attempts: got %d want 2", len(reqs))
	}
	if reqs[0].Metadata[runtimeMetadataToolValidationAttempt] != "1" {
		t.Fatalf("first attempt metadata: %+v", reqs[0].Metadata)
	}
	if reqs[1].Metadata[runtimeMetadataToolValidationAttempt] != "2" || reqs[1].Metadata[runtimeMetadataToolValidationRetryOf] != "run-bad" {
		t.Fatalf("retry metadata: %+v", reqs[1].Metadata)
	}
	if !strings.Contains(reqs[1].Metadata[runtimeMetadataToolValidationFailure], "$.arguments.commands type string") {
		t.Fatalf("retry failure reason: %+v", reqs[1].Metadata)
	}

	var resp chatCompletionResponse
	if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
		t.Fatalf("decode response: %v", err)
	}
	if resp.ID != "chatcmpl-run-good" {
		t.Fatalf("response id: got %q", resp.ID)
	}
	call := resp.Choices[0].Message.ToolCalls[0].(map[string]any)
	if call["id"] != "call_good" {
		t.Fatalf("tool call id: got %+v", call["id"])
	}
}

func TestChatCompletionsFailsMalformedToolCallAfterRetryLimit(t *testing.T) {
	invalidComplete := func(id string) *iop.RunEvent {
		return &iop.RunEvent{
			Type: "complete",
			Metadata: map[string]string{
				"finish_reason":                "tool_calls",
				runtimeMetadataOpenAIToolCalls: fmt.Sprintf(`[{"id":%q,"type":"function","function":{"name":"run_commands","arguments":"{\"unexpected\":true}"}}]`, id),
			},
		}
	}
	fake := &fakeRunService{
		eventRuns: []chan *iop.RunEvent{
			bufferedRunEvents(invalidComplete("call_bad_1")),
			bufferedRunEvents(invalidComplete("call_bad_2")),
		},
		runIDs: []string{"run-bad-1", "run-bad-2"},
	}

	srv := NewServer(config.EdgeOpenAIConf{Adapter: "ollama"}, fake, nil)
	req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
		"metadata":{},
		"model":"qwen3.6:35b",
		"messages":[{"role":"user","content":"status"}],
		"tools":[{"type":"function","function":{"name":"run_commands","parameters":{"type":"object","properties":{"commands":{"type":"array","items":{"type":"string"}}},"required":["commands"],"additionalProperties":false}}}],
		"tool_choice":{"type":"function","function":{"name":"run_commands"}}
	}`))
	w := httptest.NewRecorder()

	srv.handleChatCompletions(w, req)

	if w.Code != http.StatusBadGateway {
		t.Fatalf("status: got %d body=%s", w.Code, w.Body.String())
	}
	if !strings.Contains(w.Body.String(), `"type":"tool_validation_error"`) || !strings.Contains(w.Body.String(), "$.arguments.commands is required") {
		t.Fatalf("expected tool validation error body, got %s", w.Body.String())
	}
	if reqs := fake.reqsSnapshot(); len(reqs) != 2 {
		t.Fatalf("submit attempts: got %d want 2", len(reqs))
	}
}

func TestChatCompletionsToolChoiceNoneSkipsRuntimeToolValidation(t *testing.T) {
	fake := &fakeRunService{events: make(chan *iop.RunEvent, 1)}
	fake.events <- &iop.RunEvent{
		Type: "complete",
		Metadata: map[string]string{
			"finish_reason":                "tool_calls",
			runtimeMetadataOpenAIToolCalls: `[{"id":"call_1","type":"function","function":{"name":"unknown_tool","arguments":"not-json"}}]`,
		},
	}

	srv := NewServer(config.EdgeOpenAIConf{Adapter: "ollama"}, fake, nil)
	req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
		"metadata":{},
		"model":"qwen3.6:35b",
		"messages":[{"role":"user","content":"status"}],
		"tools":[{"type":"function","function":{"name":"run_commands","parameters":{"type":"object","properties":{"commands":{"type":"array","items":{"type":"string"}}},"required":["commands"]}}}],
		"tool_choice":"none"
	}`))
	w := httptest.NewRecorder()

	srv.handleChatCompletions(w, req)

	if w.Code != http.StatusOK {
		t.Fatalf("status: got %d body=%s", w.Code, w.Body.String())
	}
	reqs := fake.reqsSnapshot()
	if len(reqs) != 1 {
		t.Fatalf("submit attempts: got %d want 1", len(reqs))
	}
	if _, ok := reqs[0].Metadata[runtimeMetadataToolValidationAttempt]; ok {
		t.Fatalf("tool_choice none should not enable validation metadata: %+v", reqs[0].Metadata)
	}
}

func TestChatCompletionsToolsStreamRetriesMalformedToolCallBeforeChunk(t *testing.T) {
	fake := &fakeRunService{
		eventRuns: []chan *iop.RunEvent{
			bufferedRunEvents(&iop.RunEvent{
				Type: "complete",
				Metadata: map[string]string{
					"finish_reason":                "tool_calls",
					runtimeMetadataOpenAIToolCalls: `[{"id":"call_bad","type":"function","function":{"name":"edit","arguments":"{\"edits\":[{\"path\":\"apps/client/test/client_bootstrap_test.dart\",\"newText\":\"ok\"}]}"}}]`,
				},
			}),
			bufferedRunEvents(&iop.RunEvent{
				Type: "complete",
				Metadata: map[string]string{
					"finish_reason":                "tool_calls",
					runtimeMetadataOpenAIToolCalls: `[{"id":"call_good","type":"function","function":{"name":"edit","arguments":"{\"path\":\"apps/client/test/client_bootstrap_test.dart\",\"edits\":[{\"newText\":\"ok\"}]}"}}]`,
				},
			}),
		},
		runIDs: []string{"run-bad", "run-good"},
	}

	srv := NewServer(config.EdgeOpenAIConf{Adapter: "ollama"}, fake, nil)
	req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
		"metadata":{},
		"model":"qwen3.6:35b",
		"stream":true,
		"messages":[{"role":"user","content":"edit"}],
		"tools":[{"type":"function","function":{"name":"edit","parameters":{"type":"object","properties":{"path":{"type":"string"},"edits":{"type":"array","items":{"type":"object","properties":{"newText":{"type":"string"}},"required":["newText"],"additionalProperties":false}}},"required":["path","edits"],"additionalProperties":false}}}],
		"tool_choice":"auto"
	}`))
	w := httptest.NewRecorder()

	srv.handleChatCompletions(w, req)

	if w.Code != http.StatusOK {
		t.Fatalf("status: got %d body=%s", w.Code, w.Body.String())
	}
	reqs := fake.reqsSnapshot()
	if len(reqs) != 2 {
		t.Fatalf("submit attempts: got %d want 2", len(reqs))
	}
	if reqs[0].Metadata[runtimeMetadataToolValidationAttempt] != "1" {
		t.Fatalf("first attempt metadata: %+v", reqs[0].Metadata)
	}
	if reqs[1].Metadata[runtimeMetadataToolValidationAttempt] != "2" || reqs[1].Metadata[runtimeMetadataToolValidationRetryOf] != "run-bad" {
		t.Fatalf("retry metadata: %+v", reqs[1].Metadata)
	}
	if !strings.Contains(reqs[1].Metadata[runtimeMetadataToolValidationFailure], "$.arguments.path is required") {
		t.Fatalf("retry failure reason: %+v", reqs[1].Metadata)
	}
	body := w.Body.String()
	if strings.Contains(body, "call_bad") {
		t.Fatalf("buffered stream leaked invalid tool call before retry:\n%s", body)
	}
	if !strings.Contains(body, "call_good") || !strings.Contains(body, `"finish_reason":"tool_calls"`) || !strings.Contains(body, "data: [DONE]") {
		t.Fatalf("buffered stream did not emit validated tool call:\n%s", body)
	}
}

func TestChatCompletionsStrictBufferedStreamRetriesMalformedToolCallBeforeChunk(t *testing.T) {
	fake := &fakeRunService{
		eventRuns: []chan *iop.RunEvent{
			bufferedRunEvents(&iop.RunEvent{
				Type: "complete",
				Metadata: map[string]string{
					"finish_reason":                "tool_calls",
					runtimeMetadataOpenAIToolCalls: `[{"id":"call_bad","type":"function","function":{"name":"run_commands","arguments":"{\"commands\":\"git status\"}"}}]`,
				},
			}),
			bufferedRunEvents(&iop.RunEvent{
				Type: "complete",
				Metadata: map[string]string{
					"finish_reason":                "tool_calls",
					runtimeMetadataOpenAIToolCalls: `[{"id":"call_good","type":"function","function":{"name":"run_commands","arguments":"{\"commands\":[\"git status\"]}"}}]`,
				},
			}),
		},
		runIDs: []string{"run-bad", "run-good"},
	}

	srv := NewServer(config.EdgeOpenAIConf{Adapter: "ollama", StrictOutput: true, StrictStreamBuffer: true}, fake, nil)
	req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
		"metadata":{},
		"model":"qwen3.6:35b",
		"stream":true,
		"messages":[{"role":"user","content":"status"}],
		"tools":[{"type":"function","function":{"name":"run_commands","parameters":{"type":"object","properties":{"commands":{"type":"array","items":{"type":"string"}}},"required":["commands"],"additionalProperties":false}}}],
		"tool_choice":"auto"
	}`))
	w := httptest.NewRecorder()

	srv.handleChatCompletions(w, req)

	if w.Code != http.StatusOK {
		t.Fatalf("status: got %d body=%s", w.Code, w.Body.String())
	}
	reqs := fake.reqsSnapshot()
	if len(reqs) != 2 {
		t.Fatalf("submit attempts: got %d want 2", len(reqs))
	}
	if reqs[0].Metadata[runtimeMetadataToolValidationAttempt] != "1" {
		t.Fatalf("first attempt metadata: %+v", reqs[0].Metadata)
	}
	if reqs[1].Metadata[runtimeMetadataToolValidationAttempt] != "2" || reqs[1].Metadata[runtimeMetadataToolValidationRetryOf] != "run-bad" {
		t.Fatalf("retry metadata: %+v", reqs[1].Metadata)
	}
	body := w.Body.String()
	if strings.Contains(body, "call_bad") {
		t.Fatalf("buffered stream leaked invalid tool call before retry:\n%s", body)
	}
	if !strings.Contains(body, "call_good") || !strings.Contains(body, `"finish_reason":"tool_calls"`) || !strings.Contains(body, "data: [DONE]") {
		t.Fatalf("buffered stream did not emit validated tool call:\n%s", body)
	}
}

func TestChatCompletionsStrictBufferedStreamFailsMalformedToolCallAfterRetryLimit(t *testing.T) {
	invalidComplete := func(id string) *iop.RunEvent {
		return &iop.RunEvent{
			Type: "complete",
			Metadata: map[string]string{
				"finish_reason":                "tool_calls",
				runtimeMetadataOpenAIToolCalls: fmt.Sprintf(`[{"id":%q,"type":"function","function":{"name":"run_commands","arguments":"{\"unexpected\":true}"}}]`, id),
			},
		}
	}
	fake := &fakeRunService{
		eventRuns: []chan *iop.RunEvent{
			bufferedRunEvents(invalidComplete("call_bad_1")),
			bufferedRunEvents(invalidComplete("call_bad_2")),
		},
		runIDs: []string{"run-bad-1", "run-bad-2"},
	}

	srv := NewServer(config.EdgeOpenAIConf{Adapter: "ollama", StrictOutput: true, StrictStreamBuffer: true}, fake, nil)
	req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
		"metadata":{},
		"model":"qwen3.6:35b",
		"stream":true,
		"messages":[{"role":"user","content":"status"}],
		"tools":[{"type":"function","function":{"name":"run_commands","parameters":{"type":"object","properties":{"commands":{"type":"array","items":{"type":"string"}}},"required":["commands"],"additionalProperties":false}}}],
		"tool_choice":{"type":"function","function":{"name":"run_commands"}}
	}`))
	w := httptest.NewRecorder()

	srv.handleChatCompletions(w, req)

	if w.Code != http.StatusOK {
		t.Fatalf("status: got %d body=%s", w.Code, w.Body.String())
	}
	body := w.Body.String()
	if !strings.Contains(body, `"type":"tool_validation_error"`) || !strings.Contains(body, "$.arguments.commands is required") {
		t.Fatalf("expected tool_validation_error SSE body, got:\n%s", body)
	}
	if strings.Contains(body, "call_bad") {
		t.Fatalf("buffered stream leaked invalid tool call chunk after retry limit:\n%s", body)
	}
	if !strings.Contains(body, "data: [DONE]") {
		t.Fatalf("buffered stream error should terminate with [DONE]:\n%s", body)
	}
	if reqs := fake.reqsSnapshot(); len(reqs) != 2 {
		t.Fatalf("submit attempts: got %d want 2", len(reqs))
	}
}

func TestChatCompletionsRejectsUnsupportedFields(t *testing.T) {
	srv := NewServer(config.EdgeOpenAIConf{Adapter: "ollama", Target: "llama"}, &fakeRunService{}, nil)
	cases := []struct {
		name string
		body string
	}{
		{"options wrapper", `{"model":"m","messages":[{"role":"user","content":"hi"}],"options":{"num_ctx":8192}}`},
		{"format", `{"model":"m","messages":[{"role":"user","content":"hi"}],"format":"json"}`},
		{"keep_alive", `{"model":"m","messages":[{"role":"user","content":"hi"}],"keep_alive":"10m"}`},
		{"bad max_tokens", `{"model":"m","messages":[{"role":"user","content":"hi"}],"max_tokens":0}`},
		{"bad top_p", `{"model":"m","messages":[{"role":"user","content":"hi"}],"top_p":2}`},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(tc.body))
			w := httptest.NewRecorder()
			srv.routes().ServeHTTP(w, req)
			if w.Code != http.StatusBadRequest {
				t.Fatalf("got %d want 400, body=%s", w.Code, w.Body.String())
			}
		})
	}
}
