package openai

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"strings"
	"sync"
	"testing"
	"time"

	"go.uber.org/zap"

	edgeservice "iop/apps/edge/internal/service"
	"iop/packages/go/config"
	iop "iop/proto/gen/iop"
)

// chatSidebandServer returns a direct OpenAI-compatible provider route backed by
// the given tunnel frames. It routes via the legacy openai_compat model route
// (not provider-pool) so the raw provider tunnel passthrough path is exercised.
func chatSidebandServer(frames chan *iop.ProviderTunnelFrame) (*Server, *providerFakeRunService) {
	fake := &providerFakeRunService{
		tunnelFrames: frames,
	}
	srv := chatProviderRouteServer(fake, nil)
	return srv, fake
}

func chatProviderRouteServer(fake *providerFakeRunService, logger *zap.Logger) *Server {
	return NewServer(config.EdgeOpenAIConf{
		ModelRoutes: []config.OpenAIRouteEntry{
			{Model: "pool-model", Adapter: "openai_compat", Target: "served-model"},
		},
	}, fake, logger)
}

func TestChatCompletionsPassthroughDoesNotExposeSideband(t *testing.T) {
	providerBody := "data: {\"choices\":[{\"delta\":{\"content\":\"pure\"}}]}\n\ndata: [DONE]\n\n"
	frames := make(chan *iop.ProviderTunnelFrame, 4)
	frames <- &iop.ProviderTunnelFrame{
		Kind:       iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_RESPONSE_START,
		StatusCode: 200,
		Headers:    map[string]string{"Content-Type": "text/event-stream"},
	}
	frames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_BODY, Body: []byte(providerBody)}
	frames <- &iop.ProviderTunnelFrame{
		Kind:  iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_USAGE,
		Usage: &iop.Usage{InputTokens: 3, OutputTokens: 5},
	}
	frames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_END, End: true}
	close(frames)
	srv, _ := chatSidebandServer(frames)

	req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
		"model":"pool-model",
		"stream":true,
		"messages":[{"role":"user","content":"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())
	}
	// Byte-identity with the provider body proves no IOP extension markers or
	// response-mode labels are injected into the pure passthrough response.
	if got := w.Body.String(); got != providerBody {
		t.Fatalf("pure passthrough body not byte-identical:\n got: %q\nwant: %q", got, providerBody)
	}
}

func TestChatCompletionsPassthroughDoesNotLabelTransformed(t *testing.T) {
	providerBody := `{"ok":true}`
	frames := make(chan *iop.ProviderTunnelFrame, 3)
	frames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_RESPONSE_START, StatusCode: 200}
	frames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_BODY, Body: []byte(providerBody)}
	frames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_END, End: true}
	close(frames)
	srv, _ := chatSidebandServer(frames)

	req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
		"model":"pool-model",
		"messages":[{"role":"user","content":"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())
	}
	// Byte-identity with the provider body proves the pure passthrough response
	// carries no IOP response-mode label.
	if got := w.Body.String(); got != providerBody {
		t.Fatalf("pure passthrough body not byte-identical: %q", got)
	}
}

// chatPassthroughServer returns an openai.Server whose fake service relays the
// tunnel to the given provider fixture, emulating the Node relay.
func chatPassthroughServer(providerURL string) (*Server, *providerFakeRunService) {
	fake := &providerFakeRunService{
		poolDispatchPath:   string(edgeservice.ProviderPoolPathTunnel),
		tunnelProviderURL:  providerURL,
		tunnelServedTarget: "served-model",
	}
	srv := NewServer(config.EdgeOpenAIConf{}, fake, nil)
	srv.SetModelCatalog([]config.ModelCatalogEntry{
		{ID: "pool-model", Providers: map[string]string{"prov-1": "served-model"}},
	})
	return srv, fake
}

// TestChatCompletionsPassthroughNonStreamingByteIdentity verifies SDD S04 for
// the non-streaming path: when the provider omits a top-level model echo, the
// caller receives the provider JSON body byte-identically, along with provider
// status and headers.
func TestChatCompletionsPassthroughNonStreamingByteIdentity(t *testing.T) {
	// Provider-original body with fields the normalized path would rewrite or
	// drop (reasoning_content, provider-specific key order and spacing).
	providerBody := "{\"id\":\"cmpl-provider-1\",\"object\":\"chat.completion\",  \"choices\":[{\"index\":0,\"message\":{\"role\":\"assistant\",\"content\":\"hi\",\"reasoning_content\":\"because\"},\"finish_reason\":\"stop\"}],\"provider_extra\":{\"a\":1}}"
	var gotProviderReq []byte
	provider := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		gotProviderReq, _ = io.ReadAll(r.Body)
		w.Header().Set("Content-Type", "application/json")
		w.Header().Set("X-Provider-Marker", "prov-42")
		w.WriteHeader(http.StatusOK)
		_, _ = w.Write([]byte(providerBody))
	}))
	defer provider.Close()

	srv, fake := chatPassthroughServer(provider.URL)
	req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
		"model":"pool-model",
		"messages":[{"role":"user","content":"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 got := w.Body.String(); got != providerBody {
		t.Fatalf("body not byte-identical to provider:\n got: %q\nwant: %q", got, providerBody)
	}
	if got := w.Header().Get("X-Provider-Marker"); got != "prov-42" {
		t.Errorf("provider header not relayed: %q", got)
	}
	if got := w.Header().Get("Content-Type"); got != "application/json" {
		t.Errorf("content type not relayed: %q", got)
	}
	// The provider request body carries the served model (model rewrite only).
	if !strings.Contains(string(gotProviderReq), `"model":"served-model"`) {
		t.Errorf("provider request body missing served model: %s", gotProviderReq)
	}
	if !strings.Contains(string(gotProviderReq), `"content":"hello"`) {
		t.Errorf("provider request body lost caller messages: %s", gotProviderReq)
	}
	if len(fake.reqsSnapshot()) != 0 {
		t.Error("normalized SubmitRun must not be called on passthrough")
	}
}

func TestChatCompletionsPassthroughNonStreamingRewritesModelEcho(t *testing.T) {
	providerBody := `{"id":"cmpl-provider-1","object":"chat.completion","created":123,"model":"served-model","choices":[{"index":0,"message":{"role":"assistant","content":"hi","reasoning_content":"because"},"finish_reason":"stop"}],"provider_extra":{"a":1}}`
	var gotProviderReq []byte
	provider := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		gotProviderReq, _ = io.ReadAll(r.Body)
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)
		_, _ = w.Write([]byte(providerBody))
	}))
	defer provider.Close()

	srv, _ := chatPassthroughServer(provider.URL)
	req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
		"model":"pool-model",
		"messages":[{"role":"user","content":"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())
	}
	var resp struct {
		Model   string `json:"model"`
		Choices []struct {
			Message struct {
				Content          string `json:"content"`
				ReasoningContent string `json:"reasoning_content"`
			} `json:"message"`
		} `json:"choices"`
		ProviderExtra map[string]int `json:"provider_extra"`
	}
	if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
		t.Fatalf("decode response: %v body=%s", err, w.Body.String())
	}
	if resp.Model != "pool-model" {
		t.Fatalf("response model: got %q, want pool-model; body=%s", resp.Model, w.Body.String())
	}
	if resp.Choices[0].Message.Content != "hi" || resp.Choices[0].Message.ReasoningContent != "because" {
		t.Fatalf("provider content/reasoning not preserved: %+v", resp.Choices[0].Message)
	}
	if resp.ProviderExtra["a"] != 1 {
		t.Fatalf("provider_extra not preserved: %+v", resp.ProviderExtra)
	}
	if !strings.Contains(string(gotProviderReq), `"model":"served-model"`) {
		t.Fatalf("provider request body missing served model: %s", gotProviderReq)
	}
}

// TestChatCompletionsPassthroughStreamingByteIdentity verifies SDD S04 for the
// streaming path: provider SSE bytes without top-level model echoes, including
// provider-specific fields like reasoning_content and native tool_calls chunks,
// reach the caller byte-identically.
func TestChatCompletionsPassthroughStreamingByteIdentity(t *testing.T) {
	providerBody := "data: {\"choices\":[{\"delta\":{\"role\":\"assistant\"}}]}\n\n" +
		"data: {\"choices\":[{\"delta\":{\"reasoning_content\":\"thinking...\"}}]}\n\n" +
		"data: {\"choices\":[{\"delta\":{\"content\":\"<think>not rewritten</think>\"}}]}\n\n" +
		"data: {\"choices\":[{\"delta\":{\"tool_calls\":[{\"index\":0,\"function\":{\"name\":\"f\",\"arguments\":\"{}\"}}]}}]}\n\n" +
		"data: {\"choices\":[{\"delta\":{},\"finish_reason\":\"tool_calls\"}],\"usage\":{\"prompt_tokens\":3}}\n\n" +
		"data: [DONE]\n\n"
	provider := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/event-stream")
		w.WriteHeader(http.StatusOK)
		_, _ = w.Write([]byte(providerBody))
	}))
	defer provider.Close()

	srv, _ := chatPassthroughServer(provider.URL)
	req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
		"model":"pool-model",
		"stream":true,
		"messages":[{"role":"user","content":"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 got := w.Body.String(); got != providerBody {
		t.Fatalf("SSE stream not byte-identical to provider:\n got: %q\nwant: %q", got, providerBody)
	}
	if got := w.Header().Get("Content-Type"); got != "text/event-stream" {
		t.Errorf("content type not relayed: %q", got)
	}
}

func TestChatCompletionsPassthroughStreamingRewritesModelEcho(t *testing.T) {
	providerBody := "data: {\"id\":\"chunk-1\",\"object\":\"chat.completion.chunk\",\"created\":123,\"model\":\"served-model\",\"choices\":[{\"delta\":{\"role\":\"assistant\"}}]}\n\n" +
		"data: {\"id\":\"chunk-1\",\"object\":\"chat.completion.chunk\",\"created\":123,\"model\":\"served-model\",\"choices\":[{\"delta\":{\"reasoning\":\"thinking...\"}}]}\n\n" +
		"data: {\"id\":\"chunk-1\",\"object\":\"chat.completion.chunk\",\"created\":123,\"model\":\"served-model\",\"choices\":[{\"delta\":{\"content\":\"hi\"}}]}\n\n" +
		"data: [DONE]\n\n"
	provider := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/event-stream")
		w.WriteHeader(http.StatusOK)
		_, _ = w.Write([]byte(providerBody))
	}))
	defer provider.Close()

	srv, _ := chatPassthroughServer(provider.URL)
	req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
		"model":"pool-model",
		"stream":true,
		"messages":[{"role":"user","content":"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())
	}
	body := w.Body.String()
	if strings.Contains(body, `"model":"served-model"`) {
		t.Fatalf("response leaked provider-served model: %s", body)
	}
	if strings.Count(body, `"model":"pool-model"`) != 3 {
		t.Fatalf("response model aliases not rewritten in every chunk: %s", body)
	}
	if !strings.Contains(body, `"reasoning":"thinking..."`) || !strings.Contains(body, `"content":"hi"`) {
		t.Fatalf("provider reasoning/content not preserved: %s", body)
	}
	if !strings.Contains(body, "data: [DONE]") {
		t.Fatalf("done marker not preserved: %s", body)
	}
	if got := w.Header().Get("Content-Type"); got != "text/event-stream" {
		t.Errorf("content type not relayed: %q", got)
	}
}

// TestChatCompletionsPassthroughProviderErrorStatusRelayed verifies a provider
// error status/body is passed to the caller unmodified instead of being
// converted into an IOP error envelope.
func TestChatCompletionsPassthroughProviderErrorStatusRelayed(t *testing.T) {
	providerBody := `{"error":{"message":"rate limited by provider","type":"rate_limit"}}`
	provider := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusTooManyRequests)
		_, _ = w.Write([]byte(providerBody))
	}))
	defer provider.Close()

	srv, _ := chatPassthroughServer(provider.URL)
	req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
		"model":"pool-model",
		"messages":[{"role":"user","content":"hello"}]
	}`))
	w := httptest.NewRecorder()
	srv.handleChatCompletions(w, req)

	if w.Code != http.StatusTooManyRequests {
		t.Fatalf("provider status not relayed: got %d", w.Code)
	}
	if got := w.Body.String(); got != providerBody {
		t.Fatalf("provider error body not byte-identical: %q", got)
	}
}

// TestChatCompletionsPassthroughLegacyProviderRouteUsesTunnel verifies that a
// legacy model_routes entry with an openai_compat adapter also defaults to the
// tunnel passthrough path (direct route, no provider pool).
func TestChatCompletionsPassthroughLegacyProviderRouteUsesTunnel(t *testing.T) {
	frames := make(chan *iop.ProviderTunnelFrame, 3)
	frames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_RESPONSE_START, StatusCode: 200}
	frames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_BODY, Body: []byte(`{"ok":true}`)}
	frames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_END, End: true}
	close(frames)
	fake := &providerFakeRunService{
		tunnelFrames: frames,
	}
	srv := NewServer(config.EdgeOpenAIConf{
		ModelRoutes: []config.OpenAIRouteEntry{
			{Model: "compat-model", Adapter: "openai_compat", Target: "backend-model"},
		},
	}, fake, nil)

	req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
		"model":"compat-model",
		"messages":[{"role":"user","content":"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())
	}
	tunnelReqs := fake.tunnelReqsSnapshot()
	if len(tunnelReqs) != 1 {
		t.Fatalf("expected tunnel dispatch for openai_compat route, got %d", len(tunnelReqs))
	}
	if tunnelReqs[0].Adapter != "openai_compat" || tunnelReqs[0].Target != "backend-model" {
		t.Errorf("tunnel adapter/target: got %q/%q", tunnelReqs[0].Adapter, tunnelReqs[0].Target)
	}
	if tunnelReqs[0].ProviderPool {
		t.Error("legacy route must not set ProviderPool")
	}
	bodies := fake.tunnelBodiesSnapshot()
	if len(bodies) != 1 || !strings.Contains(string(bodies[0]), `"model":"backend-model"`) {
		t.Errorf("legacy route body must carry route target: %s", bodies)
	}
}

func TestChatCompletionsPassthroughNonProviderRouteKeepsNormalizedPath(t *testing.T) {
	fake := &providerFakeRunService{
		fakeRunService: fakeRunService{events: bufferedRunEvents(
			&iop.RunEvent{Type: "delta", Delta: "ok"},
			&iop.RunEvent{Type: "complete"},
		)},
	}
	srv := NewServer(config.EdgeOpenAIConf{Adapter: "ollama", Target: "llama-fixed"}, fake, nil)

	req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
		"model":"any-model",
		"messages":[{"role":"user","content":"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 len(fake.tunnelReqsSnapshot()) != 0 {
		t.Error("ollama route must not use the tunnel")
	}
	if len(fake.reqsSnapshot()) != 1 {
		t.Fatalf("expected normalized SubmitRun dispatch, got %d", len(fake.reqsSnapshot()))
	}
}

// TestChatCompletionsPassthroughTunnelErrorBeforeStartReturns502 verifies an
// ERROR frame before response-start becomes a JSON error response.
func TestChatCompletionsPassthroughTunnelErrorBeforeStartReturns502(t *testing.T) {
	frames := make(chan *iop.ProviderTunnelFrame, 1)
	frames <- &iop.ProviderTunnelFrame{
		Kind:  iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_ERROR,
		Error: "provider connect refused",
	}
	close(frames)
	fake := &providerFakeRunService{
		tunnelFrames: frames,
	}
	srv := NewServer(config.EdgeOpenAIConf{}, fake, nil)
	srv.SetModelCatalog([]config.ModelCatalogEntry{
		{ID: "pool-model", Providers: map[string]string{"prov-1": "served-model"}},
	})

	req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
		"model":"pool-model",
		"messages":[{"role":"user","content":"hello"}]
	}`))
	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(), "provider connect refused") {
		t.Fatalf("error message not surfaced: %s", w.Body.String())
	}
}

// TestChatCompletionsPassthroughContextCancelSendsCancelRun verifies caller
// disconnect propagates to the Node cancel path for tunnel dispatches.
func TestChatCompletionsPassthroughContextCancelSendsCancelRun(t *testing.T) {
	// Frames channel stays open and empty: the tunnel is in-flight when the
	// caller context is cancelled.
	fake := &providerFakeRunService{
		tunnelFrames: make(chan *iop.ProviderTunnelFrame),
	}
	srv := NewServer(config.EdgeOpenAIConf{}, fake, nil)
	srv.SetModelCatalog([]config.ModelCatalogEntry{
		{ID: "pool-model", Providers: map[string]string{"prov-1": "served-model"}},
	})

	ctx, cancel := context.WithCancel(context.Background())
	cancel()
	req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
		"model":"pool-model",
		"messages":[{"role":"user","content":"hello"}]
	}`)).WithContext(ctx)
	w := httptest.NewRecorder()
	srv.handleChatCompletions(w, req)

	calls := fake.cancelCallsSnapshot()
	if len(calls) != 1 {
		t.Fatalf("expected 1 CancelRun call, got %d: %+v", len(calls), calls)
	}
	if calls[0].RunID != "run-tunnel" || calls[0].NodeRef != "node-1" {
		t.Fatalf("unexpected cancel request: %+v", calls[0])
	}
}

// flakyResponseWriter is a tunnel-path ResponseWriter double: it accepts
// failAfterWrites successful Write calls, then fails every subsequent Write,
// optionally sleeping writeDelay per call to emulate a slow client.
type flakyResponseWriter struct {
	mu              sync.Mutex
	header          http.Header
	status          int
	body            bytes.Buffer
	failAfterWrites int // -1: never fail
	writeDelay      time.Duration
	writes          int
	failedWrites    int
}

func newFlakyResponseWriter(failAfterWrites int) *flakyResponseWriter {
	return &flakyResponseWriter{header: make(http.Header), failAfterWrites: failAfterWrites}
}

func (w *flakyResponseWriter) Header() http.Header { return w.header }

func (w *flakyResponseWriter) WriteHeader(code int) {
	w.mu.Lock()
	defer w.mu.Unlock()
	if w.status == 0 {
		w.status = code
	}
}

func (w *flakyResponseWriter) Write(p []byte) (int, error) {
	if w.writeDelay > 0 {
		time.Sleep(w.writeDelay)
	}
	w.mu.Lock()
	defer w.mu.Unlock()
	if w.status == 0 {
		w.status = http.StatusOK
	}
	if w.failAfterWrites >= 0 && w.writes >= w.failAfterWrites {
		w.failedWrites++
		return 0, errors.New("client connection write failed")
	}
	w.writes++
	w.body.Write(p)
	return len(p), nil
}

func (w *flakyResponseWriter) Flush() {}

func (w *flakyResponseWriter) snapshot() (status int, body string, failedWrites int) {
	w.mu.Lock()
	defer w.mu.Unlock()
	return w.status, w.body.String(), w.failedWrites
}

// TestChatCompletionsPassthroughWriteFailureSendsCancelRunOnce verifies S09
// for a caller write failure after response start: exactly one CancelRun is
// propagated to the Node, the ordered prefix written before the failure is
// preserved, and no tunnel bytes are written after the failed write even
// though more frames (including END) are available.
func TestChatCompletionsPassthroughWriteFailureSendsCancelRunOnce(t *testing.T) {
	frames := make(chan *iop.ProviderTunnelFrame, 5)
	frames <- &iop.ProviderTunnelFrame{
		Kind:       iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_RESPONSE_START,
		StatusCode: 200,
		Headers:    map[string]string{"Content-Type": "text/event-stream"},
	}
	frames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_BODY, Body: []byte("data: chunk-1\n\n")}
	frames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_BODY, Body: []byte("data: chunk-2\n\n")}
	frames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_BODY, Body: []byte("data: chunk-3\n\n")}
	frames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_END, End: true}
	close(frames)
	srv, fake := chatSidebandServer(frames)

	req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
		"model":"pool-model",
		"stream":true,
		"messages":[{"role":"user","content":"hello"}]
	}`))
	w := newFlakyResponseWriter(1) // first body write succeeds, second fails
	srv.handleChatCompletions(w, req)

	status, body, failedWrites := w.snapshot()
	if status != http.StatusOK {
		t.Fatalf("status: got %d body=%s", status, body)
	}
	if body != "data: chunk-1\n\n" {
		t.Fatalf("body after write failure must stay the ordered prefix:\n got: %q\nwant: %q", body, "data: chunk-1\n\n")
	}
	if failedWrites == 0 {
		t.Fatal("fixture did not exercise the write failure path")
	}
	calls := fake.cancelCallsSnapshot()
	if len(calls) != 1 {
		t.Fatalf("expected exactly 1 CancelRun call on write failure, got %d: %+v", len(calls), calls)
	}
	if calls[0].RunID != "run-tunnel" || calls[0].NodeRef != "node-1" {
		t.Fatalf("unexpected cancel request: %+v", calls[0])
	}
}

// TestChatCompletionsPassthroughWaitTimeoutSendsCancelRun verifies the tunnel
// wait timeout before response start returns a 502 and propagates exactly one
// CancelRun to the Node.
func TestChatCompletionsPassthroughWaitTimeoutSendsCancelRun(t *testing.T) {
	fake := &providerFakeRunService{
		tunnelFrames:      make(chan *iop.ProviderTunnelFrame),
		tunnelWaitTimeout: 50 * time.Millisecond,
	}
	srv := NewServer(config.EdgeOpenAIConf{}, fake, nil)
	srv.SetModelCatalog([]config.ModelCatalogEntry{
		{ID: "pool-model", Providers: map[string]string{"prov-1": "served-model"}},
	})

	req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
		"model":"pool-model",
		"messages":[{"role":"user","content":"hello"}]
	}`))
	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(), "run timed out") {
		t.Fatalf("timeout error not surfaced: %s", w.Body.String())
	}
	calls := fake.cancelCallsSnapshot()
	if len(calls) != 1 {
		t.Fatalf("expected exactly 1 CancelRun call on wait timeout, got %d: %+v", len(calls), calls)
	}
	if calls[0].RunID != "run-tunnel" {
		t.Fatalf("unexpected cancel request: %+v", calls[0])
	}
}

// TestChatCompletionsPassthroughProviderErrorAfterStartStopsBodyWrites
// verifies an ERROR frame after response start truncates the stream: bytes
// written before the error are preserved in order, later frames are never
// written, and no spurious CancelRun is sent for a provider-terminal failure.
func TestChatCompletionsPassthroughProviderErrorAfterStartStopsBodyWrites(t *testing.T) {
	frames := make(chan *iop.ProviderTunnelFrame, 4)
	frames <- &iop.ProviderTunnelFrame{
		Kind:       iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_RESPONSE_START,
		StatusCode: 200,
		Headers:    map[string]string{"Content-Type": "text/event-stream"},
	}
	frames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_BODY, Body: []byte("data: before-error\n\n")}
	frames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_ERROR, Error: "provider stream failed"}
	frames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_BODY, Body: []byte("data: after-error-must-not-write\n\n")}
	close(frames)
	srv, fake := chatSidebandServer(frames)

	req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
		"model":"pool-model",
		"stream":true,
		"messages":[{"role":"user","content":"hello"}]
	}`))
	w := httptest.NewRecorder()
	srv.handleChatCompletions(w, req)

	if w.Code != http.StatusOK {
		t.Fatalf("status already committed before the error must stay 200, got %d", w.Code)
	}
	if got := w.Body.String(); got != "data: before-error\n\n" {
		t.Fatalf("provider error must truncate after the ordered prefix:\n got: %q\nwant: %q", got, "data: before-error\n\n")
	}
	if calls := fake.cancelCallsSnapshot(); len(calls) != 0 {
		t.Fatalf("provider-terminal error must not trigger CancelRun, got %+v", calls)
	}
}

// TestChatCompletionsPassthroughSlowClientByteIdentity verifies a slow caller
// applies backpressure without corrupting opaque relay data: every body frame
// reaches the caller exactly once, in order, and a slow-but-alive client never
// triggers CancelRun.
func TestChatCompletionsPassthroughSlowClientByteIdentity(t *testing.T) {
	const chunkCount = 40
	var want strings.Builder
	frames := make(chan *iop.ProviderTunnelFrame, chunkCount+2)
	frames <- &iop.ProviderTunnelFrame{
		Kind:       iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_RESPONSE_START,
		StatusCode: 200,
		Headers:    map[string]string{"Content-Type": "text/event-stream"},
	}
	for i := 0; i < chunkCount; i++ {
		chunk := fmt.Sprintf("data: chunk-%02d\n\n", i)
		want.WriteString(chunk)
		frames <- &iop.ProviderTunnelFrame{
			Kind:     iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_BODY,
			Sequence: int64(i + 1),
			Body:     []byte(chunk),
		}
	}
	frames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_END, End: true}
	close(frames)
	srv, fake := chatSidebandServer(frames)

	req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
		"model":"pool-model",
		"stream":true,
		"messages":[{"role":"user","content":"hello"}]
	}`))
	w := newFlakyResponseWriter(-1)
	w.writeDelay = 2 * time.Millisecond
	srv.handleChatCompletions(w, req)

	status, body, _ := w.snapshot()
	if status != http.StatusOK {
		t.Fatalf("status: got %d", status)
	}
	if body != want.String() {
		t.Fatalf("slow client stream not byte-identical/ordered:\n got: %q\nwant: %q", body, want.String())
	}
	if calls := fake.cancelCallsSnapshot(); len(calls) != 0 {
		t.Fatalf("slow but alive client must not trigger CancelRun, got %+v", calls)
	}
}

// responsesProviderTunnelServer builds a server whose catalog routes
// "pool-model" through the OpenAI-compatible provider tunnel, wired to the given
// provider frames and served target.
func responsesProviderTunnelServer(frames chan *iop.ProviderTunnelFrame, servedTarget string) (*Server, *providerFakeRunService) {
	fake := &providerFakeRunService{
		poolDispatchPath:   string(edgeservice.ProviderPoolPathTunnel),
		tunnelFrames:       frames,
		tunnelServedTarget: servedTarget,
	}
	srv := NewServer(config.EdgeOpenAIConf{}, fake, nil)
	srv.SetModelCatalog([]config.ModelCatalogEntry{
		{ID: "pool-model", Providers: map[string]string{"prov-1": "served-model"}},
	})
	return srv, fake
}

// TestResponsesProviderTunnelAllowsUnknownFields verifies that Codex/Responses
// unknown fields (tools, parallel_tool_calls, store) are accepted on the
// provider passthrough route and preserved in the forwarded tunnel body.
func TestResponsesProviderTunnelAllowsUnknownFields(t *testing.T) {
	srv, fake := responsesProviderTunnelServer(staticProviderTunnelFrames(`{"ok":true}`), "served-model")
	req := httptest.NewRequest(http.MethodPost, "/v1/responses", strings.NewReader(`{
		"model":"pool-model",
		"input":"hi",
		"tools":[{"type":"web_search"}],
		"parallel_tool_calls":true,
		"store":false
	}`))
	w := httptest.NewRecorder()
	srv.handleResponses(w, req)

	if w.Code != http.StatusOK {
		t.Fatalf("provider passthrough must accept unknown fields: got %d body=%s", w.Code, w.Body.String())
	}
	bodies := fake.tunnelBodiesSnapshot()
	if len(bodies) != 1 {
		t.Fatalf("expected 1 tunnel body, got %d", len(bodies))
	}
	var providerReq map[string]any
	if err := json.Unmarshal(bodies[0], &providerReq); err != nil {
		t.Fatalf("provider body JSON: %v body=%s", err, bodies[0])
	}
	if _, ok := providerReq["tools"]; !ok {
		t.Fatalf("tools must be preserved on passthrough: %+v", providerReq)
	}
	if providerReq["parallel_tool_calls"] != true {
		t.Fatalf("parallel_tool_calls must be preserved: %+v", providerReq)
	}
	if providerReq["store"] != false {
		t.Fatalf("store must be preserved: %+v", providerReq)
	}
}

// TestResponsesProviderTunnelSubmitsResponsesPath verifies the passthrough
// dispatches a provider tunnel POST to /v1/responses and never calls SubmitRun.
func TestResponsesProviderTunnelSubmitsResponsesPath(t *testing.T) {
	srv, fake := responsesProviderTunnelServer(staticProviderTunnelFrames(`{"ok":true}`), "served-model")
	req := httptest.NewRequest(http.MethodPost, "/v1/responses", strings.NewReader(`{"model":"pool-model","input":"hi"}`))
	w := httptest.NewRecorder()
	srv.handleResponses(w, req)

	if w.Code != http.StatusOK {
		t.Fatalf("status: got %d body=%s", w.Code, w.Body.String())
	}
	if len(fake.reqsSnapshot()) != 0 {
		t.Fatalf("responses passthrough must not call SubmitRun, got %d", len(fake.reqsSnapshot()))
	}
	reqs := fake.tunnelReqsSnapshot()
	if len(reqs) != 1 {
		t.Fatalf("expected 1 tunnel dispatch, got %d", len(reqs))
	}
	if reqs[0].Path != "/v1/responses" {
		t.Fatalf("tunnel path: got %q want /v1/responses", reqs[0].Path)
	}
	if reqs[0].Method != http.MethodPost {
		t.Fatalf("tunnel method: got %q want POST", reqs[0].Method)
	}
}

// TestResponsesProviderTunnelRewritesOnlyModel verifies the caller model alias
// is rewritten to the provider-served model while max_output_tokens, tools, and
// arbitrary fields are preserved verbatim.
func TestResponsesProviderTunnelRewritesOnlyModel(t *testing.T) {
	srv, fake := responsesProviderTunnelServer(staticProviderTunnelFrames(`{"ok":true}`), "served-model")
	req := httptest.NewRequest(http.MethodPost, "/v1/responses", strings.NewReader(`{
		"model":"pool-model",
		"input":"hi",
		"max_output_tokens":123,
		"tools":[{"type":"web_search"}],
		"custom_field":"keep-me"
	}`))
	w := httptest.NewRecorder()
	srv.handleResponses(w, req)

	if w.Code != http.StatusOK {
		t.Fatalf("status: got %d body=%s", w.Code, w.Body.String())
	}
	bodies := fake.tunnelBodiesSnapshot()
	if len(bodies) != 1 {
		t.Fatalf("expected 1 tunnel body, got %d", len(bodies))
	}
	var providerReq map[string]any
	if err := json.Unmarshal(bodies[0], &providerReq); err != nil {
		t.Fatalf("provider body JSON: %v body=%s", err, bodies[0])
	}
	if providerReq["model"] != "served-model" {
		t.Fatalf("model must be rewritten to served target: %+v", providerReq["model"])
	}
	if providerReq["max_output_tokens"].(float64) != 123 {
		t.Fatalf("max_output_tokens must be preserved: %+v", providerReq)
	}
	if _, ok := providerReq["tools"]; !ok {
		t.Fatalf("tools must be preserved: %+v", providerReq)
	}
	if providerReq["custom_field"] != "keep-me" {
		t.Fatalf("arbitrary fields must be preserved: %+v", providerReq)
	}
}

// TestResponsesProviderTunnelStreaming verifies a stream:true Responses request
// sets Stream=true on the tunnel dispatch and relays raw SSE bytes verbatim.
func TestResponsesProviderTunnelStreaming(t *testing.T) {
	frames := make(chan *iop.ProviderTunnelFrame, 4)
	frames <- &iop.ProviderTunnelFrame{
		Kind:       iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_RESPONSE_START,
		StatusCode: 200,
		Headers:    map[string]string{"Content-Type": "text/event-stream"},
	}
	frames <- &iop.ProviderTunnelFrame{
		Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_BODY,
		Body: []byte("data: {\"type\":\"response.output_text.delta\",\"delta\":\"hi\"}\n\n"),
	}
	frames <- &iop.ProviderTunnelFrame{
		Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_BODY,
		Body: []byte("data: [DONE]\n\n"),
	}
	frames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_END, End: true}
	close(frames)

	srv, fake := responsesProviderTunnelServer(frames, "served-model")
	req := httptest.NewRequest(http.MethodPost, "/v1/responses", strings.NewReader(`{"model":"pool-model","input":"hi","stream":true}`))
	w := httptest.NewRecorder()
	srv.handleResponses(w, req)

	if w.Code != http.StatusOK {
		t.Fatalf("status: got %d body=%s", w.Code, w.Body.String())
	}
	reqs := fake.tunnelReqsSnapshot()
	if len(reqs) != 1 {
		t.Fatalf("expected 1 tunnel dispatch, got %d", len(reqs))
	}
	if !reqs[0].Stream {
		t.Fatalf("tunnel request Stream must be true for a stream:true responses request")
	}
	body := w.Body.String()
	if !strings.Contains(body, "response.output_text.delta") || !strings.Contains(body, "[DONE]") {
		t.Fatalf("SSE body must be relayed verbatim, got %q", body)
	}
}

func TestChatCompletionsProviderPoolTunnelRelaysProviderErrorBody(t *testing.T) {
	providerError := `{"error":{"message":"unknown field custom_provider_options","type":"invalid_request_error","param":"custom_provider_options"}}`
	frames := make(chan *iop.ProviderTunnelFrame, 3)
	frames <- &iop.ProviderTunnelFrame{
		Kind:       iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_RESPONSE_START,
		StatusCode: http.StatusUnprocessableEntity,
		Headers:    map[string]string{"Content-Type": "application/json"},
	}
	frames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_BODY, Body: []byte(providerError)}
	frames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_END, End: true}
	close(frames)

	fake := &providerFakeRunService{
		tunnelFrames:       frames,
		tunnelServedTarget: "served-model",
	}
	srv := NewServer(config.EdgeOpenAIConf{}, fake, nil)
	srv.SetModelCatalog([]config.ModelCatalogEntry{
		{ID: "pool-model", Providers: map[string]string{"prov-vllm": "served-model"}},
	})

	req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
		"model":"pool-model",
		"messages":[{"role":"user","content":"hello"}],
		"custom_provider_options":{"reject":true}
	}`))
	w := httptest.NewRecorder()
	srv.handleChatCompletions(w, req)

	if w.Code != http.StatusUnprocessableEntity {
		t.Fatalf("status: got %d body=%s", w.Code, w.Body.String())
	}
	if w.Body.String() != providerError {
		t.Fatalf("provider error body not relayed byte-identically:\n got: %q\nwant: %q", w.Body.String(), providerError)
	}
	if got := w.Header().Get("Content-Type"); !strings.Contains(got, "application/json") {
		t.Fatalf("Content-Type header not relayed: %q", got)
	}
	if len(fake.reqsSnapshot()) != 0 {
		t.Fatalf("provider error must not fall back to normalized SubmitRun, got %d calls", len(fake.reqsSnapshot()))
	}
}
