package clients

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"sort"
	"strings"
	"time"
)

const (
	ProtocolOpenAIChatCompletions = "openai_chat_completions"
	ProtocolOpenAIResponses       = "openai_responses"
	ProtocolAnthropicMessages     = "anthropic_messages"
	ProtocolOpenAIEmbeddings      = "openai_embeddings"
	ProtocolOpenAIImages          = "openai_images"
	ProtocolGeminiGenerateContent = "gemini_generate_content"
	ProtocolVolcesContents        = "volces_contents_generations_v3"
	ProtocolKlingV1Omni           = "kling_v1_omni_video"
	ProtocolKlingV2Omni           = "kling_v2_omni_video"
)

var supportedResponseFallbackParameters = map[string]struct{}{
	"model": {}, "input": {}, "messages": {}, "instructions": {}, "tools": {}, "tool_choice": {},
	"parallel_tool_calls": {}, "max_output_tokens": {}, "temperature": {}, "top_p": {},
	"presence_penalty": {}, "frequency_penalty": {}, "reasoning": {}, "text": {},
	"stream": {}, "stream_options": {}, "store": {}, "previous_response_id": {}, "metadata": {}, "user": {},
	"moderation": {}, "prompt_cache_key": {}, "prompt_cache_options": {}, "prompt_cache_retention": {},
	"safety_identifier": {}, "service_tier": {}, "top_logprobs": {},
}

func ResponsesRequestToChat(body map[string]any, history []ResponseTurn) (map[string]any, error) {
	for key := range body {
		if _, internal := gatewayOpenAIRequestExtensions[key]; internal {
			continue
		}
		if _, ok := supportedResponseFallbackParameters[key]; !ok {
			return nil, unsupportedResponseParameter(key)
		}
	}
	messages := make([]any, 0)
	for _, turn := range history {
		priorInput := turn.Request["input"]
		if priorInput == nil {
			priorInput = turn.Request["messages"]
		}
		priorMessages, err := responseInputMessages(priorInput)
		if err != nil {
			return nil, err
		}
		messages = append(messages, priorMessages...)
		if assistant := chatAssistantMessage(turn.Internal, turn.Response); assistant != nil {
			messages = append(messages, assistant)
		}
	}
	if instructions := strings.TrimSpace(stringFromAny(body["instructions"])); instructions != "" {
		messages = append(messages, map[string]any{"role": "system", "content": instructions})
	}
	if rawMessages, ok := body["messages"].([]any); ok {
		messages = append(messages, rawMessages...)
	} else {
		currentMessages, err := responseInputMessages(body["input"])
		if err != nil {
			return nil, err
		}
		messages = append(messages, currentMessages...)
	}
	if len(messages) == 0 {
		return nil, &ClientError{Code: "invalid_parameter", Message: "input is required", StatusCode: http.StatusBadRequest}
	}
	out := map[string]any{"messages": messages}
	for _, key := range []string{
		"temperature", "top_p", "presence_penalty", "frequency_penalty", "parallel_tool_calls",
		"stream", "stream_options", "store", "metadata", "user", "moderation", "prompt_cache_key",
		"prompt_cache_options", "prompt_cache_retention", "safety_identifier", "service_tier",
	} {
		if value, ok := body[key]; ok {
			out[key] = value
		}
	}
	if value, ok := body["top_logprobs"]; ok {
		out["top_logprobs"] = value
		out["logprobs"] = true
	}
	if value, ok := body["max_output_tokens"]; ok {
		out["max_tokens"] = value
	}
	if rawReasoning, ok := body["reasoning"]; ok {
		reasoning, ok := rawReasoning.(map[string]any)
		if !ok {
			return nil, unsupportedResponseParameter("reasoning")
		}
		for key := range reasoning {
			if key != "effort" {
				return nil, unsupportedResponseParameter("reasoning." + key)
			}
		}
		if effort, ok := reasoning["effort"]; ok {
			out["reasoning_effort"] = effort
		}
	}
	if rawText, ok := body["text"]; ok {
		responseFormat, verbosity, err := responseTextParams(rawText)
		if err != nil {
			return nil, err
		}
		if responseFormat != nil {
			out["response_format"] = responseFormat
		}
		if verbosity != nil {
			out["verbosity"] = verbosity
		}
	}
	if rawTools, ok := body["tools"]; ok {
		tools, err := responseToolsToChat(rawTools)
		if err != nil {
			return nil, err
		}
		out["tools"] = tools
	}
	if rawChoice, ok := body["tool_choice"]; ok {
		choice, err := responseToolChoiceToChat(rawChoice)
		if err != nil {
			return nil, err
		}
		out["tool_choice"] = choice
	}
	return out, nil
}

func responseInputMessages(value any) ([]any, error) {
	switch typed := value.(type) {
	case nil:
		return nil, nil
	case string:
		return []any{map[string]any{"role": "user", "content": typed}}, nil
	case []any:
		out := make([]any, 0, len(typed))
		for _, raw := range typed {
			switch item := raw.(type) {
			case string:
				out = append(out, map[string]any{"role": "user", "content": item})
			case map[string]any:
				switch stringFromAny(item["type"]) {
				case "function_call_output":
					callID := firstNonEmptyString(item["call_id"], item["id"])
					if callID == "" {
						return nil, &ClientError{Code: "invalid_parameter", Message: "function_call_output.call_id is required", StatusCode: http.StatusBadRequest}
					}
					out = append(out, map[string]any{"role": "tool", "tool_call_id": callID, "content": toolResultContent(item["output"])})
				case "message", "":
					role := firstNonEmptyString(item["role"], "user")
					out = append(out, map[string]any{"role": role, "content": responseContentToChat(item["content"])})
				default:
					return nil, unsupportedResponseParameter("input.type=" + stringFromAny(item["type"]))
				}
			default:
				return nil, unsupportedResponseParameter("input")
			}
		}
		return out, nil
	case map[string]any:
		return responseInputMessages([]any{typed})
	default:
		return nil, unsupportedResponseParameter("input")
	}
}

func responseContentToChat(value any) any {
	items, ok := value.([]any)
	if !ok {
		return value
	}
	out := make([]any, 0, len(items))
	for _, raw := range items {
		item, ok := raw.(map[string]any)
		if !ok {
			out = append(out, raw)
			continue
		}
		switch stringFromAny(item["type"]) {
		case "input_text", "output_text", "text":
			out = append(out, map[string]any{"type": "text", "text": stringFromAny(item["text"])})
		case "input_image":
			url := firstNonEmptyString(item["image_url"], item["url"])
			out = append(out, map[string]any{"type": "image_url", "image_url": map[string]any{"url": url}})
		default:
			out = append(out, item)
		}
	}
	return out
}

func responseToolsToChat(value any) ([]any, error) {
	items, ok := value.([]any)
	if !ok {
		return nil, unsupportedResponseParameter("tools")
	}
	out := make([]any, 0, len(items))
	for _, raw := range items {
		tool, ok := raw.(map[string]any)
		if !ok || stringFromAny(tool["type"]) != "function" {
			return nil, &ClientError{Code: "unsupported_response_tool", Message: "Chat fallback only supports custom function tools", StatusCode: http.StatusBadRequest}
		}
		function := map[string]any{
			"name":        tool["name"],
			"description": tool["description"],
			"parameters":  tool["parameters"],
		}
		if strict, ok := tool["strict"]; ok {
			function["strict"] = strict
		}
		out = append(out, map[string]any{"type": "function", "function": function})
	}
	return out, nil
}

func responseToolChoiceToChat(value any) (any, error) {
	if text, ok := value.(string); ok {
		switch text {
		case "auto", "none", "required":
			return text, nil
		default:
			return nil, unsupportedResponseParameter("tool_choice")
		}
	}
	choice, ok := value.(map[string]any)
	if !ok || stringFromAny(choice["type"]) != "function" || stringFromAny(choice["name"]) == "" {
		return nil, unsupportedResponseParameter("tool_choice")
	}
	return map[string]any{"type": "function", "function": map[string]any{"name": choice["name"]}}, nil
}

func responseTextParams(value any) (map[string]any, any, error) {
	text, ok := value.(map[string]any)
	if !ok {
		return nil, nil, unsupportedResponseParameter("text")
	}
	for key := range text {
		if key != "format" && key != "verbosity" {
			return nil, nil, unsupportedResponseParameter("text." + key)
		}
	}
	verbosity := text["verbosity"]
	format, ok := text["format"].(map[string]any)
	if !ok || len(format) == 0 {
		return nil, verbosity, nil
	}
	switch stringFromAny(format["type"]) {
	case "text":
		return map[string]any{"type": "text"}, verbosity, nil
	case "json_object":
		return map[string]any{"type": "json_object"}, verbosity, nil
	case "json_schema":
		return map[string]any{"type": "json_schema", "json_schema": map[string]any{
			"name": format["name"], "schema": format["schema"], "strict": format["strict"],
		}}, verbosity, nil
	default:
		return nil, nil, unsupportedResponseParameter("text.format.type")
	}
}

func chatAssistantMessage(internal map[string]any, visible map[string]any) map[string]any {
	if choices, ok := internal["choices"].([]any); ok && len(choices) > 0 {
		choice, _ := choices[0].(map[string]any)
		if message, ok := choice["message"].(map[string]any); ok {
			return cloneMapAny(message)
		}
	}
	output, _ := visible["output"].([]any)
	message := map[string]any{"role": "assistant"}
	textParts := make([]string, 0)
	toolCalls := make([]any, 0)
	for _, raw := range output {
		item, _ := raw.(map[string]any)
		switch stringFromAny(item["type"]) {
		case "message":
			content, _ := item["content"].([]any)
			for _, rawContent := range content {
				part, _ := rawContent.(map[string]any)
				if stringFromAny(part["type"]) == "output_text" {
					textParts = append(textParts, stringFromAny(part["text"]))
				}
			}
		case "function_call":
			toolCalls = append(toolCalls, map[string]any{
				"id": item["call_id"], "type": "function",
				"function": map[string]any{"name": item["name"], "arguments": item["arguments"]},
			})
		}
	}
	message["content"] = strings.Join(textParts, "")
	if len(toolCalls) > 0 {
		message["tool_calls"] = toolCalls
	}
	if len(textParts) == 0 && len(toolCalls) == 0 {
		return nil
	}
	return message
}

func ChatResultToResponse(chat map[string]any, publicID string, model string, requestBody map[string]any) map[string]any {
	createdAt := time.Now().Unix()
	if value := intFromAny(chat["created"]); value > 0 {
		createdAt = int64(value)
	}
	output := make([]any, 0)
	outputText := ""
	choices, _ := chat["choices"].([]any)
	if len(choices) > 0 {
		choice, _ := choices[0].(map[string]any)
		message, _ := choice["message"].(map[string]any)
		if content := visibleChatContent(message["content"]); content != "" {
			outputText = content
			output = append(output, map[string]any{
				"id": "msg_" + responseIDSuffix(publicID), "type": "message", "status": "completed", "role": "assistant",
				"content": []any{map[string]any{"type": "output_text", "text": content, "annotations": []any{}, "logprobs": []any{}}},
			})
		}
		toolCalls, _ := message["tool_calls"].([]any)
		for index, rawToolCall := range toolCalls {
			toolCall, _ := rawToolCall.(map[string]any)
			function, _ := toolCall["function"].(map[string]any)
			callID := firstNonEmptyString(toolCall["id"], fmt.Sprintf("call_%d", index))
			output = append(output, map[string]any{
				"id":   "fc_" + responseIDSuffix(publicID) + fmt.Sprintf("_%d", index),
				"type": "function_call", "status": "completed", "call_id": callID,
				"name": stringFromAny(function["name"]), "arguments": stringFromAny(function["arguments"]),
			})
		}
	}
	usage := responseUsageFromChat(chat["usage"])
	out := map[string]any{
		"id": publicID, "object": "response", "created_at": createdAt, "status": "completed",
		"model": model, "output": output, "output_text": outputText, "error": nil, "incomplete_details": nil,
		"usage": usage,
	}
	if previousResponseID := strings.TrimSpace(stringFromAny(requestBody["previous_response_id"])); previousResponseID != "" {
		out["previous_response_id"] = previousResponseID
	} else {
		out["previous_response_id"] = nil
	}
	applyResponseFinishReason(out, choices)
	for _, key := range []string{"instructions", "parallel_tool_calls", "temperature", "top_p", "tools", "tool_choice", "metadata"} {
		if value, ok := requestBody[key]; ok {
			out[key] = value
		}
	}
	return out
}

func applyResponseFinishReason(response map[string]any, choices []any) {
	if len(choices) == 0 {
		return
	}
	choice, _ := choices[0].(map[string]any)
	switch strings.TrimSpace(stringFromAny(choice["finish_reason"])) {
	case "length":
		response["status"] = "incomplete"
		response["incomplete_details"] = map[string]any{"reason": "max_output_tokens"}
	case "content_filter":
		response["status"] = "incomplete"
		response["incomplete_details"] = map[string]any{"reason": "content_filter"}
	}
}

func responseUsageFromChat(value any) map[string]any {
	usage, _ := value.(map[string]any)
	normalized := usageFromOpenAIUsage(usage)
	return map[string]any{
		"input_tokens":          normalized.InputTokens,
		"input_tokens_details":  map[string]any{"cached_tokens": normalized.CachedInputTokens},
		"output_tokens":         normalized.OutputTokens,
		"output_tokens_details": map[string]any{"reasoning_tokens": reasoningTokensFromUsage(usage)},
		"total_tokens":          normalized.TotalTokens,
	}
}

func reasoningTokensFromUsage(usage map[string]any) int {
	for _, key := range []string{"completion_tokens_details", "output_tokens_details"} {
		if details, ok := usage[key].(map[string]any); ok {
			if value := intFromAny(details["reasoning_tokens"]); value > 0 {
				return value
			}
		}
	}
	return 0
}

func visibleChatContent(value any) string {
	if text, ok := value.(string); ok {
		return text
	}
	items, _ := value.([]any)
	parts := make([]string, 0, len(items))
	for _, raw := range items {
		item, _ := raw.(map[string]any)
		if text := stringFromAny(firstPresent(item["text"], item["content"])); text != "" && !isReasoningContentBlock(item) {
			parts = append(parts, text)
		}
	}
	return strings.Join(parts, "")
}

func decodeNativeResponsesStream(resp *http.Response, onDelta StreamDelta) (map[string]any, string, error) {
	defer resp.Body.Close()
	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		raw, _ := io.ReadAll(io.LimitReader(resp.Body, 16*1024*1024))
		return nil, "", &ClientError{Code: statusCodeName(resp.StatusCode), Message: errorMessage(raw, resp.Status), StatusCode: resp.StatusCode, RequestID: requestIDFromHTTPResponse(resp), Retryable: HTTPRetryable(resp.StatusCode)}
	}
	scanner := bufio.NewScanner(resp.Body)
	scanner.Buffer(make([]byte, 0, 64*1024), 16*1024*1024)
	var completed map[string]any
	upstreamID := ""
	rawLines := make([]string, 0)
	eventName := ""
	dataLines := make([]string, 0)
	processFrame := func() error {
		payload := strings.TrimSpace(strings.Join(dataLines, "\n"))
		frameEventName := eventName
		eventName = ""
		dataLines = dataLines[:0]
		if payload == "" || payload == "[DONE]" {
			return nil
		}
		var event map[string]any
		if err := json.Unmarshal([]byte(payload), &event); err != nil {
			return nil
		}
		if stringFromAny(event["type"]) == "" && frameEventName != "" {
			event["type"] = frameEventName
		}
		if failure := responseStreamError(event); failure != nil {
			return failure
		}
		if response, ok := event["response"].(map[string]any); ok && upstreamID == "" {
			upstreamID = stringFromAny(response["id"])
		}
		if stringFromAny(event["type"]) == "response.completed" {
			completed, _ = event["response"].(map[string]any)
			if onDelta != nil {
				return onDelta(StreamDeltaEvent{Event: event})
			}
			return nil
		}
		if stringFromAny(event["type"]) == "response.failed" {
			if onDelta != nil {
				if err := onDelta(StreamDeltaEvent{Event: event}); err != nil {
					return err
				}
			}
			failed, _ := event["response"].(map[string]any)
			failure, _ := failed["error"].(map[string]any)
			code := firstNonEmptyString(failure["code"], "response_failed")
			message := firstNonEmptyString(failure["message"], "upstream Responses request failed")
			return &ClientError{Code: code, Message: message, StatusCode: http.StatusBadRequest, Retryable: false}
		}
		if onDelta != nil {
			return onDelta(StreamDeltaEvent{Text: nativeResponseDeltaText(event), Event: event})
		}
		return nil
	}
	for scanner.Scan() {
		rawLine := scanner.Text()
		rawLines = append(rawLines, rawLine)
		line := strings.TrimSpace(rawLine)
		if line == "" {
			if err := processFrame(); err != nil {
				return nil, upstreamID, err
			}
			continue
		}
		if strings.HasPrefix(line, "event:") {
			eventName = strings.TrimSpace(strings.TrimPrefix(line, "event:"))
			continue
		}
		if strings.HasPrefix(line, "data:") {
			dataLines = append(dataLines, strings.TrimSpace(strings.TrimPrefix(line, "data:")))
		}
	}
	if err := processFrame(); err != nil {
		return nil, upstreamID, err
	}
	if err := scanner.Err(); err != nil {
		return nil, upstreamID, &ClientError{Code: "stream_read_error", Message: err.Error(), Retryable: true}
	}
	if completed == nil {
		raw := strings.TrimSpace(strings.Join(rawLines, "\n"))
		var response map[string]any
		if raw != "" && json.Unmarshal([]byte(raw), &response) == nil {
			if failure := responseStreamError(response); failure != nil {
				return nil, upstreamID, failure
			}
			if stringFromAny(response["object"]) == "response" {
				upstreamID = stringFromAny(response["id"])
				return response, upstreamID, nil
			}
		}
		return nil, upstreamID, &ClientError{Code: "invalid_response", Message: "Responses stream ended without response.completed", Retryable: false}
	}
	return completed, upstreamID, nil
}

func responseStreamError(event map[string]any) error {
	if stringFromAny(event["type"]) != "error" && event["error"] == nil {
		return nil
	}
	failure, _ := event["error"].(map[string]any)
	if failure == nil {
		failure = event
	}
	code := firstNonEmptyString(failure["code"], "response_failed")
	message := firstNonEmptyString(failure["message"], "upstream Responses stream failed")
	return &ClientError{Code: code, Message: message, StatusCode: http.StatusBadRequest, Retryable: false}
}

func nativeResponseDeltaText(event map[string]any) string {
	if stringFromAny(event["type"]) == "response.output_text.delta" {
		return stringFromAny(event["delta"])
	}
	return ""
}

type chatResponsesStreamAdapter struct {
	publicID   string
	model      string
	started    bool
	sequence   int
	nextOutput int
	textOutput int
	textAdded  bool
	text       strings.Builder
	tools      map[int]*chatResponseStreamTool
}

type chatResponseStreamTool struct {
	ItemID      string
	CallID      string
	Name        string
	Arguments   strings.Builder
	Added       bool
	OutputIndex int
}

func newChatResponsesStreamAdapter(publicID string, model string) *chatResponsesStreamAdapter {
	return &chatResponsesStreamAdapter{publicID: publicID, model: model, textOutput: -1, tools: map[int]*chatResponseStreamTool{}}
}

func (a *chatResponsesStreamAdapter) emit(onDelta StreamDelta, event map[string]any) error {
	if onDelta == nil {
		return nil
	}
	event["sequence_number"] = a.sequence
	a.sequence++
	return onDelta(StreamDeltaEvent{Text: nativeResponseDeltaText(event), Event: event})
}

func (a *chatResponsesStreamAdapter) start(onDelta StreamDelta) error {
	if a.started || onDelta == nil {
		return nil
	}
	a.started = true
	response := map[string]any{"id": a.publicID, "object": "response", "created_at": time.Now().Unix(), "status": "in_progress", "model": a.model, "output": []any{}}
	for _, eventType := range []string{"response.created", "response.in_progress"} {
		if err := a.emit(onDelta, map[string]any{"type": eventType, "response": response}); err != nil {
			return err
		}
	}
	return nil
}

func (a *chatResponsesStreamAdapter) delta(event StreamDeltaEvent, onDelta StreamDelta) error {
	if onDelta == nil || event.Event == nil {
		return nil
	}
	if err := a.start(onDelta); err != nil {
		return err
	}
	choices, _ := event.Event["choices"].([]any)
	for _, rawChoice := range choices {
		choice, _ := rawChoice.(map[string]any)
		delta, _ := choice["delta"].(map[string]any)
		if content := stringFromAny(delta["content"]); content != "" {
			if !a.textAdded {
				a.textAdded = true
				a.textOutput = a.nextOutput
				a.nextOutput++
				itemID := "msg_" + responseIDSuffix(a.publicID)
				if err := a.emit(onDelta, map[string]any{
					"type": "response.output_item.added", "response_id": a.publicID, "output_index": a.textOutput,
					"item": map[string]any{"id": itemID, "type": "message", "status": "in_progress", "role": "assistant", "content": []any{}},
				}); err != nil {
					return err
				}
				if err := a.emit(onDelta, map[string]any{
					"type": "response.content_part.added", "response_id": a.publicID, "item_id": itemID,
					"output_index": a.textOutput, "content_index": 0,
					"part": map[string]any{"type": "output_text", "text": "", "annotations": []any{}, "logprobs": []any{}},
				}); err != nil {
					return err
				}
			}
			a.text.WriteString(content)
			if err := a.emit(onDelta, map[string]any{
				"type": "response.output_text.delta", "response_id": a.publicID,
				"item_id": "msg_" + responseIDSuffix(a.publicID), "output_index": a.textOutput, "content_index": 0, "delta": content,
			}); err != nil {
				return err
			}
		}
		toolCalls, _ := delta["tool_calls"].([]any)
		for _, rawToolCall := range toolCalls {
			toolCall, _ := rawToolCall.(map[string]any)
			index := intFromAny(toolCall["index"])
			tool := a.tools[index]
			if tool == nil {
				tool = &chatResponseStreamTool{ItemID: fmt.Sprintf("fc_%s_%d", responseIDSuffix(a.publicID), index), OutputIndex: a.nextOutput}
				a.nextOutput++
				a.tools[index] = tool
			}
			if callID := stringFromAny(toolCall["id"]); callID != "" {
				tool.CallID = callID
			}
			function, _ := toolCall["function"].(map[string]any)
			if name := stringFromAny(function["name"]); name != "" {
				tool.Name += name
			}
			arguments := stringFromAny(function["arguments"])
			if !tool.Added && (tool.CallID != "" || tool.Name != "" || arguments != "") {
				tool.Added = true
				if err := a.emit(onDelta, map[string]any{
					"type": "response.output_item.added", "response_id": a.publicID, "output_index": tool.OutputIndex,
					"item": map[string]any{"id": tool.ItemID, "type": "function_call", "status": "in_progress", "call_id": tool.CallID, "name": tool.Name, "arguments": ""},
				}); err != nil {
					return err
				}
			}
			if arguments != "" {
				tool.Arguments.WriteString(arguments)
				if err := a.emit(onDelta, map[string]any{
					"type": "response.function_call_arguments.delta", "response_id": a.publicID,
					"item_id": tool.ItemID, "output_index": tool.OutputIndex, "delta": arguments,
				}); err != nil {
					return err
				}
			}
		}
	}
	return nil
}

func (a *chatResponsesStreamAdapter) done(result map[string]any, onDelta StreamDelta) error {
	if onDelta == nil {
		return nil
	}
	a.alignOutput(result)
	if a.text.Len() > 0 {
		text := a.text.String()
		itemID := "msg_" + responseIDSuffix(a.publicID)
		if err := a.emit(onDelta, map[string]any{"type": "response.output_text.done", "response_id": a.publicID, "item_id": itemID, "output_index": a.textOutput, "content_index": 0, "text": text}); err != nil {
			return err
		}
		if err := a.emit(onDelta, map[string]any{
			"type": "response.content_part.done", "response_id": a.publicID, "item_id": itemID,
			"output_index": a.textOutput, "content_index": 0,
			"part": map[string]any{"type": "output_text", "text": text, "annotations": []any{}, "logprobs": []any{}},
		}); err != nil {
			return err
		}
		if err := a.emit(onDelta, map[string]any{"type": "response.output_item.done", "response_id": a.publicID, "output_index": a.textOutput, "item": firstResponseOutputItem(result, "message")}); err != nil {
			return err
		}
	}
	tools := make([]*chatResponseStreamTool, 0, len(a.tools))
	for _, tool := range a.tools {
		tools = append(tools, tool)
	}
	sort.Slice(tools, func(i, j int) bool { return tools[i].OutputIndex < tools[j].OutputIndex })
	for _, tool := range tools {
		arguments := tool.Arguments.String()
		if err := a.emit(onDelta, map[string]any{"type": "response.function_call_arguments.done", "response_id": a.publicID, "item_id": tool.ItemID, "output_index": tool.OutputIndex, "arguments": arguments}); err != nil {
			return err
		}
		if err := a.emit(onDelta, map[string]any{"type": "response.output_item.done", "response_id": a.publicID, "output_index": tool.OutputIndex, "item": responseOutputItemByID(result, tool.ItemID)}); err != nil {
			return err
		}
	}
	return nil
}

func (a *chatResponsesStreamAdapter) alignOutput(response map[string]any) {
	items, _ := response["output"].([]any)
	if len(items) == 0 || a.nextOutput == 0 {
		return
	}
	ordered := make([]any, a.nextOutput)
	if a.textOutput >= 0 {
		ordered[a.textOutput] = firstResponseOutputItem(response, "message")
	}
	for _, tool := range a.tools {
		ordered[tool.OutputIndex] = responseOutputItemByID(response, tool.ItemID)
	}
	result := make([]any, 0, len(ordered))
	for _, item := range ordered {
		if item != nil {
			result = append(result, item)
		}
	}
	response["output"] = result
}

func firstResponseOutputItem(response map[string]any, itemType string) any {
	items, _ := response["output"].([]any)
	for _, raw := range items {
		item, _ := raw.(map[string]any)
		if stringFromAny(item["type"]) == itemType {
			return item
		}
	}
	return nil
}

func responseOutputItemByID(response map[string]any, id string) any {
	items, _ := response["output"].([]any)
	for _, raw := range items {
		item, _ := raw.(map[string]any)
		if stringFromAny(item["id"]) == id {
			return item
		}
	}
	return nil
}

func responseIDSuffix(publicID string) string {
	return strings.TrimPrefix(publicID, "resp_")
}

func unsupportedResponseParameter(parameter string) error {
	return &ClientError{Code: "unsupported_response_parameter", Message: "Chat fallback does not support Responses parameter: " + parameter, StatusCode: http.StatusBadRequest}
}
