package orchestrator

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

	"git.yeij.top/AskaEth/Cyrene/ai-core/internal/llm"
	"git.yeij.top/AskaEth/Cyrene/ai-core/internal/model"
	"git.yeij.top/AskaEth/Cyrene/pkg/logger"
	plgManager "git.yeij.top/AskaEth/Cyrene-Plugins/manager"
	plgSDK "git.yeij.top/AskaEth/Cyrene-Plugins/sdk"
)

// Synthesizer 主会话综合器
// 汇总子会话结果，生成最终回复
type Synthesizer struct {
	llmAdapter   *llm.Adapter
	toolRegistry *plgManager.ToolRegistry
	resultPusher func(sessionID, userID, toolName, result string, params SynthesizeParams)
	traceFn      func(hop, sessionID, userID, label, status, detail string, durationMs int64)
}

// NewSynthesizer 创建综合器
func NewSynthesizer(llmAdapter *llm.Adapter, toolRegistry *plgManager.ToolRegistry) *Synthesizer {
	return &Synthesizer{
		llmAdapter:   llmAdapter,
		toolRegistry: toolRegistry,
	}
}

// SetResultPusher sets the callback for proactive tool result delivery.
func (s *Synthesizer) SetResultPusher(pusher func(sessionID, userID, toolName, result string, params SynthesizeParams)) {
	s.resultPusher = pusher
}

// SetTraceFunc sets the trace callback.
func (s *Synthesizer) SetTraceFunc(fn func(hop, sessionID, userID, label, status, detail string, durationMs int64)) {
	s.traceFn = fn
}

// SynthesizeParams 综合参数
type SynthesizeParams struct {
	UserID             string
	SessionID          string
	UserMessage        string
	Images             []string                    // 图片 base64 data URL (多模态)
	VideoURLs          []string                    // 视频 URL (多模态)
	Nickname           string
	PersonaPrompt      string                      // 完整人格提示词
	DialogHistory      []model.LLMMessage           // 对话历史
	MemorySummary      string                      // 记忆检索摘要
	ThoughtOutline     string                      // 通用对话思考
	IoTSummary         string                      // IoT 操作摘要
	DeviceContext      string                      // 设备状态上下文
	KnowledgeInfo      string                      // 知识库检索摘要
	PendingToolResults []PendingToolResult         // 上一轮异步完成的工具结果
	Mode               string                      // text / voice_assistant
	ChannelType        string                      // direct / group
	ChannelID          string                      // platform channel ID
	AdapterName        string                      // adapter config name
}

// Synthesize 综合所有子会话结果，流式生成最终回复。
// eventCh receives tool progress events; pass nil to suppress.
func (s *Synthesizer) Synthesize(ctx context.Context, params SynthesizeParams, eventCh chan<- model.StreamEvent) (<-chan llm.StreamChunk, error) {
	messages := s.buildSynthesizeMessages(params)

	logger.Printf("[synthesizer] 开始综合 (上下文 %d 条消息)", len(messages))

	openAITools := s.buildOpenAITools()
	if len(openAITools) == 0 {
		return s.llmAdapter.ChatStream(ctx, messages)
	}

	resp, err := s.llmAdapter.ChatWithTools(ctx, messages, openAITools)
	if err != nil {
		logger.Printf("[synthesizer] ChatWithTools 失败: %v", err)
		return nil, err
	}

	const toolDeadline = 8 * time.Second
	const maxRounds = 5

	for round := 0; len(resp.ToolCalls) > 0 && round < maxRounds; round++ {
		logger.Printf("[synthesizer] LLM 请求 %d 个工具调用 (round=%d)", len(resp.ToolCalls), round)
		for _, tc := range resp.ToolCalls {
			if s.traceFn != nil {
				s.traceFn("tool_call", params.SessionID, params.UserID, "🔧 "+tc.Name, "running", "", 0)
			}
		}

		messages = append(messages, model.LLMMessage{
			Role:             model.RoleAssistant,
			Content:          resp.Content,
			ToolCalls:        resp.ToolCalls,
			ReasoningContent: resp.ReasoningContent,
		})

		for _, tc := range resp.ToolCalls {
			var args map[string]interface{}
			if err := json.Unmarshal([]byte(tc.Arguments), &args); err != nil {
				logger.Printf("[synthesizer] 工具 %s 参数解析失败: %v", tc.Name, err)
				args = make(map[string]interface{})
			}

			// 自动注入当前会话的平台信息（reminder_create 需要知道来源）
			if tc.Name == "reminder_create" && params.ChannelType != "" {
				if _, ok := args["platform"]; !ok || args["platform"] == "" {
					args["platform"] = params.AdapterName
				}
				if _, ok := args["channel_type"]; !ok || args["channel_type"] == "" {
					args["channel_type"] = params.ChannelType
				}
				if _, ok := args["channel_id"]; !ok || args["channel_id"] == "" {
					// Strip platform-internal prefix (private_xxx -> xxx)
					chID := params.ChannelID
					if strings.HasPrefix(chID, "private_") {
						chID = chID[len("private_"):]
					}
					args["channel_id"] = chID
				}
				// adapter_name will be resolved when the reminder fires
			}

			s.emitToolProgress(eventCh, tc.Name, "started", 0, "正在执行 "+tc.Name)

			// 所有工具异步执行，不阻塞前台会话
			go s.executeAsyncAndStore(tc, args, params, eventCh)
			result := &plgSDK.ToolResult{
				ToolName: tc.Name,
				Success:  true,
				Output:   fmt.Sprintf(`[后台执行中] %s 已提交后台执行。不要猜测或编造结果，告知用户你正在查询中即可。真实结果稍后会发送给你。`, tc.Name),
			}
			resultJSON, _ := json.Marshal(result)
			messages = append(messages, model.LLMMessage{
				Role:       model.RoleTool,
				Content:    string(resultJSON),
				ToolCallID: tc.ID,
			})
		}

		resp, err = s.llmAdapter.ChatWithTools(ctx, messages, openAITools)
		if err != nil {
			logger.Printf("[synthesizer] ChatWithTools 失败 (round=%d): %v", round+1, err)
			return nil, err
		}
	}

	finalContent := resp.Content
	ch := make(chan llm.StreamChunk, 200)
	go func() {
		defer close(ch)
		runes := []rune(finalContent)
		for i := 0; i < len(runes); i += 3 {
			end := i + 3
			if end > len(runes) {
				end = len(runes)
			}
			ch <- llm.StreamChunk{Content: string(runes[i:end])}
		}
		ch <- llm.StreamChunk{Done: true}
	}()
	return ch, nil
}

// emitToolProgress sends a StreamToolProgress event if eventCh is available.
func (s *Synthesizer) emitToolProgress(eventCh chan<- model.StreamEvent, name, status string, progress float64, message string) {
	if eventCh == nil {
		return
	}
	defer func() { recover() }() // channel may be closed by caller
	select {
	case eventCh <- model.StreamEvent{
		Type: model.StreamToolProgress,
		ToolProgress: &model.ToolProgressInfo{
			ToolName: name,
			Status:   status,
			Progress: progress,
			Message:  message,
		},
	}:
	default:
	}
}

// executeAsyncAndStore runs a tool in background and stores the result for the next turn.
func (s *Synthesizer) executeAsyncAndStore(tc model.ToolCall, args map[string]interface{}, params SynthesizeParams, eventCh chan<- model.StreamEvent) {
	ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
	defer cancel()

	result, err := s.toolRegistry.Execute(ctx, tc.Name, args)
	if err != nil {
		logger.Printf("[synthesizer] 后台工具 %s 执行失败: %v", tc.Name, err)
		s.emitToolProgress(eventCh, tc.Name, "failed", 1.0, tc.Name+" 后台执行失败: "+err.Error())
		return
	}

	s.emitToolProgress(eventCh, tc.Name, "completed", 1.0, tc.Name+" 后台执行完成")
	if s.traceFn != nil {
		status := "success"
		if result == nil || !result.Success {
			status = "error"
		}
		s.traceFn("tool_call", params.SessionID, params.UserID, "🔧 "+tc.Name, status, result.Output, time.Since(time.Now()).Milliseconds())
	}

	resultJSON, _ := json.Marshal(result)
	store := GetGlobalPendingToolStore()
	if store != nil {
		store.AppendToolResult(params.SessionID, PendingToolResult{
			ToolCallID: tc.ID,
			ToolName:   tc.Name,
			Result:     string(resultJSON),
			Success:    result != nil && result.Success,
		})
	}
	// 触发工具跟进回调 — 由 main.go 驱动 LLM 生成回复并推送到原渠道
	if s.resultPusher != nil && result != nil && result.Success {
		s.resultPusher(params.SessionID, params.UserID, tc.Name, string(resultJSON), params)
	}
}

// buildSynthesizeMessages 构建综合用的 LLM 消息列表
func (s *Synthesizer) buildSynthesizeMessages(params SynthesizeParams) []model.LLMMessage {
	var messages []model.LLMMessage

	userName := params.Nickname
	if userName == "" {
		userName = params.UserID
	}

	// 构建综合系统提示词
	systemPrompt := params.PersonaPrompt

	// 注入设备上下文
	if params.DeviceContext != "" {
		systemPrompt += "\n\n" + params.DeviceContext
	}

	messages = append(messages, model.LLMMessage{
		Role:    model.RoleSystem,
		Content: systemPrompt,
	})

	// 群聊上下文：当消息来自群聊时，告知模型这是一条群聊消息而非一对一私聊。
	if params.ChannelType == "group" {
		messages = append(messages, model.LLMMessage{
			Role:    model.RoleSystem,
			Content: "【群聊上下文】这条消息来自OBv11群聊。消息前缀 [群聊 群号] 昵称 (OBv11账号) 标注了真实发送者。你不是在和开拓者一对一私聊，而是在群聊中和不同成员交流。请根据当前这条消息前缀中的发送者名字来称呼对方——即使你之前在历史对话中称呼过别人，也不要把之前用的称呼套在当前发送者身上。不同的人有不同的名字。只在对你说话或延续已有对话时才回复。",
		})
	}

	// 注入记忆摘要// 注入记忆摘要
	if params.MemorySummary != "" && !strings.Contains(params.MemorySummary, "没有找到") {
		messages = append(messages, model.LLMMessage{
			Role:    model.RoleSystem,
			Content: fmt.Sprintf("【你回忆起的关于%s的事】\n%s", userName, params.MemorySummary),
		})
	}

	// 注入通用对话思考
	if params.ThoughtOutline != "" && params.ThoughtOutline != "思考完成，等待主会话综合" {
		messages = append(messages, model.LLMMessage{
			Role:    model.RoleSystem,
			Content: fmt.Sprintf("【你对%s这句话的理解】\n%s", userName, params.ThoughtOutline),
		})
	}

	// 注入 IoT 操作摘要
	if params.IoTSummary != "" && !strings.Contains(params.IoTSummary, "未匹配") && !strings.Contains(params.IoTSummary, "未执行") {
		messages = append(messages, model.LLMMessage{
			Role:    model.RoleSystem,
			Content: fmt.Sprintf("【IoT 设备操作结果】\n%s", params.IoTSummary),
		})
	}

	// 注入知识库检索结果
	if params.KnowledgeInfo != "" && !strings.Contains(params.KnowledgeInfo, "未找到") {
		messages = append(messages, model.LLMMessage{
			Role:    model.RoleSystem,
			Content: fmt.Sprintf("【知识库参考资料 - 必须严格基于以下内容回答，不得编造、不得虚构、不得猜测。如果资料中没有直接答案，使用 web_search 工具搜索后再回答，不要自己编。】\n%s", params.KnowledgeInfo),
		})
	}

	// 注入上一轮异步工具执行结果
	if len(params.PendingToolResults) > 0 {
		var sb strings.Builder
		sb.WriteString("【上一轮后台工具执行结果】\n")
		for _, ptr := range params.PendingToolResults {
			status := "成功"
			if !ptr.Success {
				status = "失败"
			}
			sb.WriteString(fmt.Sprintf("- %s (%s): %s\n", ptr.ToolName, status, ptr.Result))
		}
		messages = append(messages, model.LLMMessage{
			Role:    model.RoleSystem,
			Content: sb.String(),
		})
	}

	// 注入对话历史（去掉末尾的当前用户消息，因为后面会单独追加）
	history := params.DialogHistory
	if len(history) > 0 {
		last := history[len(history)-1]
		if last.Role == model.RoleUser && last.Content == params.UserMessage {
			history = history[:len(history)-1]
		}
	}
	if len(history) > 0 {
		messages = append(messages, history...)
	}

	// 当前用户消息 (支持多模态图片和视频)
	messages = append(messages, model.LLMMessage{
		Role:      model.RoleUser,
		Content:   params.UserMessage,
		Images:    params.Images,
		VideoURLs: params.VideoURLs,
	})

	return messages
}

// buildOpenAITools 将工具注册中心的定义转换为 LLM 工具格式
func (s *Synthesizer) buildOpenAITools() []llm.OpenAITool {
	if s.toolRegistry == nil || !s.toolRegistry.IsEnabled() {
		return nil
	}
	defs := s.toolRegistry.Definitions()
	if len(defs) == 0 {
		return nil
	}
	result := make([]llm.OpenAITool, 0, len(defs))
	for _, d := range defs {
		result = append(result, llm.OpenAITool{
			Type: "function",
			Function: llm.OpenAIToolFunc{
				Name:        d.Name,
				Description: d.Description,
				Parameters:  d.Parameters,
			},
		})
	}
	return result
}

// AggregateResults 汇总子会话结果
func AggregateResults(results []model.SubSessionResult) *AggregatedContext {
	agg := &AggregatedContext{
		MemorySummary:  "",
		ThoughtOutline: "",
		IoTSummary:     "",
	}

	for _, r := range results {
		if r.Error != "" {
			logger.Printf("[aggregate] 子会话 %s 出错: %s", r.Type, r.Error)
			continue
		}

		switch r.Type {
		case model.SubSessionMemory:
			agg.MemorySummary = r.Summary
			if r.Details != "" {
				agg.MemorySummary += "\n" + r.Details
			}
			agg.MemorySnippets = r.Memories

		case model.SubSessionGeneral:
			agg.ThoughtOutline = r.Summary
			if r.Details != "" {
				agg.ThoughtOutline += "\n" + r.Details
			}

		case model.SubSessionIoT:
			agg.IoTSummary = r.Summary

		case model.SubSessionKnowledge:
			agg.KnowledgeInfo = r.Summary
		}
	}

	return agg
}

// AggregatedContext 汇总后的上下文
type AggregatedContext struct {
	MemorySummary   string                 `json:"memory_summary"`
	ThoughtOutline  string                 `json:"thought_outline"`
	IoTSummary      string                 `json:"iot_summary"`
	KnowledgeInfo   string                 `json:"knowledge_info"`
	MemorySnippets  []model.MemorySnippet  `json:"memory_snippets"`
}
