package openai

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

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

func TestHotPathChatDirectStreamCodec(t *testing.T) {
	candidate := anthropicTestCandidate(t, "openai")
	providerStream := strings.Join([]string{
		`data: {"id":"chatcmpl-chat-gate","object":"chat.completion.chunk","created":1777001001,"choices":[{"index":0,"delta":{"content":"alpha "},"finish_reason":null}]}`,
		`data: {"id":"chatcmpl-chat-gate","object":"chat.completion.chunk","created":1777001001,"choices":[{"index":0,"delta":{"reasoning_content":"think "},"finish_reason":null}]}`,
		`data: {"id":"chatcmpl-chat-gate","object":"chat.completion.chunk","created":1777001001,"choices":[{"index":0,"delta":{"content":"omega"},"finish_reason":null}]}`,
		`data: {"id":"chatcmpl-chat-gate","object":"chat.completion.chunk","created":1777001001,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"provider-chat-gate","type":"function","function":{"name":"read_file","arguments":"{\"path\":"}}]},"finish_reason":null}]}`,
		`data: {"id":"chatcmpl-chat-gate","object":"chat.completion.chunk","created":1777001001,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"README.md\"}"}}]},"finish_reason":null}]}`,
		`data: {"id":"chatcmpl-chat-gate","object":"chat.completion.chunk","created":1777001001,"choices":[{"index":0,"delta":{},"finish_reason":"tool_calls"}],"usage":{"prompt_tokens":9,"completion_tokens":7,"total_tokens":16}}`,
		`data: [DONE]`, "",
	}, "\n\n")
	srv, fake := newHotPathHandlerServer(t, candidate, staticProviderTunnelFrames(providerStream))
	body := `{"model":"virtual-model","messages":[{"role":"user","content":"hello"}],"tools":[{"type":"function","function":{"name":"read_file","parameters":{"type":"object"}}}],"max_completion_tokens":64,"stream":true}`
	response := serveHotPathChatBody(t, srv, body)
	if response.Code != http.StatusOK {
		t.Fatalf("status=%d body=%s", response.Code, response.Body.String())
	}
	chunks, done := decodeHotPathChatSSE(t, response.Body.String())
	if done != 1 {
		t.Fatalf("DONE count=%d body=%s", done, response.Body.String())
	}
	assertHotPathChatChunks(t, chunks, hotPathChatChunkExpectation{
		ResponseID: "chatcmpl-chat-gate", Model: "virtual-model", Content: "alpha omega", Reasoning: "think ",
		Kinds:  []string{"content", "reasoning", "content", "tool", "tool", "terminal"},
		ToolID: "chatcmpl-chat-gate-tool-1", ToolName: "read_file", ToolArgs: `{"path":"README.md"}`,
		FinishReason: "tool_calls", PromptTokens: 9, CompletionTokens: 7,
	})
	if fake.poolSubmitCountSnapshot() != 1 {
		t.Fatalf("selector submissions=%d, want 1", fake.poolSubmitCountSnapshot())
	}
	assertHotPathWaiting(t, srv, "chatcmpl-chat-gate-tool-1", "provider-chat-gate")
}

func TestHotPathChatToolIndexesAreMonotonic(t *testing.T) {
	candidate := anthropicTestCandidate(t, "openai")
	providerStream := strings.Join([]string{
		`data: {"id":"chatcmpl-chat-tools","created":1777001004,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"provider-tool-0","type":"function","function":{"name":"read_file","arguments":"{\"path\":"}}]},"finish_reason":null}]}`,
		`data: {"id":"chatcmpl-chat-tools","created":1777001004,"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"a\"}"}}]},"finish_reason":null}]}`,
		`data: {"id":"chatcmpl-chat-tools","created":1777001004,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"id":"provider-tool-1","type":"function","function":{"name":"read_file","arguments":"{\"path\":"}}]},"finish_reason":null}]}`,
		`data: {"id":"chatcmpl-chat-tools","created":1777001004,"choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"\"b\"}"}}]},"finish_reason":null}]}`,
		`data: {"id":"chatcmpl-chat-tools","created":1777001004,"choices":[{"index":0,"delta":{},"finish_reason":"tool_calls"}]}`,
		`data: [DONE]`, "",
	}, "\n\n")
	srv, _ := newHotPathHandlerServer(t, candidate, staticProviderTunnelFrames(providerStream))
	response := serveHotPathChatBody(t, srv, `{"model":"virtual-model","messages":[{"role":"user","content":"tools"}],"tools":[{"type":"function","function":{"name":"read_file","parameters":{"type":"object"}}}],"stream":true}`)
	chunks, done := decodeHotPathChatSSE(t, response.Body.String())
	if response.Code != http.StatusOK || done != 1 {
		t.Fatalf("status=%d DONE=%d body=%s", response.Code, done, response.Body.String())
	}
	var indexes []int
	var ids []string
	for _, chunk := range chunks {
		choice := chunk["choices"].([]any)[0].(map[string]any)
		delta := choice["delta"].(map[string]any)
		tools, ok := delta["tool_calls"].([]any)
		if !ok {
			continue
		}
		tool := tools[0].(map[string]any)
		indexes = append(indexes, int(tool["index"].(float64)))
		if id, _ := tool["id"].(string); id != "" {
			ids = append(ids, id)
		}
	}
	if fmt.Sprint(indexes) != "[0 0 1 1]" || fmt.Sprint(ids) != "[chatcmpl-chat-tools-tool-1 chatcmpl-chat-tools-tool-2]" {
		t.Fatalf("tool index/id sequence: indexes=%v ids=%v body=%s", indexes, ids, response.Body.String())
	}
}

func TestHotPathChatCallerCapAndNonStream(t *testing.T) {
	for _, capField := range []string{"max_tokens", "max_completion_tokens"} {
		capField := capField
		t.Run(capField+" preserves provider terminal", func(t *testing.T) {
			candidate := anthropicTestCandidate(t, "openai")
			providerStream := strings.Join([]string{
				`data: {"id":"chatcmpl-chat-cap","created":1777001002,"choices":[{"index":0,"delta":{"content":"abcdefghij"},"finish_reason":null}]}`,
				`data: {"id":"chatcmpl-chat-cap","created":1777001002,"choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}`,
				`data: [DONE]`, "",
			}, "\n\n")
			srv, _ := newHotPathHandlerServer(t, candidate, staticProviderTunnelFrames(providerStream))
			body := fmt.Sprintf(`{"model":"virtual-model","messages":[{"role":"user","content":"cap"}],%q:2,"stream":true}`, capField)
			response := serveHotPathChatBody(t, srv, body)
			chunks, done := decodeHotPathChatSSE(t, response.Body.String())
			if response.Code != http.StatusOK || done != 1 {
				t.Fatalf("status=%d DONE=%d body=%s", response.Code, done, response.Body.String())
			}
			assertHotPathChatChunks(t, chunks, hotPathChatChunkExpectation{
				ResponseID: "chatcmpl-chat-cap", Model: "virtual-model", Content: "abcdefghij",
				Kinds: []string{"content", "terminal"}, FinishReason: "stop",
			})
			assertHotPathTerminal(t, srv)
		})
	}

	t.Run("non-stream compatibility", func(t *testing.T) {
		candidate := anthropicTestCandidate(t, "openai")
		providerBody := `{"id":"chatcmpl-chat-json","object":"chat.completion","created":1777001003,"model":"served-selector","choices":[{"index":0,"message":{"role":"assistant","content":"json final","reasoning_content":"json thought"},"finish_reason":"stop"}],"usage":{"prompt_tokens":4,"completion_tokens":3,"total_tokens":7}}`
		srv, fake := newHotPathHandlerServer(t, candidate, staticProviderTunnelFrames(providerBody))
		response := serveHotPathChatBody(t, srv, `{"model":"virtual-model","messages":[{"role":"user","content":"json"}],"stream":false}`)
		if response.Code != http.StatusOK {
			t.Fatalf("status=%d body=%s", response.Code, response.Body.String())
		}
		var decoded struct {
			ID      string `json:"id"`
			Model   string `json:"model"`
			Choices []struct {
				Message      chatMessage `json:"message"`
				FinishReason string      `json:"finish_reason"`
			} `json:"choices"`
			Usage openAIUsage `json:"usage"`
		}
		if err := json.Unmarshal(response.Body.Bytes(), &decoded); err != nil {
			t.Fatal(err)
		}
		if decoded.ID != "chatcmpl-chat-json" || decoded.Model != "virtual-model" || len(decoded.Choices) != 1 ||
			decoded.Choices[0].Message.Content != "json final" || decoded.Choices[0].Message.ReasoningContent != "json thought" ||
			decoded.Choices[0].FinishReason != "stop" || decoded.Usage.PromptTokens != 4 || decoded.Usage.CompletionTokens != 3 {
			t.Fatalf("non-stream response mismatch: %+v", decoded)
		}
		if fake.poolSubmitCountSnapshot() != 1 {
			t.Fatalf("selector submissions=%d, want 1", fake.poolSubmitCountSnapshot())
		}
		assertHotPathTerminal(t, srv)
	})
}

func TestHotPathChatMixedProviderStages(t *testing.T) {
	openAICandidate := anthropicTestCandidate(t, "openai")
	anthropicCandidate := anthropicTestCandidate(t, "anthropic")
	service := &hotPathChatGateScriptedService{}
	service.steps = []hotPathChatGateStep{
		{candidate: openAICandidate, body: func(requestID string) string { return scriptedArtifactPrepare("openai", requestID) }},
		{candidate: openAICandidate, body: func(requestID string) string { return scriptedArtifactPair("openai", requestID) }},
		{candidate: openAICandidate, body: func(requestID string) string { return scriptedArtifactLocalRead("openai", requestID) }},
		{candidate: openAICandidate, body: func(string) string { return scriptedLocalWorkTool("openai") }},
		{candidate: openAICandidate, contentType: "text/event-stream", body: func(string) string { return hotPathChatMixedLocalSSE() }},
		{candidate: anthropicCandidate, body: func(requestID string) string { return scriptedReviewReadsAndInspectionWithUsage(requestID, 7, 3) }},
	}

	preset := hotPathSelectorPreset([]string{config.ModeDirect, config.ModeLight})
	preset.WorkspaceTools = []config.ExecutionWorkspaceToolAlternative{scriptedLightWorkspaceAlternative()}
	srv := NewServer(config.EdgeOpenAIConf{}, service, nil)
	srv.SetEdgeID("edge-chat-gate-mixed")
	srv.SetExecutionPresets([]config.ExecutionPreset{preset})
	providers := map[string]string{
		openAICandidate.ProviderID: "served-openai", anthropicCandidate.ProviderID: "served-anthropic",
	}
	srv.SetModelCatalog([]config.ModelCatalogEntry{
		{ID: "virtual-model", ExecutionPreset: preset.ID},
		{ID: "selector-model", Providers: providers},
		{ID: "local-model", Providers: providers},
		{ID: "review-model", Providers: providers},
	})

	tools := scriptedLightTools("openai")
	history := []any{map[string]any{"role": "user", "content": "mixed provider task"}}
	consume := func(response *httptest.ResponseRecorder, results []string) {
		t.Helper()
		assistant, ids, err := artifactAssistantFromResponse("openai", response.Body.Bytes())
		if err != nil || len(ids) != len(results) {
			t.Fatalf("consume tool response: ids=%v err=%v body=%s", ids, err, response.Body.String())
		}
		history = append(history, assistant)
		history = scriptedArtifactAppendResults("openai", history, ids, results)
	}
	request := func(stream bool) *httptest.ResponseRecorder {
		t.Helper()
		body := scriptedArtifactRequestBodyWithOptions(t, "openai", tools, history, 64, stream)
		return serveScriptedArtifactRequest(t, srv, "openai", body)
	}

	consume(request(false), []string{`{"written":true}`})
	consume(request(false), []string{`{"written":true}`, `{"written":true}`})
	consume(request(false), []string{`{"written":true}`})
	consume(request(false), []string{`{"ok":true}`})
	consume(request(false), []string{`{"written":true}`})
	response := request(true)
	if response.Code != http.StatusOK {
		t.Fatalf("status=%d body=%s", response.Code, response.Body.String())
	}
	chunks, done := decodeHotPathChatSSE(t, response.Body.String())
	if done != 1 {
		t.Fatalf("DONE count=%d body=%s", done, response.Body.String())
	}
	requestID, snapshot := soleHotPathSnapshot(t, srv)
	assertHotPathChatChunks(t, chunks, hotPathChatChunkExpectation{
		ResponseID: "msg-review-inspection", Model: "virtual-model", Content: "review-visible", Reasoning: "review-reason",
		Kinds:        []string{"reasoning", "content", "tool", "tool", "tool", "terminal"},
		FinishReason: "tool_calls", PromptTokens: 7, CompletionTokens: 3,
	})
	for _, chunk := range chunks {
		if chunk["id"] == requestID {
			t.Fatalf("logical request identity became the public response id: %+v", chunk)
		}
	}
	for _, internalID := range []string{snapshot.ActiveStageID, "run-chat-gate-5", "run-chat-gate-6"} {
		if strings.Contains(response.Body.String(), internalID) {
			t.Fatalf("internal or later-stage identity %q leaked: %s", internalID, response.Body.String())
		}
	}
	if got := service.requestCount(); got != 6 {
		t.Fatalf("provider submissions=%d, want 6", got)
	}
}

func TestHotPathChatProviderErrorBeforeCommit(t *testing.T) {
	candidate := anthropicTestCandidate(t, "openai")
	frames := make(chan *iop.ProviderTunnelFrame, 2)
	frames <- &iop.ProviderTunnelFrame{
		Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_RESPONSE_START, StatusCode: http.StatusBadGateway,
	}
	frames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_END, End: true}
	close(frames)
	srv, fake := newHotPathHandlerServer(t, candidate, frames)
	response := serveHotPathChatBody(t, srv, `{"model":"virtual-model","messages":[{"role":"user","content":"fail"}],"stream":true}`)
	if response.Code != http.StatusBadGateway || !strings.Contains(response.Body.String(), `"type":"run_error"`) || strings.Contains(response.Body.String(), "[DONE]") {
		t.Fatalf("pre-commit error mismatch: status=%d body=%s", response.Code, response.Body.String())
	}
	if fake.poolSubmitCountSnapshot() != 1 {
		t.Fatalf("selector submissions=%d, want 1", fake.poolSubmitCountSnapshot())
	}
	assertHotPathTerminal(t, srv)
}

func TestHotPathChatProviderBadRequestStaysNonRetryable(t *testing.T) {
	candidate := anthropicTestCandidate(t, "openai")
	frames := make(chan *iop.ProviderTunnelFrame, 2)
	frames <- &iop.ProviderTunnelFrame{
		Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_RESPONSE_START, StatusCode: http.StatusBadRequest,
	}
	frames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_END, End: true}
	close(frames)
	srv, fake := newHotPathHandlerServer(t, candidate, frames)
	response := serveHotPathChatBody(t, srv, `{"model":"virtual-model","messages":[{"role":"user","content":"invalid"}],"stream":true}`)
	if response.Code != http.StatusBadRequest || !strings.Contains(response.Body.String(), `"type":"invalid_request_error"`) || strings.Contains(response.Body.String(), "[DONE]") {
		t.Fatalf("provider rejection projection mismatch: status=%d body=%s", response.Code, response.Body.String())
	}
	if fake.poolSubmitCountSnapshot() != 1 {
		t.Fatalf("selector submissions=%d, want 1", fake.poolSubmitCountSnapshot())
	}
	assertHotPathTerminal(t, srv)
}

func TestHotPathChatFlushesVisibleDeltaBeforeProviderTerminal(t *testing.T) {
	candidate := anthropicTestCandidate(t, "openai")
	frames := make(chan *iop.ProviderTunnelFrame, 4)
	frames <- &iop.ProviderTunnelFrame{
		Kind:       iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_RESPONSE_START,
		StatusCode: http.StatusOK, Headers: map[string]string{"Content-Type": "text/event-stream"},
		RunId: "run-chat-gate-4",
	}
	frames <- &iop.ProviderTunnelFrame{
		Kind:  iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_BODY,
		Body:  []byte("data: {\"id\":\"chatcmpl-live-local\",\"created\":1777001201,\"choices\":[{\"index\":0,\"delta\":{\"content\":\"early-visible\"},\"finish_reason\":null}]}\n\n"),
		RunId: "run-chat-gate-4",
	}
	service := &hotPathChatGateScriptedService{}
	service.steps = []hotPathChatGateStep{
		{candidate: candidate, body: func(requestID string) string { return scriptedArtifactPrepare("openai", requestID) }},
		{candidate: candidate, body: func(requestID string) string { return scriptedArtifactPair("openai", requestID) }},
		{candidate: candidate, body: func(requestID string) string { return scriptedArtifactLocalRead("openai", requestID) }},
		{candidate: candidate, contentType: "text/event-stream", frames: frames},
	}
	preset := hotPathSelectorPreset([]string{config.ModeDirect, config.ModeLight})
	preset.WorkspaceTools = []config.ExecutionWorkspaceToolAlternative{scriptedLightWorkspaceAlternative()}
	srv := NewServer(config.EdgeOpenAIConf{}, service, nil)
	srv.SetEdgeID("edge-chat-gate-live")
	srv.SetExecutionPresets([]config.ExecutionPreset{preset})
	providers := map[string]string{candidate.ProviderID: "served-openai"}
	srv.SetModelCatalog([]config.ModelCatalogEntry{
		{ID: "virtual-model", ExecutionPreset: preset.ID},
		{ID: "selector-model", Providers: providers},
		{ID: "local-model", Providers: providers},
		{ID: "review-model", Providers: providers},
	})

	tools := scriptedLightTools("openai")
	history := []any{map[string]any{"role": "user", "content": "flush before terminal"}}
	consume := func(response *httptest.ResponseRecorder, results []string) {
		t.Helper()
		assistant, ids, err := artifactAssistantFromResponse("openai", response.Body.Bytes())
		if err != nil || len(ids) != len(results) {
			t.Fatalf("consume setup response: ids=%v err=%v body=%s", ids, err, response.Body.String())
		}
		history = append(history, assistant)
		history = scriptedArtifactAppendResults("openai", history, ids, results)
	}
	requestSetup := func() *httptest.ResponseRecorder {
		body := scriptedArtifactRequestBodyWithOptions(t, "openai", tools, history, 64, false)
		return serveScriptedArtifactRequest(t, srv, "openai", body)
	}
	consume(requestSetup(), []string{`{"written":true}`})
	consume(requestSetup(), []string{`{"written":true}`, `{"written":true}`})
	consume(requestSetup(), []string{`{"written":true}`})

	httpServer := httptest.NewServer(srv.routes())
	defer httpServer.Close()
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	body := scriptedArtifactRequestBodyWithOptions(t, "openai", tools, history, 64, true)
	request, err := http.NewRequestWithContext(ctx, http.MethodPost, httpServer.URL+"/v1/chat/completions", strings.NewReader(string(body)))
	if err != nil {
		t.Fatal(err)
	}
	response, err := http.DefaultClient.Do(request)
	if err != nil {
		t.Fatalf("stream request did not flush before terminal: %v", err)
	}
	defer response.Body.Close()
	reader := bufio.NewReader(response.Body)
	roleFrame, err := readHotPathSSEFrame(reader)
	if err != nil {
		t.Fatalf("read role before terminal: %v", err)
	}
	contentFrame, err := readHotPathSSEFrame(reader)
	if err != nil {
		t.Fatalf("read content before terminal: %v", err)
	}
	early := roleFrame + contentFrame
	if response.StatusCode != http.StatusOK || !strings.Contains(early, `"role":"assistant"`) ||
		!strings.Contains(early, `"content":"early-visible"`) || !strings.Contains(early, `"id":"chatcmpl-live-local"`) ||
		strings.Contains(early, "[DONE]") || strings.Contains(early, `"finish_reason":"`) {
		t.Fatalf("pre-terminal flush mismatch: status=%d body=%s", response.StatusCode, early)
	}
	requestID, snapshot := soleHotPathSnapshot(t, srv)

	frames <- &iop.ProviderTunnelFrame{
		Kind:  iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_BODY,
		Body:  []byte("data: {\"id\":\"chatcmpl-live-local\",\"created\":1777001201,\"choices\":[{\"index\":0,\"delta\":{\"tool_calls\":[{\"index\":0,\"id\":\"provider-live-tool\",\"type\":\"function\",\"function\":{\"name\":\"run_command\",\"arguments\":\"{\\\"command\\\":\\\"status\\\"}\"}}]},\"finish_reason\":null}]}\n\n"),
		RunId: "run-chat-gate-4",
	}
	toolFrame, err := readHotPathSSEFrame(reader)
	if err != nil {
		t.Fatalf("read tool fragment before terminal: %v", err)
	}
	if !strings.Contains(toolFrame, `"tool_calls"`) || !strings.Contains(toolFrame, `"name":"run_command"`) ||
		strings.Contains(toolFrame, "[DONE]") || strings.Contains(toolFrame, `"finish_reason":"`) {
		t.Fatalf("pre-terminal tool flush mismatch: %s", toolFrame)
	}
	frames <- &iop.ProviderTunnelFrame{
		Kind:  iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_BODY,
		Body:  []byte("data: {\"id\":\"chatcmpl-live-local\",\"created\":1777001201,\"choices\":[{\"index\":0,\"delta\":{},\"finish_reason\":\"tool_calls\"}],\"usage\":{\"prompt_tokens\":3,\"completion_tokens\":1,\"total_tokens\":4}}\n\ndata: [DONE]\n\n"),
		RunId: "run-chat-gate-4",
	}
	frames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_END, End: true, RunId: "run-chat-gate-4"}
	close(frames)
	rest, err := io.ReadAll(reader)
	if err != nil {
		t.Fatalf("read terminal stream: %v", err)
	}
	wire := early + toolFrame + string(rest)
	chunks, done := decodeHotPathChatSSE(t, wire)
	if done != 1 {
		t.Fatalf("DONE count=%d body=%s", done, wire)
	}
	assertHotPathChatChunks(t, chunks, hotPathChatChunkExpectation{
		ResponseID: "chatcmpl-live-local", Model: "virtual-model", Content: "early-visible",
		Kinds: []string{"content", "tool", "terminal"}, ToolName: "run_command", ToolArgs: `{"command":"status"}`,
		FinishReason: "tool_calls", PromptTokens: 3, CompletionTokens: 1,
	})
	for _, internalID := range []string{requestID, snapshot.ActiveStageID, "run-chat-gate-4", "provider-live-tool"} {
		if strings.Contains(wire, internalID) {
			t.Fatalf("internal identity %q leaked: %s", internalID, wire)
		}
	}
}

func TestHotPathNormalizedStageSourceRequiresIdentityOnEveryVisibleAndCompleteEvent(t *testing.T) {
	for _, eventType := range []string{"delta", "reasoning_delta", "complete"} {
		eventType := eventType
		t.Run(eventType, func(t *testing.T) {
			source := &hotPathNormalizedStageSource{}
			if err := source.observeRunEvent(&iop.RunEvent{
				Type: "delta", Delta: "first", Metadata: map[string]string{hotPathOpenAIResponseIDMetadata: "chatcmpl-event-scoped"},
			}); err != nil {
				t.Fatalf("observe valid first event: %v", err)
			}
			if err := source.observeRunEvent(&iop.RunEvent{Type: eventType, Delta: "missing"}); err == nil {
				t.Fatalf("%s without event-scoped identity was accepted", eventType)
			}
		})
	}
}

func TestHotPathLiveStageTerminalReason(t *testing.T) {
	tests := []struct {
		name, protocol, want string
		frames               chan *iop.ProviderTunnelFrame
	}{
		{
			name: "OpenAI length", protocol: "openai", want: "length",
			frames: staticProviderTunnelFrames(strings.Join([]string{
				`data: {"id":"chatcmpl-length-probe","choices":[{"delta":{"content":"limited"},"finish_reason":null}]}`,
				`data: {"id":"chatcmpl-length-probe","choices":[{"delta":{},"finish_reason":"length"}]}`,
				`data: [DONE]`, "",
			}, "\n\n")),
		},
		{
			name: "Anthropic max tokens", protocol: "anthropic", want: "max_tokens",
			frames: anthropicTunnelFrames(http.StatusOK, "text/event-stream", []byte(strings.ReplaceAll(strings.Join([]string{
				`event: message_start\ndata: {"type":"message_start","message":{"id":"msg-length-probe","usage":{"input_tokens":2}}}`,
				`event: content_block_start\ndata: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":"limited"}}`,
				`event: message_delta\ndata: {"type":"message_delta","delta":{"stop_reason":"max_tokens","stop_sequence":null},"usage":{"output_tokens":3}}`,
				`event: message_stop\ndata: {"type":"message_stop"}`, "",
			}, "\n\n"), `\n`, "\n"))),
		},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			source := newHotPathTunnelStageSource(
				edgeservice.ProviderTunnelStream{Frames: test.frames}, time.Second, newHotPathStageDecoderForProtocol(test.protocol),
			)
			outer := newHotPathOuterTurn("")
			output, terminal, err := runHotPathStreamingStage(
				context.Background(), outer,
				hotPathStageMeta{StageID: "terminal-reason", Protocol: test.protocol, Model: "model", Provider: "provider", AttemptID: test.name},
				source, source, &hotPathCountingController{},
			)
			if err != nil {
				t.Fatalf("run live stage: %v", err)
			}
			if !terminal.Success || terminal.Reason != test.want || output.TerminalReason != test.want || output.Content != "limited" {
				t.Fatalf("terminal reason projection: terminal=%+v output=%+v", terminal, output)
			}
		})
	}
	t.Run("Normalized max tokens", func(t *testing.T) {
		const responseID = "chatcmpl-normalized-length-probe"
		source := newHotPathNormalizedStageSource(edgeservice.RunStream{Events: bufferedRunEvents(
			&iop.RunEvent{Type: "delta", Delta: "limited", Metadata: map[string]string{hotPathOpenAIResponseIDMetadata: responseID}},
			&iop.RunEvent{Type: "complete", Metadata: map[string]string{
				hotPathOpenAIResponseIDMetadata: responseID, "finish_reason": "max_tokens",
			}},
		)}, time.Second)
		outer := newHotPathOuterTurn("")
		output, terminal, err := runHotPathStreamingStage(
			context.Background(), outer,
			hotPathStageMeta{StageID: "normalized-terminal-reason", Protocol: "openai", Model: "model", Provider: "provider", AttemptID: "normalized"},
			source, source, &hotPathCountingController{},
		)
		if err != nil {
			t.Fatalf("run normalized live stage: %v", err)
		}
		if !terminal.Success || terminal.Reason != "max_tokens" || output.TerminalReason != "max_tokens" || output.Content != "limited" {
			t.Fatalf("normalized terminal reason projection: terminal=%+v output=%+v", terminal, output)
		}
	})
}

func TestHotPathChatProviderLengthFlushesBeforeTerminalAndStopsLight(t *testing.T) {
	candidate := anthropicTestCandidate(t, "openai")
	frames := make(chan *iop.ProviderTunnelFrame, 4)
	frames <- &iop.ProviderTunnelFrame{
		Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_RESPONSE_START, StatusCode: http.StatusOK,
		Headers: map[string]string{"Content-Type": "text/event-stream"}, RunId: "run-chat-length-local",
	}
	frames <- &iop.ProviderTunnelFrame{
		Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_BODY, RunId: "run-chat-length-local",
		Body: []byte("data: {\"id\":\"chatcmpl-provider-length\",\"created\":1777001301,\"choices\":[{\"index\":0,\"delta\":{\"content\":\"provider-limited\"},\"finish_reason\":null}]}\n\n"),
	}
	service := &hotPathChatGateScriptedService{}
	service.steps = []hotPathChatGateStep{
		{candidate: candidate, body: func(requestID string) string { return scriptedArtifactPrepare("openai", requestID) }},
		{candidate: candidate, body: func(requestID string) string { return scriptedArtifactPair("openai", requestID) }},
		{candidate: candidate, body: func(requestID string) string { return scriptedArtifactLocalRead("openai", requestID) }},
		{candidate: candidate, contentType: "text/event-stream", frames: frames},
	}
	preset := hotPathSelectorPreset([]string{config.ModeDirect, config.ModeLight})
	preset.WorkspaceTools = []config.ExecutionWorkspaceToolAlternative{scriptedLightWorkspaceAlternative()}
	srv := NewServer(config.EdgeOpenAIConf{}, service, nil)
	srv.SetEdgeID("edge-chat-provider-length")
	srv.SetExecutionPresets([]config.ExecutionPreset{preset})
	providers := map[string]string{candidate.ProviderID: "served-openai"}
	srv.SetModelCatalog([]config.ModelCatalogEntry{
		{ID: "virtual-model", ExecutionPreset: preset.ID},
		{ID: "selector-model", Providers: providers},
		{ID: "local-model", Providers: providers},
		{ID: "review-model", Providers: providers},
	})

	tools := scriptedLightTools("openai")
	history := []any{map[string]any{"role": "user", "content": "provider length terminal"}}
	consume := func(response *httptest.ResponseRecorder, results []string) {
		t.Helper()
		assistant, ids, err := artifactAssistantFromResponse("openai", response.Body.Bytes())
		if err != nil || len(ids) != len(results) {
			t.Fatalf("consume setup response: ids=%v err=%v body=%s", ids, err, response.Body.String())
		}
		history = append(history, assistant)
		history = scriptedArtifactAppendResults("openai", history, ids, results)
	}
	requestSetup := func() *httptest.ResponseRecorder {
		body := scriptedArtifactRequestBodyWithOptions(t, "openai", tools, history, 64, false)
		return serveScriptedArtifactRequest(t, srv, "openai", body)
	}
	consume(requestSetup(), []string{`{"written":true}`})
	consume(requestSetup(), []string{`{"written":true}`, `{"written":true}`})
	consume(requestSetup(), []string{`{"written":true}`})

	httpServer := httptest.NewServer(srv.routes())
	defer httpServer.Close()
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	body := scriptedArtifactRequestBodyWithOptions(t, "openai", tools, history, 64, true)
	request, err := http.NewRequestWithContext(ctx, http.MethodPost, httpServer.URL+"/v1/chat/completions", strings.NewReader(string(body)))
	if err != nil {
		t.Fatal(err)
	}
	response, err := http.DefaultClient.Do(request)
	if err != nil {
		t.Fatalf("stream request did not flush before terminal: %v", err)
	}
	defer response.Body.Close()
	reader := bufio.NewReader(response.Body)
	roleFrame, err := readHotPathSSEFrame(reader)
	if err != nil {
		t.Fatalf("read role before provider terminal: %v", err)
	}
	contentFrame, err := readHotPathSSEFrame(reader)
	if err != nil {
		t.Fatalf("read content before provider terminal: %v", err)
	}
	early := roleFrame + contentFrame
	if response.StatusCode != http.StatusOK || !strings.Contains(early, `"role":"assistant"`) ||
		!strings.Contains(early, `"content":"provider-limited"`) || !strings.Contains(early, `"id":"chatcmpl-provider-length"`) ||
		strings.Contains(early, "[DONE]") || strings.Contains(early, `"finish_reason":"`) {
		t.Fatalf("pre-terminal provider length flush mismatch: status=%d body=%s", response.StatusCode, early)
	}
	requestID, snapshot := soleHotPathSnapshot(t, srv)

	frames <- &iop.ProviderTunnelFrame{
		Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_BODY, RunId: "run-chat-length-local",
		Body: []byte("data: {\"id\":\"chatcmpl-provider-length\",\"created\":1777001301,\"choices\":[{\"index\":0,\"delta\":{},\"finish_reason\":\"length\"}],\"usage\":{\"prompt_tokens\":5,\"completion_tokens\":6,\"total_tokens\":11}}\n\ndata: [DONE]\n\n"),
	}
	frames <- &iop.ProviderTunnelFrame{Kind: iop.ProviderTunnelFrameKind_PROVIDER_TUNNEL_FRAME_KIND_END, End: true, RunId: "run-chat-length-local"}
	close(frames)
	rest, err := io.ReadAll(reader)
	if err != nil {
		t.Fatalf("read provider length terminal: %v", err)
	}
	wire := early + string(rest)
	chunks, done := decodeHotPathChatSSE(t, wire)
	if done != 1 {
		t.Fatalf("DONE count=%d body=%s", done, wire)
	}
	assertHotPathChatChunks(t, chunks, hotPathChatChunkExpectation{
		ResponseID: "chatcmpl-provider-length", Model: "virtual-model", Content: "provider-limited",
		Kinds: []string{"content", "terminal"}, FinishReason: "length", PromptTokens: 5, CompletionTokens: 6,
	})
	if got := service.requestCount(); got != 4 {
		t.Fatalf("provider submissions=%d, want 4 with no review dispatch", got)
	}
	if srv.lightFlows.has(requestID, srv.edgeIDValue()) {
		t.Fatalf("provider length retained light state for %q", requestID)
	}
	assertHotPathTerminal(t, srv)
	for _, internalID := range []string{requestID, snapshot.ActiveStageID, "run-chat-length-local"} {
		if strings.Contains(wire, internalID) {
			t.Fatalf("internal identity %q leaked: %s", internalID, wire)
		}
	}
}

func readHotPathSSEFrame(reader *bufio.Reader) (string, error) {
	var frame strings.Builder
	for {
		line, err := reader.ReadString('\n')
		frame.WriteString(line)
		if err != nil {
			return frame.String(), err
		}
		if line == "\n" || line == "\r\n" {
			return frame.String(), nil
		}
	}
}

type hotPathChatChunkExpectation struct {
	ResponseID, Model, Content, Reasoning    string
	ToolID, ToolName, ToolArgs, FinishReason string
	Kinds                                    []string
	PromptTokens, CompletionTokens           int
}

func assertHotPathChatChunks(t *testing.T, chunks []map[string]any, want hotPathChatChunkExpectation) {
	t.Helper()
	var content, reasoning, toolID, toolName, toolArgs, finish string
	var kinds []string
	roleCount := 0
	terminalCount := 0
	toolIndex := -1
	promptTokens := 0
	completionTokens := 0
	for _, chunk := range chunks {
		if chunk["id"] != want.ResponseID || chunk["model"] != want.Model {
			t.Fatalf("chunk identity mismatch: %+v", chunk)
		}
		choices, ok := chunk["choices"].([]any)
		if !ok || len(choices) != 1 {
			t.Fatalf("chunk choices mismatch: %+v", chunk)
		}
		choice := choices[0].(map[string]any)
		delta := choice["delta"].(map[string]any)
		if delta["role"] == "assistant" {
			roleCount++
		}
		if text, _ := delta["content"].(string); text != "" {
			content += text
			kinds = append(kinds, "content")
		}
		if text, _ := delta["reasoning_content"].(string); text != "" {
			reasoning += text
			kinds = append(kinds, "reasoning")
		}
		if tools, ok := delta["tool_calls"].([]any); ok {
			if len(tools) != 1 {
				t.Fatalf("tool delta count=%d chunk=%+v", len(tools), chunk)
			}
			tool := tools[0].(map[string]any)
			index := int(tool["index"].(float64))
			if toolIndex == -1 {
				toolIndex = index
			} else if want.ToolName != "" && toolIndex != index {
				t.Fatalf("tool index changed from %d to %d", toolIndex, index)
			}
			if id, _ := tool["id"].(string); id != "" {
				toolID = id
			}
			function := tool["function"].(map[string]any)
			if name, _ := function["name"].(string); name != "" {
				toolName = name
			}
			if args, _ := function["arguments"].(string); args != "" {
				toolArgs += args
			}
			kinds = append(kinds, "tool")
		}
		if reason, _ := choice["finish_reason"].(string); reason != "" {
			finish = reason
			terminalCount++
			kinds = append(kinds, "terminal")
			if usage, ok := chunk["usage"].(map[string]any); ok {
				promptTokens = int(usage["prompt_tokens"].(float64))
				completionTokens = int(usage["completion_tokens"].(float64))
			}
		}
	}
	if roleCount != 1 || terminalCount != 1 || content != want.Content || reasoning != want.Reasoning ||
		finish != want.FinishReason || promptTokens != want.PromptTokens || completionTokens != want.CompletionTokens ||
		strings.Join(kinds, ",") != strings.Join(want.Kinds, ",") {
		t.Fatalf("chunk aggregate mismatch: role=%d terminal=%d content=%q reasoning=%q finish=%q usage=%d/%d kinds=%v chunks=%+v",
			roleCount, terminalCount, content, reasoning, finish, promptTokens, completionTokens, kinds, chunks)
	}
	if want.ToolName != "" {
		if toolIndex != 0 || toolName != want.ToolName || toolArgs != want.ToolArgs {
			t.Fatalf("tool aggregate mismatch: index=%d id=%q name=%q args=%q", toolIndex, toolID, toolName, toolArgs)
		}
		if want.ToolID != "" && toolID != want.ToolID {
			t.Fatalf("tool id=%q, want %q", toolID, want.ToolID)
		}
	}
}

func decodeHotPathChatSSE(t *testing.T, body string) ([]map[string]any, int) {
	t.Helper()
	var chunks []map[string]any
	done := 0
	for _, frame := range strings.Split(body, "\n\n") {
		frame = strings.TrimSpace(frame)
		if frame == "" {
			continue
		}
		if !strings.HasPrefix(frame, "data: ") {
			t.Fatalf("unexpected SSE frame %q", frame)
		}
		data := strings.TrimSpace(strings.TrimPrefix(frame, "data: "))
		if data == "[DONE]" {
			done++
			continue
		}
		var chunk map[string]any
		if err := json.Unmarshal([]byte(data), &chunk); err != nil {
			t.Fatalf("decode SSE chunk: %v data=%s", err, data)
		}
		chunks = append(chunks, chunk)
	}
	return chunks, done
}

func serveHotPathChatBody(t *testing.T, srv *Server, body string) *httptest.ResponseRecorder {
	t.Helper()
	request := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(body))
	response := httptest.NewRecorder()
	srv.routes().ServeHTTP(response, request)
	return response
}

type hotPathChatGateStep struct {
	candidate   edgeservice.ProviderPoolCandidate
	contentType string
	body        func(string) string
	frames      chan *iop.ProviderTunnelFrame
}

type hotPathChatGateScriptedService struct {
	providerFakeRunService
	mu       sync.Mutex
	steps    []hotPathChatGateStep
	requests []edgeservice.ProviderPoolDispatchRequest
}

func (s *hotPathChatGateScriptedService) SubmitProviderPool(ctx context.Context, req edgeservice.ProviderPoolDispatchRequest) (*edgeservice.ProviderPoolDispatchResult, error) {
	if err := ctx.Err(); err != nil {
		return nil, err
	}
	s.mu.Lock()
	index := len(s.requests)
	if index >= len(s.steps) {
		s.mu.Unlock()
		return nil, fmt.Errorf("unexpected Chat gate dispatch %d", index+1)
	}
	s.requests = append(s.requests, req)
	step := s.steps[index]
	s.mu.Unlock()

	dispatch := edgeservice.RunDispatch{
		RunID: fmt.Sprintf("run-chat-gate-%d", index+1), NodeID: "node-chat-gate",
		ModelGroupKey: req.Run.ModelGroupKey, ProviderID: step.candidate.ProviderID,
		ExecutionPath: string(edgeservice.ProviderPoolPathTunnel), ProfileID: step.candidate.ProfileID,
		ProfileDriver:       step.candidate.ProfileDriver,
		ProfileCapabilities: append([]string(nil), step.candidate.ProfileCapabilities...),
	}
	contentType := step.contentType
	if contentType == "" {
		contentType = "application/json"
	}
	frames := step.frames
	if frames == nil {
		providerBody, operation, err := materializeScriptedProviderBody(req, step.candidate)
		if err != nil {
			return nil, err
		}
		var requestID string
		if strings.Contains(string(providerBody), "IOP caller-workspace selector instruction") {
			requestID, err = scriptedSelectorRequestID(providerBody, operation)
		} else {
			requestID, err = scriptedRequestIDFromProviderBody(providerBody)
		}
		if err != nil {
			return nil, err
		}
		body := step.body(requestID)
		frames = hotPathTunnelFrames(body, contentType, dispatch.RunID, 1_777_001_100_000_000_000+int64(index))
	}
	return &edgeservice.ProviderPoolDispatchResult{
		Path:   edgeservice.ProviderPoolPathTunnel,
		Tunnel: &fakeTunnelHandle{dispatch: dispatch, frames: frames}, DispatchInfo: dispatch,
	}, nil
}

func (s *hotPathChatGateScriptedService) requestCount() int {
	s.mu.Lock()
	defer s.mu.Unlock()
	return len(s.requests)
}

func hotPathChatMixedLocalSSE() string {
	return strings.Join([]string{
		`data: {"id":"chatcmpl-mixed-local","created":1777001101,"choices":[{"index":0,"delta":{"content":"local-A "},"finish_reason":null}]}`,
		`data: {"id":"chatcmpl-mixed-local","created":1777001101,"choices":[{"index":0,"delta":{"reasoning_content":"local-think "},"finish_reason":null}]}`,
		`data: {"id":"chatcmpl-mixed-local","created":1777001101,"choices":[{"index":0,"delta":{"content":"local-B"},"finish_reason":null}]}`,
		`data: {"id":"chatcmpl-mixed-local","created":1777001101,"choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":{"prompt_tokens":5,"completion_tokens":4,"total_tokens":9}}`,
		`data: [DONE]`, "",
	}, "\n\n")
}

func hotPathChatMixedReviewSSE(requestID string) string {
	args := hotPathChatReviewArguments(requestID)
	events := []any{
		map[string]any{"type": "message_start", "message": map[string]any{
			"id": "msg-mixed-review", "type": "message", "role": "assistant", "content": []any{},
			"usage": map[string]any{"input_tokens": 7, "output_tokens": 0},
		}},
		map[string]any{"type": "content_block_start", "index": 0, "content_block": map[string]any{"type": "thinking", "thinking": "review-think ", "signature": ""}},
		map[string]any{"type": "content_block_start", "index": 1, "content_block": map[string]any{"type": "text", "text": "review-visible"}},
		map[string]any{"type": "content_block_start", "index": 2, "content_block": map[string]any{"type": "tool_use", "id": "provider-review-write", "name": "write_file", "input": json.RawMessage(args)}},
		map[string]any{"type": "message_delta", "delta": map[string]any{"stop_reason": "tool_use", "stop_sequence": nil}, "usage": map[string]any{"output_tokens": 3}},
		map[string]any{"type": "message_stop"},
	}
	var builder strings.Builder
	for _, event := range events {
		encoded, _ := json.Marshal(event)
		fmt.Fprintf(&builder, "data: %s\n\n", encoded)
	}
	return builder.String()
}

func hotPathChatReviewArguments(requestID string) string {
	encoded, _ := json.Marshal(map[string]string{
		"content": "review", "path": newReservedPaths(requestID).ReviewPath,
	})
	return string(encoded)
}
