package service

import (
	"encoding/json"
	"fmt"
	"strings"

	"oci-portal/internal/aiwire"
)

// OpenAI Chat Completions ↔ OCI OpenAI 兼容面(/actions/v1/responses)直通转换。
// 端点定位 Tier 2 兼容层(承接只会说 CC 的存量客户端),机制与 anthresponses.go
// 同构。语义损失(README 披露):stop / seed / n / penalty 等无对应字段,忽略;
// 上游 reasoning 输出项与增量事件丢弃;非 function 工具类型拒绝。

// ChatToResponsesBody 把 Chat Completions 请求转为直通 body(强制 store:false)。
func ChatToResponsesBody(req aiwire.ChatRequest) ([]byte, error) {
	input, instructions, err := chatInputItems(req.Messages)
	if err != nil {
		return nil, err
	}
	body := map[string]any{"model": req.Model, "input": input, "store": false}
	if instructions != "" {
		body["instructions"] = instructions
	}
	chatSampling(body, req)
	if err := chatBodyTools(body, req); err != nil {
		return nil, err
	}
	if tf := chatTextFormat(req.ResponseFormat); tf != nil {
		body["text"] = map[string]any{"format": tf}
	}
	if req.ReasoningEffort != "" {
		body["reasoning"] = map[string]string{"effort": strings.ToLower(req.ReasoningEffort)}
	}
	if req.Stream {
		body["stream"] = true
	}
	return json.Marshal(body)
}

// chatSampling 装配采样与输出预算;max_completion_tokens 优先于已弃用的 max_tokens。
func chatSampling(body map[string]any, req aiwire.ChatRequest) {
	if req.MaxCompletionTokens != nil {
		body["max_output_tokens"] = *req.MaxCompletionTokens
	} else if req.MaxTokens != nil {
		body["max_output_tokens"] = *req.MaxTokens
	}
	if req.Temperature != nil {
		body["temperature"] = *req.Temperature
	}
	if req.TopP != nil {
		body["top_p"] = *req.TopP
	}
	if req.ParallelToolCalls != nil {
		body["parallel_tool_calls"] = *req.ParallelToolCalls
	}
}

// chatBodyTools 装配工具与 tool_choice;非 function 工具类型拒绝
// (web_search 等新能力仅在 Responses / Messages 端点供给)。
func chatBodyTools(body map[string]any, req aiwire.ChatRequest) error {
	if len(req.Tools) == 0 {
		return nil
	}
	tools := make([]map[string]any, 0, len(req.Tools))
	for _, t := range req.Tools {
		if t.Type != "function" {
			return fmt.Errorf("不支持的工具类型 %q:该端点仅支持 function 工具", t.Type)
		}
		tools = append(tools, map[string]any{"type": "function", "name": t.Function.Name,
			"description": t.Function.Description, "parameters": t.Function.Parameters})
	}
	body["tools"] = tools
	if tc := chatRespToolChoice(req.ToolChoice); tc != nil {
		body["tool_choice"] = tc
	}
	return nil
}

// chatInputItems 把消息序列展开为 Responses input 项与 instructions:
// system/developer 文本聚为 instructions;tool 消息为 function_call_output 项;
// 其余消息经 chatMessageItems 展开,保持相对顺序。
func chatInputItems(messages []aiwire.ChatMessage) ([]any, string, error) {
	items := make([]any, 0, len(messages))
	var sys []string
	for _, m := range messages {
		switch m.Role {
		case "system", "developer":
			if txt := m.Content.JoinText(); txt != "" {
				sys = append(sys, txt)
			}
		case "tool":
			items = append(items, map[string]any{"type": "function_call_output",
				"call_id": m.ToolCallID, "output": m.Content.JoinText()})
		default:
			msgItems, err := chatMessageItems(m)
			if err != nil {
				return nil, "", err
			}
			items = append(items, msgItems...)
		}
	}
	return items, strings.Join(sys, "\n\n"), nil
}

// chatMessageItems 把 user/assistant 消息转为 message 项;assistant 的
// tool_calls 追加为独立 function_call 项(内容在前,与原语序一致)。
func chatMessageItems(m aiwire.ChatMessage) ([]any, error) {
	parts, err := chatContentParts(m.Role, m.Content)
	if err != nil {
		return nil, err
	}
	items := make([]any, 0, 1+len(m.ToolCalls))
	if len(parts) > 0 {
		items = append(items, map[string]any{"role": m.Role, "content": parts})
	}
	for _, tc := range m.ToolCalls {
		items = append(items, map[string]any{"type": "function_call", "call_id": tc.ID,
			"name": tc.Function.Name, "arguments": tc.Function.Arguments})
	}
	return items, nil
}

// chatContentParts 把消息内容转为 Responses 部件(文本按角色定型,图片转 input_image)。
func chatContentParts(role string, c aiwire.Content) ([]map[string]any, error) {
	if !c.IsArray {
		if c.Text == "" {
			return nil, nil
		}
		return []map[string]any{respTextPart(role, c.Text)}, nil
	}
	parts := make([]map[string]any, 0, len(c.Parts))
	for _, p := range c.Parts {
		switch p.Type {
		case "", "text":
			parts = append(parts, respTextPart(role, p.Text))
		case "image_url":
			if p.ImageURL == nil || p.ImageURL.URL == "" {
				return nil, fmt.Errorf("image_url 块缺少 url")
			}
			// detail 不透传:兼容面对该字段的支持无法证实(实测期间上游视觉请求不稳,
			// 无从归因),它仅是质量提示,忽略合法且消除一个风险轴(README 披露)
			parts = append(parts, map[string]any{"type": "input_image", "image_url": p.ImageURL.URL})
		default:
			return nil, ErrAiUnsupportedBlock
		}
	}
	return parts, nil
}

// chatRespToolChoice 映射 tool_choice:string 形态(auto/none/required)原样;
// {"type":"function","function":{"name":N}} 拍平为 Responses 具名形态;其余忽略。
func chatRespToolChoice(raw json.RawMessage) any {
	if len(raw) == 0 {
		return nil
	}
	var s string
	if json.Unmarshal(raw, &s) == nil {
		switch s {
		case "auto", "none", "required":
			return s
		}
		return nil
	}
	var tc struct {
		Type     string `json:"type"`
		Function struct {
			Name string `json:"name"`
		} `json:"function"`
	}
	if json.Unmarshal(raw, &tc) != nil || tc.Type != "function" || tc.Function.Name == "" {
		return nil
	}
	return map[string]string{"type": "function", "name": tc.Function.Name}
}

// chatTextFormat 把 response_format 拍平为 text.format(json_schema 提升嵌套字段)。
func chatTextFormat(rf *aiwire.ResponseFormat) map[string]any {
	if rf == nil || rf.Type == "" || rf.Type == "text" {
		return nil
	}
	format := map[string]any{"type": rf.Type}
	if rf.Type != "json_schema" || len(rf.JSONSchema) == 0 {
		return format
	}
	var js struct {
		Name   string          `json:"name"`
		Schema json.RawMessage `json:"schema"`
		Strict *bool           `json:"strict"`
	}
	if json.Unmarshal(rf.JSONSchema, &js) != nil {
		return format
	}
	if js.Name != "" {
		format["name"] = js.Name
	}
	if len(js.Schema) > 0 {
		format["schema"] = js.Schema
	}
	if js.Strict != nil {
		format["strict"] = *js.Strict
	}
	return format
}

// ResponsesToChat 把直通非流式响应转为 Chat Completions 响应(reasoning 项丢弃)。
func ResponsesToChat(payload []byte, id string, created int64) (*aiwire.ChatResponse, error) {
	var resp respPayload
	if err := json.Unmarshal(payload, &resp); err != nil {
		return nil, fmt.Errorf("解析上游响应: %w", err)
	}
	msg := aiwire.ChatMessage{Role: "assistant"}
	var text strings.Builder
	for _, item := range resp.Output {
		switch item.Type {
		case "message":
			for _, part := range item.Content {
				if part.Type == "output_text" {
					text.WriteString(part.Text)
				}
			}
		case "function_call":
			msg.ToolCalls = append(msg.ToolCalls, aiwire.ToolCall{ID: item.CallID, Type: "function",
				Function: aiwire.FunctionCall{Name: item.Name, Arguments: item.Arguments}})
		}
	}
	msg.Content = aiwire.NewTextContent(text.String())
	choice := aiwire.Choice{Index: 0, Message: msg,
		FinishReason: chatFinishReason(&resp, len(msg.ToolCalls) > 0)}
	return &aiwire.ChatResponse{ID: id, Object: "chat.completion", Created: created,
		Model: resp.Model, Choices: []aiwire.Choice{choice}, Usage: usageFromResp(resp.Usage)}, nil
}

// chatFinishReason 映射终态:截断优先 → length;含工具调用 → tool_calls;默认 stop。
func chatFinishReason(resp *respPayload, hasTools bool) string {
	if respTruncated(resp) {
		return "length"
	}
	if hasTools {
		return "tool_calls"
	}
	return "stop"
}

// respTruncated 报告直通响应是否因 max_output_tokens 截断。
func respTruncated(resp *respPayload) bool {
	return resp != nil && resp.Status == "incomplete" && resp.IncompleteDetails != nil &&
		resp.IncompleteDetails.Reason == "max_output_tokens"
}

// ---- Chat Completions 流式桥:直通 SSE 事件 → chat.completion.chunk 序列 ----

// ChatRespBridge 把直通 SSE 事件流桥接为 chunk 序列:首个增量补 role,文本与
// 工具增量按 OpenAI 形态输出;reasoning 系列事件丢弃;[DONE] 由传输层写出。
type ChatRespBridge struct {
	id, model    string
	created      int64
	includeUsage bool
	started      bool
	toolIndex    int
	inTool       bool
	finish       string
	usage        *aiwire.Usage
}

// NewChatRespBridge 构造桥;includeUsage 即 stream_options.include_usage。
func NewChatRespBridge(id, model string, created int64, includeUsage bool) *ChatRespBridge {
	return &ChatRespBridge{id: id, model: model, created: created,
		includeUsage: includeUsage, toolIndex: -1, finish: "stop"}
}

// Feed 消费一行 SSE data JSON,返回应立即写出的 chunk。
func (b *ChatRespBridge) Feed(data []byte) []aiwire.ChatChunk {
	var ev respStreamEvent
	if json.Unmarshal(data, &ev) != nil {
		return nil
	}
	switch ev.Type {
	case "response.output_text.delta":
		b.inTool = false
		return []aiwire.ChatChunk{b.chunk(b.deltaWithRole(aiwire.Delta{Content: ev.Delta}), nil)}
	case "response.output_item.added":
		return b.toolOpen(ev.Item)
	case "response.function_call_arguments.delta":
		return b.argsChunk(ev.Delta)
	case "response.completed", "response.incomplete", "response.failed":
		b.finishFrom(ev.Response)
	}
	return nil
}

// chunk 装配一条单 choice 增量事件。
func (b *ChatRespBridge) chunk(delta aiwire.Delta, finish *string) aiwire.ChatChunk {
	return aiwire.ChatChunk{ID: b.id, Object: "chat.completion.chunk", Created: b.created,
		Model: b.model, Choices: []aiwire.ChunkChoice{{Index: 0, Delta: delta, FinishReason: finish}}}
}

// deltaWithRole 给首个增量补 role(OpenAI 首块携带 role 语义)。
func (b *ChatRespBridge) deltaWithRole(d aiwire.Delta) aiwire.Delta {
	if !b.started {
		b.started = true
		d.Role = "assistant"
	}
	return d
}

// toolOpen 在新 function_call 输出项开始时发 id/name 增量并推进聚合 index;
// 非工具输出项只复位增量目标。
func (b *ChatRespBridge) toolOpen(item *respOutputItem) []aiwire.ChatChunk {
	if item == nil || item.Type != "function_call" {
		b.inTool = false
		return nil
	}
	b.toolIndex++
	b.inTool = true
	b.finish = "tool_calls"
	d := aiwire.Delta{ToolCalls: []aiwire.ToolCallDelta{{Index: b.toolIndex, ID: item.CallID,
		Type: "function", Function: aiwire.FunctionCallDelta{Name: item.Name}}}}
	return []aiwire.ChatChunk{b.chunk(b.deltaWithRole(d), nil)}
}

// argsChunk 输出当前工具的实参增量(仅在工具输出项进行中)。
func (b *ChatRespBridge) argsChunk(delta string) []aiwire.ChatChunk {
	if !b.inTool {
		return nil
	}
	d := aiwire.Delta{ToolCalls: []aiwire.ToolCallDelta{{Index: b.toolIndex,
		Function: aiwire.FunctionCallDelta{Arguments: delta}}}}
	return []aiwire.ChatChunk{b.chunk(d, nil)}
}

// finishFrom 记录终态 usage 与截断语义(max_output_tokens → length)。
func (b *ChatRespBridge) finishFrom(resp *respPayload) {
	if resp == nil {
		return
	}
	b.usage = usageFromResp(resp.Usage)
	if respTruncated(resp) {
		b.finish = "length"
	}
}

// Finish 在上游流结束后收尾:终块带 finish_reason;include_usage 时追加 usage 块
// (choices 为空数组,OpenAI 规范形态;上游未给 usage 时输出零值)。
func (b *ChatRespBridge) Finish() []aiwire.ChatChunk {
	chunks := []aiwire.ChatChunk{b.chunk(b.deltaWithRole(aiwire.Delta{}), &b.finish)}
	if !b.includeUsage {
		return chunks
	}
	usage := b.usage
	if usage == nil {
		usage = &aiwire.Usage{}
	}
	return append(chunks, aiwire.ChatChunk{ID: b.id, Object: "chat.completion.chunk",
		Created: b.created, Model: b.model, Choices: []aiwire.ChunkChoice{}, Usage: usage})
}

// Usage 返回聚合到的用量(供调用日志),上游未报告时为 nil。
func (b *ChatRespBridge) Usage() *aiwire.Usage { return b.usage }

// ChatResponseChunks 把完整 Chat 响应展开为 chunk 序列(内容与工具调用 →
// 终块 → 可选 usage 块),供流式上游断流后的非流式降级结果推送。
func ChatResponseChunks(resp *aiwire.ChatResponse, includeUsage bool) []aiwire.ChatChunk {
	if len(resp.Choices) == 0 {
		return nil
	}
	choice := resp.Choices[0]
	mk := func(delta aiwire.Delta, finish *string) aiwire.ChatChunk {
		return aiwire.ChatChunk{ID: resp.ID, Object: "chat.completion.chunk", Created: resp.Created,
			Model: resp.Model, Choices: []aiwire.ChunkChoice{{Index: 0, Delta: delta, FinishReason: finish}}}
	}
	delta := aiwire.Delta{Role: "assistant", Content: choice.Message.Content.Text}
	for i, tc := range choice.Message.ToolCalls {
		delta.ToolCalls = append(delta.ToolCalls, aiwire.ToolCallDelta{Index: i, ID: tc.ID,
			Type: "function", Function: aiwire.FunctionCallDelta{Name: tc.Function.Name, Arguments: tc.Function.Arguments}})
	}
	finish := choice.FinishReason
	chunks := []aiwire.ChatChunk{mk(delta, nil), mk(aiwire.Delta{}, &finish)}
	if includeUsage {
		usage := resp.Usage
		if usage == nil {
			usage = &aiwire.Usage{}
		}
		chunks = append(chunks, aiwire.ChatChunk{ID: resp.ID, Object: "chat.completion.chunk",
			Created: resp.Created, Model: resp.Model, Choices: []aiwire.ChunkChoice{}, Usage: usage})
	}
	return chunks
}
