package openai

import (
	"encoding/json"
	"strings"
	"time"

	"nex/backend/internal/conversion"
	"nex/backend/internal/conversion/canonical"
)

// encodeRequest 将 Canonical 请求编码为 OpenAI 请求
func encodeRequest(req *canonical.CanonicalRequest, provider *conversion.TargetProvider) ([]byte, error) {
	result := map[string]any{
		"model":  provider.ModelName,
		"stream": req.Stream,
	}

	// 系统消息 + 消息
	messages := encodeSystemAndMessages(req)
	result["messages"] = messages

	// 参数
	encodeParametersInto(req, result)

	// 工具
	if len(req.Tools) > 0 {
		tools := make([]map[string]any, len(req.Tools))
		for i, t := range req.Tools {
			tools[i] = map[string]any{
				"type": "function",
				"function": map[string]any{
					"name":        t.Name,
					"description": t.Description,
					"parameters":  t.InputSchema,
				},
			}
		}
		result["tools"] = tools
	}
	if req.ToolChoice != nil {
		result["tool_choice"] = encodeToolChoice(req.ToolChoice)
	}

	// 公共字段
	if req.UserID != "" {
		result["user"] = req.UserID
	}
	if req.OutputFormat != nil {
		result["response_format"] = encodeOutputFormat(req.OutputFormat)
	}
	if req.ParallelToolUse != nil {
		result["parallel_tool_calls"] = *req.ParallelToolUse
	}
	if req.Thinking != nil {
		switch req.Thinking.Type {
		case "disabled":
			result["reasoning_effort"] = "none"
		default:
			if req.Thinking.Effort != "" {
				result["reasoning_effort"] = req.Thinking.Effort
			} else {
				result["reasoning_effort"] = "medium"
			}
		}
	}

	body, err := json.Marshal(result)
	if err != nil {
		return nil, conversion.NewConversionError(conversion.ErrorCodeEncodingFailure, "编码 OpenAI 请求失败").WithCause(err)
	}
	return body, nil
}

// encodeSystemAndMessages 编码系统消息和消息列表
func encodeSystemAndMessages(req *canonical.CanonicalRequest) []map[string]any {
	var messages []map[string]any

	// 系统消息
	switch v := req.System.(type) {
	case string:
		if v != "" {
			messages = append(messages, map[string]any{
				"role":    "system",
				"content": v,
			})
		}
	case []canonical.SystemBlock:
		var parts []string
		for _, b := range v {
			parts = append(parts, b.Text)
		}
		text := strings.Join(parts, "\n\n")
		if text != "" {
			messages = append(messages, map[string]any{
				"role":    "system",
				"content": text,
			})
		}
	}

	// 消息
	for _, msg := range req.Messages {
		encoded := encodeMessage(msg)
		messages = append(messages, encoded...)
	}

	// 合并连续同角色消息
	return mergeConsecutiveRoles(messages)
}

// encodeMessage 编码单条消息
func encodeMessage(msg canonical.CanonicalMessage) []map[string]any {
	switch msg.Role {
	case canonical.RoleUser:
		return []map[string]any{{
			"role":    "user",
			"content": encodeUserContent(msg.Content),
		}}
	case canonical.RoleAssistant:
		m := map[string]any{"role": "assistant"}
		var textParts []string
		var toolUses []canonical.ContentBlock

		for _, b := range msg.Content {
			switch b.Type {
			case "text":
				textParts = append(textParts, b.Text)
			case "tool_use":
				toolUses = append(toolUses, b)
			}
		}

		if len(toolUses) > 0 {
			if len(textParts) > 0 {
				m["content"] = strings.Join(textParts, "")
			} else {
				m["content"] = nil
			}
			tcs := make([]map[string]any, len(toolUses))
			for i, tu := range toolUses {
				tcs[i] = map[string]any{
					"id":   tu.ID,
					"type": "function",
					"function": map[string]any{
						"name":      tu.Name,
						"arguments": string(tu.Input),
					},
				}
			}
			m["tool_calls"] = tcs
		} else if len(textParts) > 0 {
			m["content"] = strings.Join(textParts, "")
		} else {
			m["content"] = ""
		}
		return []map[string]any{m}

	case canonical.RoleTool:
		for _, b := range msg.Content {
			if b.Type == "tool_result" {
				var contentStr string
				if b.Content != nil {
					var s string
					if json.Unmarshal(b.Content, &s) == nil {
						contentStr = s
					} else {
						contentStr = string(b.Content)
					}
				}
				return []map[string]any{{
					"role":         "tool",
					"tool_call_id": b.ToolUseID,
					"content":      contentStr,
				}}
			}
		}
	}
	return nil
}

// encodeUserContent 编码用户内容
func encodeUserContent(blocks []canonical.ContentBlock) any {
	if len(blocks) == 1 && blocks[0].Type == "text" {
		return blocks[0].Text
	}
	parts := make([]map[string]any, 0, len(blocks))
	for _, b := range blocks {
		switch b.Type {
		case "text":
			parts = append(parts, map[string]any{"type": "text", "text": b.Text})
		case "image":
			parts = append(parts, map[string]any{"type": "image_url"})
		case "audio":
			parts = append(parts, map[string]any{"type": "input_audio"})
		case "file":
			parts = append(parts, map[string]any{"type": "file"})
		}
	}
	if len(parts) == 0 {
		return ""
	}
	return parts
}

// encodeToolChoice 编码工具选择
func encodeToolChoice(choice *canonical.ToolChoice) any {
	switch choice.Type {
	case "auto":
		return "auto"
	case "none":
		return "none"
	case "any":
		return "required"
	case "tool":
		return map[string]any{
			"type": "function",
			"function": map[string]any{
				"name": choice.Name,
			},
		}
	}
	return "auto"
}

// encodeParametersInto 编码参数到结果 map
func encodeParametersInto(req *canonical.CanonicalRequest, result map[string]any) {
	if req.Parameters.MaxTokens != nil {
		result["max_completion_tokens"] = *req.Parameters.MaxTokens
	}
	if req.Parameters.Temperature != nil {
		result["temperature"] = *req.Parameters.Temperature
	}
	if req.Parameters.TopP != nil {
		result["top_p"] = *req.Parameters.TopP
	}
	if req.Parameters.FrequencyPenalty != nil {
		result["frequency_penalty"] = *req.Parameters.FrequencyPenalty
	}
	if req.Parameters.PresencePenalty != nil {
		result["presence_penalty"] = *req.Parameters.PresencePenalty
	}
	if len(req.Parameters.StopSequences) > 0 {
		result["stop"] = req.Parameters.StopSequences
	}
}

// encodeOutputFormat 编码输出格式
func encodeOutputFormat(format *canonical.OutputFormat) map[string]any {
	switch format.Type {
	case "json_object":
		return map[string]any{"type": "json_object"}
	case "json_schema":
		m := map[string]any{"type": "json_schema"}
		schema := map[string]any{
			"name": format.Name,
		}
		if format.Schema != nil {
			schema["schema"] = format.Schema
		}
		if format.Strict != nil {
			schema["strict"] = *format.Strict
		}
		m["json_schema"] = schema
		return m
	}
	return nil
}

// encodeResponse 将 Canonical 响应编码为 OpenAI 响应
func encodeResponse(resp *canonical.CanonicalResponse) ([]byte, error) {
	var textParts []string
	var thinkingParts []string
	var toolUses []canonical.ContentBlock

	for _, b := range resp.Content {
		switch b.Type {
		case "text":
			textParts = append(textParts, b.Text)
		case "thinking":
			thinkingParts = append(thinkingParts, b.Thinking)
		case "tool_use":
			toolUses = append(toolUses, b)
		}
	}

	message := map[string]any{"role": "assistant"}
	if len(toolUses) > 0 {
		if len(textParts) > 0 {
			message["content"] = strings.Join(textParts, "")
		} else {
			message["content"] = nil
		}
		tcs := make([]map[string]any, len(toolUses))
		for i, tu := range toolUses {
			tcs[i] = map[string]any{
				"id":   tu.ID,
				"type": "function",
				"function": map[string]any{
					"name":      tu.Name,
					"arguments": string(tu.Input),
				},
			}
		}
		message["tool_calls"] = tcs
	} else if len(textParts) > 0 {
		message["content"] = strings.Join(textParts, "")
	} else {
		message["content"] = ""
	}

	if len(thinkingParts) > 0 {
		message["reasoning_content"] = strings.Join(thinkingParts, "")
	}

	var finishReason *string
	if resp.StopReason != nil {
		fr := mapCanonicalToFinishReason(*resp.StopReason)
		finishReason = &fr
	}

	result := map[string]any{
		"id":      resp.ID,
		"object":  "chat.completion",
		"created": time.Now().Unix(),
		"model":   resp.Model,
		"choices": []map[string]any{{
			"index":         0,
			"message":       message,
			"finish_reason": finishReason,
		}},
		"usage": encodeUsage(resp.Usage),
	}

	body, err := json.Marshal(result)
	if err != nil {
		return nil, conversion.NewConversionError(conversion.ErrorCodeEncodingFailure, "编码 OpenAI 响应失败").WithCause(err)
	}
	return body, nil
}

// mapCanonicalToFinishReason 映射 Canonical 停止原因到 OpenAI finish_reason
func mapCanonicalToFinishReason(reason canonical.StopReason) string {
	switch reason {
	case canonical.StopReasonEndTurn:
		return "stop"
	case canonical.StopReasonMaxTokens:
		return "length"
	case canonical.StopReasonToolUse:
		return "tool_calls"
	case canonical.StopReasonContentFilter:
		return "content_filter"
	case canonical.StopReasonStopSequence:
		return "stop"
	case canonical.StopReasonRefusal:
		return "stop"
	default:
		return "stop"
	}
}

// encodeUsage 编码用量
func encodeUsage(usage canonical.CanonicalUsage) map[string]any {
	result := map[string]any{
		"prompt_tokens":     usage.InputTokens,
		"completion_tokens": usage.OutputTokens,
		"total_tokens":      usage.InputTokens + usage.OutputTokens,
	}
	if usage.CacheReadTokens != nil && *usage.CacheReadTokens > 0 {
		result["prompt_tokens_details"] = map[string]any{
			"cached_tokens": *usage.CacheReadTokens,
		}
	}
	if usage.ReasoningTokens != nil && *usage.ReasoningTokens > 0 {
		result["completion_tokens_details"] = map[string]any{
			"reasoning_tokens": *usage.ReasoningTokens,
		}
	}
	return result
}

// encodeModelsResponse 编码模型列表响应
func encodeModelsResponse(list *canonical.CanonicalModelList) ([]byte, error) {
	data := make([]map[string]any, len(list.Models))
	for i, m := range list.Models {
		created := int64(0)
		if m.Created != 0 {
			created = m.Created
		}
		ownedBy := "unknown"
		if m.OwnedBy != "" {
			ownedBy = m.OwnedBy
		}
		data[i] = map[string]any{
			"id":       m.ID,
			"object":   "model",
			"created":  created,
			"owned_by": ownedBy,
		}
	}
	return json.Marshal(map[string]any{
		"object": "list",
		"data":   data,
	})
}

// encodeModelInfoResponse 编码模型详情响应
func encodeModelInfoResponse(info *canonical.CanonicalModelInfo) ([]byte, error) {
	created := int64(0)
	if info.Created != 0 {
		created = info.Created
	}
	ownedBy := "unknown"
	if info.OwnedBy != "" {
		ownedBy = info.OwnedBy
	}
	return json.Marshal(map[string]any{
		"id":       info.ID,
		"object":   "model",
		"created":  created,
		"owned_by": ownedBy,
	})
}

// encodeEmbeddingRequest 编码嵌入请求
func encodeEmbeddingRequest(req *canonical.CanonicalEmbeddingRequest, provider *conversion.TargetProvider) ([]byte, error) {
	result := map[string]any{
		"model": provider.ModelName,
		"input": req.Input,
	}
	if req.EncodingFormat != "" {
		result["encoding_format"] = req.EncodingFormat
	}
	if req.Dimensions != nil {
		result["dimensions"] = *req.Dimensions
	}
	return json.Marshal(result)
}

// encodeEmbeddingResponse 编码嵌入响应
func encodeEmbeddingResponse(resp *canonical.CanonicalEmbeddingResponse) ([]byte, error) {
	data := make([]map[string]any, len(resp.Data))
	for i, d := range resp.Data {
		data[i] = map[string]any{
			"index":     d.Index,
			"embedding": d.Embedding,
		}
	}
	return json.Marshal(map[string]any{
		"object": "list",
		"data":   data,
		"model":  resp.Model,
		"usage":  resp.Usage,
	})
}

// encodeRerankRequest 编码重排序请求
func encodeRerankRequest(req *canonical.CanonicalRerankRequest, provider *conversion.TargetProvider) ([]byte, error) {
	result := map[string]any{
		"model":     provider.ModelName,
		"query":     req.Query,
		"documents": req.Documents,
	}
	if req.TopN != nil {
		result["top_n"] = *req.TopN
	}
	if req.ReturnDocuments != nil {
		result["return_documents"] = *req.ReturnDocuments
	}
	return json.Marshal(result)
}

// encodeRerankResponse 编码重排序响应
func encodeRerankResponse(resp *canonical.CanonicalRerankResponse) ([]byte, error) {
	results := make([]map[string]any, len(resp.Results))
	for i, r := range resp.Results {
		m := map[string]any{
			"index":           r.Index,
			"relevance_score": r.RelevanceScore,
		}
		if r.Document != nil {
			m["document"] = *r.Document
		}
		results[i] = m
	}
	return json.Marshal(map[string]any{
		"results": results,
		"model":   resp.Model,
	})
}

// mergeConsecutiveRoles 合并连续同角色消息（拼接内容）
func mergeConsecutiveRoles(messages []map[string]any) []map[string]any {
	if len(messages) <= 1 {
		return messages
	}
	var result []map[string]any
	for _, msg := range messages {
		if len(result) > 0 {
			lastRole := result[len(result)-1]["role"]
			currRole := msg["role"]
			if lastRole == currRole {
				lastContent := result[len(result)-1]["content"]
				currContent := msg["content"]
				switch lv := lastContent.(type) {
				case string:
					if cv, ok := currContent.(string); ok {
						result[len(result)-1]["content"] = lv + cv
					}
				case []any:
					if cv, ok := currContent.([]any); ok {
						result[len(result)-1]["content"] = append(lv, cv...)
					}
				}
				continue
			}
		}
		result = append(result, msg)
	}
	return result
}
