package adapters_test

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

	"go.uber.org/zap"

	"iop/apps/node/internal/adapters"
	noderuntime "iop/packages/go/execution"
	iop "iop/proto/gen/iop"
)

type testSink struct {
	mu     sync.Mutex
	events []noderuntime.RuntimeEvent
}

func (s *testSink) Emit(_ context.Context, event noderuntime.RuntimeEvent) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.events = append(s.events, event)
	return nil
}

// --- BuildFromPayload tests ---

func TestBuildFromPayload_EmptyPayloadRegistersNoAdapters(t *testing.T) {
	reg, err := adapters.BuildFromPayload(&iop.NodeConfigPayload{}, zap.NewNop())
	if err != nil {
		t.Fatalf("build from payload: %v", err)
	}
	if got := len(reg.All()); got != 0 {
		t.Fatalf("expected no adapters, got %d", got)
	}
	if _, ok := reg.Get("mock"); ok {
		t.Fatal("mock adapter must not be registered implicitly")
	}
}

func TestBuildFromPayload_ExplicitMockEnabled(t *testing.T) {
	reg, err := adapters.BuildFromPayload(&iop.NodeConfigPayload{
		Adapters: []*iop.AdapterConfig{
			{Type: "mock", Enabled: true},
		},
	}, zap.NewNop())
	if err != nil {
		t.Fatalf("build from payload: %v", err)
	}
	if _, ok := reg.Get("mock"); !ok {
		t.Fatal("expected explicit mock adapter to be registered")
	}
}

func TestBuildFromPayload_OllamaEnabled(t *testing.T) {
	reg, err := adapters.BuildFromPayload(&iop.NodeConfigPayload{
		Adapters: []*iop.AdapterConfig{
			{
				Type:    "ollama",
				Enabled: true,
				Config:  &iop.AdapterConfig_Ollama{Ollama: &iop.OllamaAdapterConfig{BaseUrl: "http://localhost:11434"}},
			},
		},
	}, zap.NewNop())
	if err != nil {
		t.Fatalf("build from payload: %v", err)
	}
	if _, ok := reg.Get("ollama"); !ok {
		t.Fatal("expected ollama adapter to be registered")
	}
}

func TestBuildFromPayload_MultipleAdapters(t *testing.T) {
	reg, err := adapters.BuildFromPayload(&iop.NodeConfigPayload{
		Adapters: []*iop.AdapterConfig{
			{Type: "ollama", Enabled: true, Config: &iop.AdapterConfig_Ollama{Ollama: &iop.OllamaAdapterConfig{BaseUrl: "x"}}},
			{Type: "vllm", Enabled: true, Config: &iop.AdapterConfig_Vllm{Vllm: &iop.VllmAdapterConfig{Endpoint: "http://localhost:8000"}}},
		},
	}, zap.NewNop())
	if err != nil {
		t.Fatalf("build from payload: %v", err)
	}
	for _, name := range []string{"ollama", "vllm"} {
		if _, ok := reg.Get(name); !ok {
			t.Fatalf("expected %s adapter to be registered", name)
		}
	}
}

func TestBuildFromPayload_VllmEnabled(t *testing.T) {
	reg, err := adapters.BuildFromPayload(&iop.NodeConfigPayload{
		Adapters: []*iop.AdapterConfig{
			{Type: "vllm", Enabled: true, Config: &iop.AdapterConfig_Vllm{Vllm: &iop.VllmAdapterConfig{Endpoint: "http://localhost:8000"}}},
		},
	}, zap.NewNop())
	if err != nil {
		t.Fatalf("build from payload: %v", err)
	}
	if _, ok := reg.Get("vllm"); !ok {
		t.Fatal("expected vllm adapter to be registered")
	}
}

func TestBuildFromPayload_UnknownType(t *testing.T) {
	_, err := adapters.BuildFromPayload(&iop.NodeConfigPayload{
		Adapters: []*iop.AdapterConfig{
			{Type: "unknown", Enabled: true},
		},
	}, zap.NewNop())
	if err == nil {
		t.Fatal("expected unknown adapter type error")
	}
}

// --- Registry lifecycle tests ---

type lifecycleAdapter struct {
	name string
	log  *[]string
}

func (a *lifecycleAdapter) Name() string { return a.name }
func (a *lifecycleAdapter) Capabilities(_ context.Context) (noderuntime.Capabilities, error) {
	return noderuntime.Capabilities{AdapterName: a.name}, nil
}
func (a *lifecycleAdapter) Execute(_ context.Context, _ noderuntime.ExecutionSpec, _ noderuntime.EventSink) error {
	return nil
}
func (a *lifecycleAdapter) Start(_ context.Context) error {
	*a.log = append(*a.log, "start:"+a.name)
	return nil
}
func (a *lifecycleAdapter) Stop(_ context.Context) error {
	*a.log = append(*a.log, "stop:"+a.name)
	return nil
}

type failingLifecycleAdapter struct {
	name string
	log  *[]string
}

func (a *failingLifecycleAdapter) Name() string { return a.name }
func (a *failingLifecycleAdapter) Capabilities(_ context.Context) (noderuntime.Capabilities, error) {
	return noderuntime.Capabilities{AdapterName: a.name}, nil
}
func (a *failingLifecycleAdapter) Execute(_ context.Context, _ noderuntime.ExecutionSpec, _ noderuntime.EventSink) error {
	return nil
}
func (a *failingLifecycleAdapter) Start(_ context.Context) error {
	*a.log = append(*a.log, "start:"+a.name)
	return fmt.Errorf("start failed: %s", a.name)
}
func (a *failingLifecycleAdapter) Stop(_ context.Context) error {
	*a.log = append(*a.log, "stop:"+a.name)
	return nil
}

type failingStopAdapter struct {
	name    string
	log     *[]string
	stopErr error
}

func (a *failingStopAdapter) Name() string { return a.name }
func (a *failingStopAdapter) Capabilities(_ context.Context) (noderuntime.Capabilities, error) {
	return noderuntime.Capabilities{AdapterName: a.name}, nil
}
func (a *failingStopAdapter) Execute(_ context.Context, _ noderuntime.ExecutionSpec, _ noderuntime.EventSink) error {
	return nil
}
func (a *failingStopAdapter) Start(_ context.Context) error {
	*a.log = append(*a.log, "start:"+a.name)
	return nil
}
func (a *failingStopAdapter) Stop(_ context.Context) error {
	*a.log = append(*a.log, "stop:"+a.name)
	return a.stopErr
}

func TestRegistryLifecycle_StartStopOrder(t *testing.T) {
	log := []string{}
	reg := noderuntime.NewRegistry()
	reg.Register(&lifecycleAdapter{name: "first", log: &log})
	reg.Register(&lifecycleAdapter{name: "second", log: &log})

	ctx := context.Background()
	if err := reg.Start(ctx); err != nil {
		t.Fatalf("Start: %v", err)
	}
	if err := reg.Stop(ctx); err != nil {
		t.Fatalf("Stop: %v", err)
	}

	want := []string{"start:first", "start:second", "stop:second", "stop:first"}
	if len(log) != len(want) {
		t.Fatalf("expected %v, got %v", want, log)
	}
	for i, got := range log {
		if got != want[i] {
			t.Fatalf("log[%d]: want %q, got %q", i, want[i], got)
		}
	}
}

func TestRegistryLifecycle_StartFailureStopsStartedAdapters(t *testing.T) {
	log := []string{}
	reg := noderuntime.NewRegistry()
	reg.Register(&lifecycleAdapter{name: "first", log: &log})
	reg.Register(&failingLifecycleAdapter{name: "second", log: &log})

	ctx := context.Background()
	if err := reg.Start(ctx); err == nil {
		t.Fatal("expected Start to return error")
	}

	want := []string{"start:first", "start:second", "stop:first"}
	if len(log) != len(want) {
		t.Fatalf("expected log %v, got %v", want, log)
	}
	for i, got := range log {
		if got != want[i] {
			t.Fatalf("log[%d]: want %q, got %q", i, want[i], got)
		}
	}
}

func TestRegistryLifecycle_StopContinuesOnFailingAdapter(t *testing.T) {
	log := []string{}
	reg := noderuntime.NewRegistry()
	reg.Register(&lifecycleAdapter{name: "first", log: &log})
	reg.Register(&failingStopAdapter{name: "second", log: &log, stopErr: fmt.Errorf("stop failed: second")})

	ctx := context.Background()
	if err := reg.Start(ctx); err != nil {
		t.Fatalf("Start: %v", err)
	}
	if err := reg.Stop(ctx); err == nil {
		t.Fatal("expected non-nil error from Stop")
	}

	want := []string{"start:first", "start:second", "stop:second", "stop:first"}
	if len(log) != len(want) {
		t.Fatalf("expected log %v, got %v", want, log)
	}
	for i, got := range log {
		if got != want[i] {
			t.Fatalf("log[%d]: want %q, got %q", i, want[i], got)
		}
	}
}

func TestRegistryLifecycle_NonLifecycleAdapterSkipped(t *testing.T) {
	reg, err := adapters.BuildFromPayload(&iop.NodeConfigPayload{
		Adapters: []*iop.AdapterConfig{
			{Type: "mock", Enabled: true},
		},
	}, zap.NewNop())
	if err != nil {
		t.Fatalf("build: %v", err)
	}
	ctx := context.Background()
	if err := reg.Start(ctx); err != nil {
		t.Fatalf("Start: %v", err)
	}
	if err := reg.Stop(ctx); err != nil {
		t.Fatalf("Stop: %v", err)
	}
}

// --- Registry namespace tests ---

func TestRegistry_MultiInstanceSameType(t *testing.T) {
	log := []string{}
	reg := noderuntime.NewRegistry()
	reg.RegisterKeyed("ollama@local", "ollama", &lifecycleAdapter{name: "ollama@local", log: &log})
	reg.RegisterKeyed("ollama@dgx", "ollama", &lifecycleAdapter{name: "ollama@dgx", log: &log})

	a1, ok1 := reg.Get("ollama@local")
	a2, ok2 := reg.Get("ollama@dgx")
	if !ok1 || !ok2 {
		t.Fatalf("expected both instances to be retrievable by key; ok1=%v ok2=%v", ok1, ok2)
	}
	if a1 == a2 {
		t.Fatal("expected distinct adapter instances")
	}
	if a1.Name() != "ollama@local" || a2.Name() != "ollama@dgx" {
		t.Errorf("adapter names: got %q and %q", a1.Name(), a2.Name())
	}

	all := reg.All()
	if len(all) != 2 {
		t.Fatalf("expected 2 adapters in All(), got %d", len(all))
	}
}

func TestRegistry_LegacyLookupSingleInstance(t *testing.T) {
	reg := noderuntime.NewRegistry()
	reg.RegisterKeyed("ollama@prod", "ollama", &lifecycleAdapter{name: "ollama@prod", log: nil})

	// Exact key lookup must work.
	a, err := reg.Lookup("ollama@prod")
	if err != nil {
		t.Fatalf("Lookup by instance key: %v", err)
	}
	if a.Name() != "ollama@prod" {
		t.Errorf("expected ollama@prod, got %q", a.Name())
	}

	// Legacy type-name lookup works when there is exactly one instance.
	a2, err := reg.Lookup("ollama")
	if err != nil {
		t.Fatalf("legacy Lookup by type name: %v", err)
	}
	if a2.Name() != "ollama@prod" {
		t.Errorf("expected ollama@prod via legacy lookup, got %q", a2.Name())
	}
}

func TestRegistry_AmbiguousLegacyLookup(t *testing.T) {
	log := []string{}
	reg := noderuntime.NewRegistry()
	reg.RegisterKeyed("ollama@local", "ollama", &lifecycleAdapter{name: "ollama@local", log: &log})
	reg.RegisterKeyed("ollama@dgx", "ollama", &lifecycleAdapter{name: "ollama@dgx", log: &log})

	_, err := reg.Lookup("ollama")
	if err == nil {
		t.Fatal("expected ambiguous error for type-name lookup with multiple instances")
	}
	if !strings.Contains(err.Error(), "ambiguous") {
		t.Errorf("expected 'ambiguous' in error, got %v", err)
	}
	if !strings.Contains(err.Error(), "instance key") {
		t.Errorf("expected guidance to use instance key, got %v", err)
	}
}

func TestRegistry_LifecycleOrderMultiInstance(t *testing.T) {
	log := []string{}
	reg := noderuntime.NewRegistry()
	reg.RegisterKeyed("ollama@local", "ollama", &lifecycleAdapter{name: "ollama@local", log: &log})
	reg.RegisterKeyed("ollama@dgx", "ollama", &lifecycleAdapter{name: "ollama@dgx", log: &log})
	reg.RegisterKeyed("cli", "cli", &lifecycleAdapter{name: "cli", log: &log})

	ctx := context.Background()
	if err := reg.Start(ctx); err != nil {
		t.Fatalf("Start: %v", err)
	}
	if err := reg.Stop(ctx); err != nil {
		t.Fatalf("Stop: %v", err)
	}

	want := []string{
		"start:ollama@local", "start:ollama@dgx", "start:cli",
		"stop:cli", "stop:ollama@dgx", "stop:ollama@local",
	}
	if len(log) != len(want) {
		t.Fatalf("expected log %v, got %v", want, log)
	}
	for i, got := range log {
		if got != want[i] {
			t.Fatalf("log[%d]: want %q, got %q", i, want[i], got)
		}
	}
}

func TestBuildFromPayload_MultiInstanceSameType(t *testing.T) {
	reg, err := adapters.BuildFromPayload(&iop.NodeConfigPayload{
		Adapters: []*iop.AdapterConfig{
			{
				Type:    "ollama",
				Name:    "ollama@local",
				Enabled: true,
				Config:  &iop.AdapterConfig_Ollama{Ollama: &iop.OllamaAdapterConfig{BaseUrl: "http://localhost:11434"}},
			},
			{
				Type:    "ollama",
				Name:    "ollama@dgx",
				Enabled: true,
				Config:  &iop.AdapterConfig_Ollama{Ollama: &iop.OllamaAdapterConfig{BaseUrl: "http://192.168.1.10:11434"}},
			},
		},
	}, zap.NewNop())
	if err != nil {
		t.Fatalf("build from payload: %v", err)
	}

	if len(reg.All()) != 2 {
		t.Fatalf("expected 2 adapters, got %d", len(reg.All()))
	}
	if _, ok := reg.Get("ollama@local"); !ok {
		t.Error("expected ollama@local to be registered")
	}
	if _, ok := reg.Get("ollama@dgx"); !ok {
		t.Error("expected ollama@dgx to be registered")
	}
	// Legacy type-name lookup must fail with ambiguous error.
	if _, err := reg.Lookup("ollama"); err == nil {
		t.Error("expected ambiguous error for legacy type-name lookup")
	}
	// Exact key lookup must succeed.
	if _, err := reg.Lookup("ollama@local"); err != nil {
		t.Errorf("Lookup ollama@local: %v", err)
	}
}

func TestBuildFromPayload_OllamaMultiInstanceExecution(t *testing.T) {
	var (
		gotModelA  string
		gotNumCtxA int
		gotModelB  string
		gotNumCtxB int
	)

	makeOllamaServer := func(gotModel *string, gotNumCtx *int, reply string) *httptest.Server {
		return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.URL.Path == "/api/tags" {
				_, _ = w.Write([]byte(`{"models":[]}`))
				return
			}
			if r.URL.Path != "/api/chat" {
				return
			}
			var req struct {
				Model   string         `json:"model"`
				Options map[string]any `json:"options"`
			}
			if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
				t.Errorf("decode: %v", err)
				return
			}
			*gotModel = req.Model
			if v, ok := req.Options["num_ctx"].(float64); ok {
				*gotNumCtx = int(v)
			}
			w.Header().Set("Content-Type", "application/x-ndjson")
			_, _ = fmt.Fprintf(w, `{"message":{"role":"assistant","content":"%s"},"done":false}`+"\n", reply)
			_, _ = w.Write([]byte(`{"done":true}` + "\n"))
		}))
	}

	serverA := makeOllamaServer(&gotModelA, &gotNumCtxA, "a")
	defer serverA.Close()
	serverB := makeOllamaServer(&gotModelB, &gotNumCtxB, "b")
	defer serverB.Close()

	reg, err := adapters.BuildFromPayload(&iop.NodeConfigPayload{
		Adapters: []*iop.AdapterConfig{
			{
				Type:    "ollama",
				Name:    "ollama@local",
				Enabled: true,
				Config:  &iop.AdapterConfig_Ollama{Ollama: &iop.OllamaAdapterConfig{BaseUrl: serverA.URL, ContextSize: 4096}},
			},
			{
				Type:    "ollama",
				Name:    "ollama@dgx",
				Enabled: true,
				Config:  &iop.AdapterConfig_Ollama{Ollama: &iop.OllamaAdapterConfig{BaseUrl: serverB.URL, ContextSize: 8192}},
			},
		},
	}, zap.NewNop())
	if err != nil {
		t.Fatalf("build: %v", err)
	}

	adapterA, ok := reg.Get("ollama@local")
	if !ok {
		t.Fatal("expected ollama@local")
	}
	adapterB, ok := reg.Get("ollama@dgx")
	if !ok {
		t.Fatal("expected ollama@dgx")
	}

	if err := adapterA.Execute(context.Background(), noderuntime.ExecutionSpec{
		RunID: "run-a", Target: "model-a", Input: map[string]any{"prompt": "test"},
	}, &testSink{}); err != nil {
		t.Fatalf("A execute: %v", err)
	}
	if err := adapterB.Execute(context.Background(), noderuntime.ExecutionSpec{
		RunID: "run-b", Target: "model-b", Input: map[string]any{"prompt": "test"},
	}, &testSink{}); err != nil {
		t.Fatalf("B execute: %v", err)
	}

	if gotModelA != "model-a" {
		t.Errorf("A model: got %q want model-a", gotModelA)
	}
	if gotNumCtxA != 4096 {
		t.Errorf("A num_ctx: got %d want 4096", gotNumCtxA)
	}
	if gotModelB != "model-b" {
		t.Errorf("B model: got %q want model-b", gotModelB)
	}
	if gotNumCtxB != 8192 {
		t.Errorf("B num_ctx: got %d want 8192", gotNumCtxB)
	}

	capsA, _ := adapterA.Capabilities(context.Background())
	if capsA.InstanceKey != "ollama@local" {
		t.Errorf("A InstanceKey: got %q want ollama@local", capsA.InstanceKey)
	}
	capsB, _ := adapterB.Capabilities(context.Background())
	if capsB.InstanceKey != "ollama@dgx" {
		t.Errorf("B InstanceKey: got %q want ollama@dgx", capsB.InstanceKey)
	}
}

func TestBuildFromPayload_VllmExecution(t *testing.T) {
	var gotModel string
	var gotStream bool

	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path == "/v1/models" {
			_, _ = w.Write([]byte(`{"object":"list","data":[]}`))
			return
		}
		if r.URL.Path != "/v1/chat/completions" {
			t.Errorf("unexpected path: %s", r.URL.Path)
			return
		}
		var req struct {
			Model  string `json:"model"`
			Stream bool   `json:"stream"`
		}
		if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
			t.Errorf("decode: %v", err)
			return
		}
		gotModel = req.Model
		gotStream = req.Stream
		w.Header().Set("Content-Type", "text/event-stream")
		_, _ = fmt.Fprintf(w, "data: %s\n\n", `{"choices":[{"delta":{"content":"ok"}}]}`)
		_, _ = fmt.Fprintf(w, "data: [DONE]\n\n")
	}))
	defer server.Close()

	reg, err := adapters.BuildFromPayload(&iop.NodeConfigPayload{
		Adapters: []*iop.AdapterConfig{
			{
				Type:    "vllm",
				Name:    "vllm@gpu",
				Enabled: true,
				Config:  &iop.AdapterConfig_Vllm{Vllm: &iop.VllmAdapterConfig{Endpoint: server.URL}},
			},
		},
	}, zap.NewNop())
	if err != nil {
		t.Fatalf("build: %v", err)
	}

	a, ok := reg.Get("vllm@gpu")
	if !ok {
		t.Fatal("expected vllm@gpu")
	}

	if err := a.Execute(context.Background(), noderuntime.ExecutionSpec{
		RunID: "run-vllm", Target: "llama-3", Input: map[string]any{"prompt": "hi"},
	}, &testSink{}); err != nil {
		t.Fatalf("Execute: %v", err)
	}

	if gotModel != "llama-3" {
		t.Errorf("model: got %q want llama-3", gotModel)
	}
	if !gotStream {
		t.Error("expected stream=true")
	}

	caps, _ := a.Capabilities(context.Background())
	if caps.InstanceKey != "vllm@gpu" {
		t.Errorf("InstanceKey: got %q want vllm@gpu", caps.InstanceKey)
	}
}

func TestBuildFromPayload_OpenAICompatTypedRegistered(t *testing.T) {
	reg, err := adapters.BuildFromPayload(&iop.NodeConfigPayload{
		Adapters: []*iop.AdapterConfig{
			{
				Type:    "openai_compat",
				Name:    "lemonade-local",
				Enabled: true,
				Config: &iop.AdapterConfig_OpenaiCompat{OpenaiCompat: &iop.OpenAICompatAdapterConfig{
					Provider: "lemonade",
					Endpoint: "http://localhost:8000",
				}},
			},
		},
	}, zap.NewNop())
	if err != nil {
		t.Fatalf("build: %v", err)
	}
	a, ok := reg.Get("lemonade-local")
	if !ok {
		t.Fatal("expected lemonade-local registered")
	}
	if a.Name() != "openai_compat" {
		t.Fatalf("adapter name: got %q want openai_compat", a.Name())
	}
	caps, _ := a.Capabilities(context.Background())
	if caps.InstanceKey != "lemonade-local" {
		t.Errorf("InstanceKey: got %q want lemonade-local", caps.InstanceKey)
	}
}

func TestBuildFromPayload_OpenAICompatExecution(t *testing.T) {
	var gotAuth string
	var gotBody map[string]any

	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path == "/v1/models" {
			_, _ = w.Write([]byte(`{"object":"list","data":[{"id":"lemonade-model"}]}`))
			return
		}
		if r.URL.Path != "/v1/chat/completions" {
			t.Errorf("unexpected path: %s", r.URL.Path)
			return
		}
		gotAuth = r.Header.Get("Authorization")
		if err := json.NewDecoder(r.Body).Decode(&gotBody); err != nil {
			t.Errorf("decode: %v", err)
			return
		}
		w.Header().Set("Content-Type", "text/event-stream")
		_, _ = fmt.Fprintf(w, "data: %s\n\n", `{"choices":[{"delta":{"content":"LEMONADE_"}}]}`)
		_, _ = fmt.Fprintf(w, "data: %s\n\n", `{"choices":[{"delta":{"content":"OK"},"finish_reason":"stop"}]}`)
		_, _ = fmt.Fprintf(w, "data: [DONE]\n\n")
	}))
	defer server.Close()

	reg, err := adapters.BuildFromPayload(&iop.NodeConfigPayload{
		Adapters: []*iop.AdapterConfig{
			{
				Type:    "openai_compat",
				Name:    "lemonade-local",
				Enabled: true,
				Config: &iop.AdapterConfig_OpenaiCompat{OpenaiCompat: &iop.OpenAICompatAdapterConfig{
					Provider: "lemonade",
					Endpoint: server.URL,
					Headers:  map[string]string{"Authorization": "Bearer secret"},
				}},
			},
		},
	}, zap.NewNop())
	if err != nil {
		t.Fatalf("build: %v", err)
	}

	a, ok := reg.Get("lemonade-local")
	if !ok {
		t.Fatal("expected lemonade-local")
	}

	sink := &testSink{}
	if err := a.Execute(context.Background(), noderuntime.ExecutionSpec{
		RunID:  "run-lemonade",
		Target: "lemonade-model",
		Input: map[string]any{
			"messages": []any{map[string]any{"role": "user", "content": "hi"}},
			"options":  map[string]any{"temperature": 0.2, "max_tokens": float64(8)},
		},
	}, sink); err != nil {
		t.Fatalf("Execute: %v", err)
	}

	if gotAuth != "Bearer secret" {
		t.Errorf("Authorization: got %q want Bearer secret", gotAuth)
	}
	if gotBody["model"] != "lemonade-model" {
		t.Errorf("model: got %v want lemonade-model", gotBody["model"])
	}
	if gotBody["stream"] != true {
		t.Errorf("expected stream=true, got %v", gotBody["stream"])
	}
	if gotBody["temperature"] != 0.2 || gotBody["max_tokens"] != float64(8) {
		t.Errorf("options passthrough: got temperature=%v max_tokens=%v", gotBody["temperature"], gotBody["max_tokens"])
	}

	sink.mu.Lock()
	defer sink.mu.Unlock()
	var text string
	var complete *noderuntime.RuntimeEvent
	for i := range sink.events {
		ev := sink.events[i]
		if ev.Type == noderuntime.EventTypeDelta {
			text += ev.Delta
		}
		if ev.Type == noderuntime.EventTypeComplete {
			complete = &sink.events[i]
		}
	}
	if text != "LEMONADE_OK" {
		t.Errorf("streamed text: got %q want LEMONADE_OK", text)
	}
	if complete == nil || complete.Metadata["finish_reason"] != "stop" {
		t.Errorf("expected complete with finish_reason stop, got %+v", complete)
	}
}
