//go:generate go run gen_plugins.go

package main

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"runtime"
	"os/signal"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/joho/godotenv"

	"git.yeij.top/AskaEth/Cyrene/ai-core/internal/background"
	aiConfig "git.yeij.top/AskaEth/Cyrene/ai-core/internal/config"
	"git.yeij.top/AskaEth/Cyrene/ai-core/internal/crashlog"
	ctxbuild "git.yeij.top/AskaEth/Cyrene/ai-core/internal/context"
	"git.yeij.top/AskaEth/Cyrene/ai-core/internal/host"
	"git.yeij.top/AskaEth/Cyrene/ai-core/internal/llm"
	"git.yeij.top/AskaEth/Cyrene/ai-core/internal/memory"
	"git.yeij.top/AskaEth/Cyrene/ai-core/internal/model"
	"git.yeij.top/AskaEth/Cyrene/ai-core/internal/orchestrator"
	"git.yeij.top/AskaEth/Cyrene/ai-core/internal/persona"
	"git.yeij.top/AskaEth/Cyrene/ai-core/internal/rag"
	"git.yeij.top/AskaEth/Cyrene/ai-core/internal/subsession"
	"git.yeij.top/AskaEth/Cyrene/ai-core/internal/tools"

	plgManager "git.yeij.top/AskaEth/Cyrene-Plugins/manager"
	plgSDK "git.yeij.top/AskaEth/Cyrene-Plugins/sdk"
	pluginFile "git.yeij.top/AskaEth/Cyrene-Plugins/file"
	pluginHTTP "git.yeij.top/AskaEth/Cyrene-Plugins/http"
	pluginWF "git.yeij.top/AskaEth/Cyrene-Plugins/web_fetch"
)

var cfg Config

func main() {
	// 自动加载 .env 文件（优先从可执行文件位置反推仓库根目录）
	_ = godotenv.Load() // 先尝试当前目录
	if exe, err := os.Executable(); err == nil {
		_ = godotenv.Load(filepath.Join(filepath.Dir(exe), "..", "..", ".env"))
	}
	// 兜底：如果 LLM_MODEL 仍未设置，打印提示
	if os.Getenv("LLM_MODEL") == "" {
		log.Println("ℹ 未找到 .env 文件，将使用环境变量或默认值")
	}

	log.SetFlags(log.LstdFlags | log.Lshortfile)
	log.Println("🧠 AI-Core 服务启动中...")

	// 加载配置
	cfg = loadConfig()

	// 初始化人格加载器
	personaDir := cfg.PersonaDir
	if personaDir == "" {
		personaDir = "./internal/persona"
	}
	personaLoader, err := persona.NewLoader(personaDir)
	if err != nil {
		log.Fatalf("加载人格配置失败: %v", err)
	}
	log.Printf("已加载 %d 个人格: %v", len(personaLoader.List()), personaLoader.List())

	// 初始化模型配置加载器 (Phase 6: 多模型配置)
	configPath := getEnv("MODELS_CONFIG_PATH", "../models.json")
	configLoader, err := aiConfig.NewLoader(configPath)
	if err != nil {
		log.Printf("⚠ 模型配置加载失败，回退到 .env: %v", err)
		configLoader = nil
	}

	// 构建 .env 回退配置
	envFallback := llm.OpenAIConfig{
		BaseURL:       cfg.LLMBaseURL,
		APIKey:        cfg.LLMAPIKey,
		Model:         cfg.LLMModel,
		FallbackModel: cfg.LLMFallbackModel,
		MaxRetries:    3,
		Timeout:       180 * time.Second,
	}

	// 创建 ModelSelector (优先使用 models.json，回退到 .env)
	modelSelector := llm.NewModelSelector(configLoader, envFallback)

	// 为不同用途创建独立的适配器，支持目的路由
	// 在 .env 回退模式下，所有适配器共享同一 provider
	chatAdapter := modelSelector.DefaultAdapter()
	provider, _ := modelSelector.Select(context.Background(), llm.PurposeIntentAnalysis)
	intentAdapter := llm.NewAdapter(provider)
	provider, _ = modelSelector.Select(context.Background(), llm.PurposeDeepThinking)
	thinkerAdapter := llm.NewAdapter(provider)
	provider, _ = modelSelector.Select(context.Background(), llm.PurposeMemoryExtraction)
	memoryAdapter := llm.NewAdapter(provider)
	provider, _ = modelSelector.Select(context.Background(), llm.PurposeToolCalling)
	toolAdapter := llm.NewAdapter(provider)

	if configLoader != nil && configLoader.HasConfig() {
		log.Printf("LLM适配器已就绪: models.json 驱动 (chat=%s, intent=%s, think=%s, memory=%s, tool=%s)",
			chatAdapter.ModelName(), intentAdapter.ModelName(), thinkerAdapter.ModelName(), memoryAdapter.ModelName(), toolAdapter.ModelName())
	} else {
		log.Printf("LLM适配器已就绪: .env 驱动 (模型=%s)", chatAdapter.ModelName())
	}

	// 初始化记忆系统
	var memStore *memory.Store
	var memRetriever *memory.Retriever
	var memExtractor *memory.Extractor

	if cfg.DatabaseURL != "" {
		memStore = memory.NewStore(cfg.DatabaseURL)
		defer memStore.Close()

		memRetriever = memory.NewRetriever(memStore, nil)

		// 记忆提取器使用 memory purpose 适配器
		memExtractor = memory.NewExtractor(memStore, func(ctx context.Context, messages []model.LLMMessage) (*model.LLMResponse, error) {
			return memoryAdapter.Chat(ctx, messages)
		})
		log.Println("记忆提取器已就绪")
	}

	// 初始化会话历史存储
	convStore := ctxbuild.NewConversationStore(100)

	// 从数据库恢复主会话历史（避免重启丢失上下文）
	adminUserID := "admin"
	adminSessionID := "admin-session-main"
	if cfg.DatabaseURL != "" {
		convStore.SetDatabaseURL(cfg.DatabaseURL)
		if err := convStore.LoadFromDB(cfg.DatabaseURL, adminSessionID, 50); err != nil {
			log.Printf("⚠ 从数据库恢复会话历史失败（不影响服务启动）: %v", err)
		}
	}
	log.Println("会话历史存储已就绪 (上限50条)")

	// 初始化上下文构建器
	ctxBuilder := ctxbuild.NewBuilder(convStore)

	// 初始化 IoT 客户端
	var iotClient *tools.IoTClient
	if cfg.IoTServiceURL != "" {
		iotClient = tools.NewIoTClient(cfg.IoTServiceURL)
		log.Printf("IoT 客户端已就绪: %s", cfg.IoTServiceURL)
	} else {
		log.Println("IoT 客户端未配置 (IOT_SERVICE_URL 和 IOT_DEBUG_SERVICE_URL 均为空)")
	}

	// 初始化 Gateway HTTP 客户端（提醒/日程等工具用）
	gwBaseURL := getEnv("GATEWAY_URL", "http://localhost:8080")
	gwToken := os.Getenv("INTERNAL_SERVICE_TOKEN")
	gwClient := tools.NewGatewayClient(gwBaseURL, gwToken)
	log.Printf("Gateway 客户端已就绪: %s", gwBaseURL)

	// 初始化主机操控管理器 (沙箱执行 + 文件系统隔离)
	hostSandbox := host.NewSandbox(host.DefaultSandboxConfig())
	directBackend := host.NewDirectBackend(hostSandbox)
	hostManager := host.NewManager(directBackend)
	dataDir := getEnv("DATA_DIR", "/tmp/cyrene_data")
	hostManager.SetAllowedDirs([]string{dataDir, os.TempDir(), "."})
	log.Printf("主机操控管理器已就绪: 沙箱执行 + 文件隔离 (数据目录=%s)", dataDir)

	// 初始化完整OS环境管理器 (WSL/Docker，无沙箱限制，供 os_* 工具使用)
	osManager := createOSManager()
	if osManager != nil {
		log.Printf("完整OS环境管理器已就绪: backend=%s", osManager.BackendName())
	} else {
		log.Println("完整OS环境管理器未配置 (设置 HOST_EXEC_BACKEND=wsl 或 docker 以启用)")
	}

		// 初始化 RAG 知识库 (Phase 6.6: 知识库 RAG 增强)
		knowledgeDir := getEnv("KNOWLEDGE_DIR", "./data/knowledge")
		ragEmbedder := rag.NewEmbedder(cfg.LLMBaseURL, cfg.LLMAPIKey, "text-embedding-3-small")
		knowledgeStore := rag.NewKnowledgeStore(ragEmbedder, knowledgeDir)
		knowledgeRetriever := rag.NewRetriever(knowledgeStore)
		log.Printf("RAG 知识库已就绪: 目录=%s, 嵌入模型=text-embedding-3-small", knowledgeDir)

	// 初始化工具注册中心 (使用共享插件模块)
	toolRegistry := plgManager.NewToolRegistry()
	var visionProvider llm.LLMProvider
	var ocrProvider llm.LLMProvider
		var videoProvider llm.LLMProvider
	var asrProvider llm.ASRProvider
	if getEnvBool("ENABLE_TOOLS", true) {
		// 11 个共享通用插件 — 注册其工具到统一注册中心
		registerPlugins(toolRegistry)
		for _, t := range pluginFile.NewFilePlugin(dataDir).Tools() { toolRegistry.Register(t) }
		for _, t := range pluginHTTP.NewHTTPPlugin().Tools() { toolRegistry.Register(t) }
		// 仅使用本地 SearXNG（国内 DuckDuckGo 不可用）
		searxngURL := getEnv("SEARXNG_URL", "")
		if searxngURL != "" {
			toolRegistry.Register(wrapTool(tools.NewWebSearchToolWithURL(searxngURL), "web_search", "Search the web via SearXNG", "search"))
		}
		for _, t := range pluginWF.NewWebFetchPlugin().Tools() { toolRegistry.Register(t) }
		// ai-core 专属工具 — 通过 sdk.Tool 适配器注册
		if iotClient != nil {
			toolRegistry.Register(wrapTool(tools.NewIoTQueryTool(iotClient), "iot_query", "Query IoT Devices", "iot"))
			toolRegistry.Register(wrapTool(tools.NewIoTControlTool(iotClient), "iot_control", "Control IoT Devices", "iot"))
		}

		if hostManager != nil {
			toolRegistry.Register(wrapTool(tools.NewHostExecTool(hostManager), "host_exec", "Host Command Execution", "system"))
			toolRegistry.Register(wrapTool(tools.NewHostFileTool(hostManager), "host_file", "Host File Operations", "system"))
			toolRegistry.Register(wrapTool(tools.NewHostSystemTool(hostManager), "host_system", "Host System Info", "system"))
		}

		if osManager != nil {
			toolRegistry.Register(wrapTool(tools.NewOSExecTool(osManager), "os_exec", "OS Command Execution", "system"))
			toolRegistry.Register(wrapTool(tools.NewOSFileTool(osManager), "os_file", "OS File Operations", "system"))
			toolRegistry.Register(wrapTool(tools.NewOSSystemTool(osManager), "os_system", "OS System Info", "system"))
		}

		// 提醒/日程工具（需要 Gateway 客户端）
		if gwClient != nil && gwToken != "" {
			toolRegistry.Register(wrapTool(tools.NewReminderCreateTool(gwClient, adminUserID), "reminder_create", "Create Reminder", "reminder"))
			toolRegistry.Register(wrapTool(tools.NewReminderListTool(gwClient, adminUserID), "reminder_list", "List Reminders", "reminder"))
			toolRegistry.Register(wrapTool(tools.NewReminderDeleteTool(gwClient, adminUserID), "reminder_delete", "Delete Reminder", "reminder"))
			log.Println("[工具] 提醒工具已注册 (reminder_create, reminder_list, reminder_delete)")
		}

		visionProvider = nil
		if configLoader != nil && configLoader.HasConfig() {
			cfg := configLoader.GetConfig()
			if route, ok := cfg.Routing["vision"]; ok && len(route.FallbackChain) > 0 {
				for _, mid := range route.FallbackChain {
					if _, ok := cfg.Models[mid]; ok {
						visionProvider, _ = modelSelector.Select(context.Background(), llm.PurposeVision)
						log.Printf("视觉模型已启用: %s", visionProvider.ModelName())
						break
					}
				}
			}
		}
		if visionProvider == nil {
			log.Println("视觉模型未配置，vision_analyze 将使用 base64 模式")
		}

		// 初始化 OCR 模型（与视觉模型并行调用，提供文字提取结果给会话模型自主判断）
		ocrProvider = nil
		if configLoader != nil && configLoader.HasConfig() {
			cfg := configLoader.GetConfig()
			if route, ok := cfg.Routing["ocr"]; ok && len(route.FallbackChain) > 0 {
				for _, mid := range route.FallbackChain {
					if _, ok := cfg.Models[mid]; ok {
						ocrProvider, _ = modelSelector.Select(context.Background(), llm.PurposeOCR)
						log.Printf("OCR模型已启用: %s", ocrProvider.ModelName())
						break
					}
				}
			}

		}
		if ocrProvider == nil {
			log.Println("OCR模型未配置，图片文字提取将复用视觉模型")
		}

		// 初始化视频理解模型
		videoProvider = nil
		if configLoader != nil && configLoader.HasConfig() {
			cfg := configLoader.GetConfig()
			if route, ok := cfg.Routing["video"]; ok && len(route.FallbackChain) > 0 {
				for _, mid := range route.FallbackChain {
					if _, ok := cfg.Models[mid]; ok {
						videoProvider, _ = modelSelector.Select(context.Background(), llm.PurposeVideo)
						log.Printf("视频理解模型已启用: %s", videoProvider.ModelName())
						break
					}
				}
			}
		}
		if videoProvider == nil {
			log.Println("视频理解模型未配置")
		}

		// 初始化 ASR 语音识别模型
		asrProvider = nil
		if configLoader != nil && configLoader.HasConfig() {
			cfg := configLoader.GetConfig()
			if route, ok := cfg.Routing["speech_recognition"]; ok && len(route.FallbackChain) > 0 {
				for _, mid := range route.FallbackChain {
					if m, ok := cfg.Models[mid]; ok {
						if p, ok := cfg.Providers[m.Provider]; ok {
							asrProvider = llm.NewDashScopeASRProvider(p.BaseURL, p.APIKey, m.Name)
							log.Printf("ASR语音识别模型已启用: %s", m.Name)
							break
						}
					}
				}
			}
		}
		if asrProvider == nil {
			log.Println("ASR语音识别模型未配置")
		}

		toolRegistry.Register(wrapTool(tools.NewVisionTool(visionProvider), "vision_analyze", "Image Vision Analysis & OCR", "multimodal"))
		toolRegistry.Register(wrapTool(tools.NewVideoTool(videoProvider), "video_analyze", "Video Understanding & Analysis", "multimodal"))

		if knowledgeRetriever != nil {
			toolRegistry.Register(wrapTool(tools.NewKnowledgeSearchTool(knowledgeRetriever), "knowledge_search", "Search Knowledge Base", "knowledge"))
			toolRegistry.Register(wrapTool(tools.NewKnowledgeIngestTool(knowledgeStore), "knowledge_ingest", "Ingest Knowledge Document", "knowledge"))
		}
		log.Printf("工具注册中心已就绪: %d 个工具 (%v)", len(toolRegistry.DefinitionNames()), toolRegistry.DefinitionNames())
	}

	// 初始化后台思考器（增强版：支持工具调用和记忆管理）
	thinkerCfg := background.DefaultThinkerConfig()

	// 创建记忆服务 HTTP 客户端（用于持久化思考日志到 memory-service）
	memServiceURL := getEnv("MEMORY_SERVICE_URL", "http://localhost:8091")
	memClient := memory.NewClient(memServiceURL)
	log.Printf("记忆服务客户端已就绪: %s", memServiceURL)

	thinker := background.NewThinker(
		thinkerCfg,
		personaLoader,
		memRetriever,
		thinkerAdapter,
		toolAdapter,
		iotClient,
		memStore,
		toolRegistry,
		convStore,
		adminUserID,
		adminSessionID,
		cfg.AdminNickname,
		memClient,
	)

	// 初始化动态调度加载器 (Phase: thinking-schedule)
	schedulePath := getEnv("THINKING_SCHEDULE_PATH", "../thinking_schedule.json")
	if scheduleLoader, err := background.NewScheduleLoader(schedulePath); err != nil {
		log.Printf("⚠ 思考调度配置加载失败，使用固定间隔: %v", err)
	} else if scheduleLoader.HasConfig() {
		thinker.SetScheduleLoader(scheduleLoader)
		log.Println("[后台思考] 动态调度已启用 (thinking_schedule.json)")
	} else {
		log.Println("[后台思考] 调度配置文件不存在，使用 .env 固定间隔")
	}

	thinker.Start()
	defer thinker.Stop()

	// 设置主动消息推送回调（调用 Gateway 内部 API）
	gatewayURL := getEnv("GATEWAY_URL", "http://localhost:8080")
	internalToken := os.Getenv("INTERNAL_SERVICE_TOKEN")
	if internalToken != "" {
		proactiveClient := &http.Client{Timeout: 5 * time.Second}
		thinker.SetMessagePusher(func(userID, sessionID, message string) {
			reqBody, _ := json.Marshal(map[string]string{
				"user_id":    userID,
				"session_id": sessionID,
				"content":    message,
			})
			req, _ := http.NewRequest("POST", gatewayURL+"/api/v1/internal/proactive-message", strings.NewReader(string(reqBody)))
			req.Header.Set("Content-Type", "application/json")
			req.Header.Set("X-Internal-Token", internalToken)
			resp, err := proactiveClient.Do(req)
			if err != nil {
				log.Printf("[主动消息] 推送失败: %v", err)
				return
			}
			resp.Body.Close()
			if resp.StatusCode == http.StatusOK {
				log.Printf("[主动消息] 已推送到 Gateway: user=%s, len=%d", userID, len(message))
			} else {
				log.Printf("[主动消息] Gateway 返回 %d", resp.StatusCode)
			}
		})
		log.Printf("[主动消息] 推送已启用 (Gateway=%s)", gatewayURL)

		// 设置平台主动消息推送回调（调用 Platform Bridge 内部 API）
		platformBridgeURL := getEnv("PLATFORM_BRIDGE_URL", "http://localhost:8095")
		thinker.SetPlatformMessagePusher(func(target background.ProactiveTarget, message string) {
			reqBody, _ := json.Marshal(map[string]string{
				"platform":   target.Platform,
				"chat_type":  target.ChatType,
				"user_id":    target.UserID,
				"group_id":   target.GroupID,
				"at_user_id": target.AtUserID,
				"content":    message,
			})
			req, _ := http.NewRequest("POST",
				platformBridgeURL+"/api/v1/internal/send-proactive",
				strings.NewReader(string(reqBody)))
			req.Header.Set("Content-Type", "application/json")
			req.Header.Set("X-Internal-Token", internalToken)
			resp, err := proactiveClient.Do(req)
			if err != nil {
				log.Printf("[主动消息] 平台推送失败: %v", err)
				return
			}
			resp.Body.Close()
			if resp.StatusCode == http.StatusOK {
				log.Printf("[主动消息] 已推送到 Platform Bridge: target=%s/%s user=%s group=%s len=%d",
					target.Platform, target.ChatType, target.UserID, target.GroupID, len(message))
			} else {
				log.Printf("[主动消息] Platform Bridge 返回 %d", resp.StatusCode)
			}
		})
		log.Printf("[主动消息] 平台推送已启用 (PlatformBridge=%s)", platformBridgeURL)
		go syncPlatformChannels(thinker, platformBridgeURL, internalToken)
	} else {
		log.Println("[主动消息] 未配置 INTERNAL_SERVICE_TOKEN，主动消息推送已禁用")
	}

	// 健康检查与对话API的HTTP mux
	mux := http.NewServeMux()

	// 注册全链路追踪事件端点
	registerTraceEndpoint(mux)

	// 初始化子会话管理器
	subManager := subsession.NewManager(chatAdapter)

	// 注册子会话提供者
	subManager.Register(subsession.NewGeneralProvider(personaLoader))
	if memRetriever != nil {
		memProvider := subsession.NewMemoryProvider(memRetriever)
		memProvider.SetFuzzySearch(memoryAdapter, memClient)
		subManager.Register(memProvider)
	}
	if iotClient != nil {
		subManager.Register(subsession.NewIoTProvider(iotClient, personaDir))
	}
	subManager.Register(subsession.NewReviewProvider())
	if knowledgeRetriever != nil {
		subManager.Register(subsession.NewKnowledgeProvider(knowledgeRetriever))
	}
	log.Printf("子会话管理器已就绪: %d 个提供者 (%v)", len(subManager.ListProviders()), subManager.ListProviders())

	// 构建新的 Orchestrator (v2.0) — 传入 purpose 专用适配器
	orch := orchestrator.NewOrchestrator(
		personaLoader,
		ctxBuilder,
		chatAdapter,
		intentAdapter,
		subManager,
		memRetriever,
		memExtractor,
	)
	orch.SetToolRegistry(toolRegistry)
	// 设置工具结果主动推送回调 — 通用，不绑定特定工具
	orch.SetToolResultPusher(func(sessionID, userID, toolName, result string, params orchestrator.SynthesizeParams) {
		if thinker == nil || orch == nil {
			return
		}
		if userID == "" {
			userID = adminUserID
		}

		// 异步执行跟进：触发 LLM 生成回复并推送到原消息渠道
		go func() {
			var toolResult map[string]interface{}
			if err := json.Unmarshal([]byte(result), &toolResult); err != nil {
				toolResult = map[string]interface{}{"output": result}
			}
			output, _ := toolResult["output"].(string)
			if output == "" {
				output = result
			}

			followUpMsg := fmt.Sprintf("【系统消息】后台工具 %s 执行完成。结果：\n%s\n\n请基于以上结果生成回复发送给用户。", toolName, output)

			// 持久化工具结果到会话历史（重启后不丢失）
			ctxBuilder.CacheMessage(sessionID, model.RoleSystem,
				fmt.Sprintf("[工具 %s 执行结果]\n%s", toolName, output))

			ctx, cancel := context.WithTimeout(context.Background(), 90*time.Second)
			defer cancel()
			eventCh, err := orch.ProcessInput(ctx, orchestrator.ProcessParams{
				UserID:      userID,
				SessionID:   sessionID,
				Message:     followUpMsg,
				Mode:        "text",
				Nickname:    params.Nickname,
				ChannelType: params.ChannelType,
				ChannelID:   params.ChannelID,
				AdapterName: params.AdapterName,
			})
			if err != nil {
				log.Printf("[tool-followup] ProcessInput 失败: %v", err)
				return
			}

			var sb strings.Builder
			for event := range eventCh {
				if event.Type == model.StreamDelta {
					sb.WriteString(event.Delta)
				}
			}
			followUpResponse := sb.String()
			if followUpResponse == "" {
				return
			}

			// 推送到原平台渠道
			if params.ChannelType != "" && params.ChannelID != "" && params.AdapterName != "" {
				target := background.ProactiveTarget{
					Platform: params.AdapterName,
					ChatType: "private", // send-proactive 要求 private/group，不是 direct
				}
				if params.ChannelType == "group" {
					target.ChatType = "group"
					target.GroupID = params.ChannelID
				} else {
					target.UserID = strings.TrimPrefix(params.ChannelID, "private_")
				}
				log.Printf("[tool-followup] 推送跟进到 platform=%s chat=%s channel=%s len=%d",
					target.Platform, target.ChatType, params.ChannelID, len(followUpResponse))
				thinker.PushPlatformMessage(target, followUpResponse)
			} else {
				thinker.TriggerReminderMessage(userID, sessionID, followUpResponse)
			}
		}()
	})

	if visionProvider != nil {
		orch.SetVisionProvider(visionProvider)
		log.Printf("对话编排器: 视觉模型已注入 (%s)", visionProvider.ModelName())
	}
	if ocrProvider != nil {
		orch.SetOCRProvider(ocrProvider)
		log.Printf("对话编排器: OCR模型已注入 (%s)", ocrProvider.ModelName())
	}
		if videoProvider != nil {
			orch.SetVideoProvider(videoProvider)
			log.Printf("对话编排器: 视频模型已注入 (%s)\n", videoProvider.ModelName())
		} else {
			log.Println("对话编排器: 视频模型未配置，视频理解功能不可用")
		}
		if asrProvider != nil && asrProvider.IsAvailable() {
			orch.SetASRProvider(asrProvider)
			log.Printf("对话编排器: ASR语音识别模型已注入 (%s)\n", asrProvider.ModelName())
		} else {
			log.Println("对话编排器: ASR语音识别模型未配置")
		}
			log.Println("对话编排器 v2.0 已就绪")
	_ = orch

	// 注册对话API端点
	mux.HandleFunc("/api/v1/chat", func(w http.ResponseWriter, r *http.Request) {
		handleChat(w, r, orch, ctxBuilder, personaLoader, memRetriever, memExtractor, iotClient, thinker, toolRegistry, adminSessionID)
	})

	// 注册记忆API端点
	mux.HandleFunc("/api/v1/memory/search", func(w http.ResponseWriter, r *http.Request) {
		handleMemorySearch(w, r, memRetriever)
	})
	mux.HandleFunc("/api/v1/memory", func(w http.ResponseWriter, r *http.Request) {
		handleMemoryCRUD(w, r, memStore, memExtractor)
	})

		// Phase 2: 在线状态通知端点 (Gateway -> ai-core)
	mux.HandleFunc("/api/v1/internal/presence", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPost {
			w.WriteHeader(http.StatusMethodNotAllowed)
			return
		}
		if r.Header.Get("X-Internal-Token") != os.Getenv("INTERNAL_SERVICE_TOKEN") {
			w.WriteHeader(http.StatusUnauthorized)
			return
		}
		var req struct {
			UserID    string `json:"user_id"`
			Status    string `json:"status"`
			SessionID string `json:"session_id"`
		}
		if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
			w.WriteHeader(http.StatusBadRequest)
			return
		}
		online := req.Status == "online"
		thinker.UpdatePresence(online, req.SessionID)
		w.Header().Set("Content-Type", "application/json")
		w.Write([]byte(`{"status":"ok"}`))
	})

	// 提醒触发端点：Gateway 调度器在提醒到期时调用，由 LLM 生成自然提醒语推送
	mux.HandleFunc("/api/v1/internal/reminder-trigger", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPost {
			w.WriteHeader(http.StatusMethodNotAllowed)
			return
		}
		if r.Header.Get("X-Internal-Token") != os.Getenv("INTERNAL_SERVICE_TOKEN") {
			w.WriteHeader(http.StatusUnauthorized)
			return
		}
		var req struct {
			Title       string `json:"title"`
			Description string `json:"description"`
			UserID      string `json:"user_id"`
			SessionID   string `json:"session_id"`
			Platform    string `json:"platform,omitempty"`
			ChannelType string `json:"channel_type,omitempty"`
			ChannelID   string `json:"channel_id,omitempty"`
			AdapterName string `json:"adapter_name,omitempty"`
		}
		if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
			w.WriteHeader(http.StatusBadRequest)
			return
		}
		// Generate a natural reminder via LLM.
		prompt := fmt.Sprintf(`【系统提醒】现在有一条提醒到时间了：
标题：%s
描述：%s

请用昔涟的口吻，用一句话自然地提醒开拓者。语气温柔俏皮，像朋友间的关心。
只输出提醒语本身，不要加前缀或引号。`, req.Title, req.Description)

		messages := []model.LLMMessage{
			{Role: model.RoleSystem, Content: "你是昔涟。用温柔俏皮的语气，一句话提醒开拓者。"},
			{Role: model.RoleUser, Content: prompt},
		}
		ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second)
		defer cancel()
		resp, err := chatAdapter.Chat(ctx, messages)
		if err != nil {
			log.Printf("[reminder-trigger] LLM生成失败: %v", err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		reminderMsg := strings.TrimSpace(resp.Content)
		if reminderMsg == "" {
			reminderMsg = fmt.Sprintf("开拓者～「%s」的提醒到时间啦♪", req.Title)
		}
		log.Printf("[reminder-trigger] 生成提醒语: %s", reminderMsg)

		// Push through the message pusher if available.
		if thinker != nil {
			// If group reminder, push through platform-bridge via thinker
			if req.Platform != "" && req.ChannelID != "" {
				target := background.ProactiveTarget{
					Platform: req.Platform,
					ChatType: req.ChannelType,
					GroupID:  req.ChannelID,
				}
				if req.AdapterName != "" {
					target.Platform = req.AdapterName
				}
				thinker.PushPlatformMessage(target, reminderMsg)
			}
			// 只有非群聊提醒才推Web端（群聊提醒已通过platform-bridge发送）
			if req.Platform == "" {
				thinker.TriggerReminderMessage(req.UserID, req.SessionID, reminderMsg)
			}
		}
		w.Header().Set("Content-Type", "application/json")
		w.Write([]byte(`{"status":"ok"}`))
	})

	mux.HandleFunc("/api/v1/debug/lock-holder", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		by, at := thinker.LockHolder()
		json.NewEncoder(w).Encode(map[string]interface{}{
			"locked_by": by,
			"locked_at": at.Format(time.RFC3339),
			"held_for":  time.Since(at).String(),
		})
	})
	mux.HandleFunc("/api/v1/debug/goroutines", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/plain")
		buf := make([]byte, 1024*1024)
		n := runtime.Stack(buf, true)
		w.Write(buf[:n])
	})
	mux.HandleFunc("/api/v1/health", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		w.Write([]byte(`{"status":"ok","service":"ai-core","model":"` + chatAdapter.ModelName() + `"}`))
	})

	// LLM 调用日志（调试用）
	mux.HandleFunc("/api/v1/llm-calls", func(w http.ResponseWriter, r *http.Request) {
		limit := 50
		if n, err := fmt.Sscanf(r.URL.Query().Get("limit"), "%d", &limit); n != 1 || err != nil || limit <= 0 {
			limit = 50
		}
		if limit > 500 {
			limit = 500
		}
		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(map[string]interface{}{
			"calls": llm.GetCalls(limit),
			"total": len(llm.GetCalls(0)),
		})
	})


	// LLM 调用 SSE 实时推送
	mux.HandleFunc("/api/v1/llm-calls/stream", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/event-stream")
		w.Header().Set("Cache-Control", "no-cache")
		w.Header().Set("Connection", "keep-alive")
		w.Header().Set("Access-Control-Allow-Origin", "*")

		
		flusher, ok := w.(http.Flusher)
		if !ok {
			http.Error(w, "streaming not supported", http.StatusInternalServerError)
			return
		}

		ch, done := llm.SubscribeCalls()
		defer llm.UnsubscribeCalls(ch)

		for {
			select {
			case rec := <-ch:
				data, _ := json.Marshal(rec)
				fmt.Fprintf(w, "data: %s\n\n", data)
				flusher.Flush()
			case <-done:
				return
			case <-r.Context().Done():
				return
			}
		}
	})
	// 工具调用记录
	mux.HandleFunc("/api/v1/tools/calls", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodGet {
			w.WriteHeader(http.StatusMethodNotAllowed)
			return
		}
		toolName := r.URL.Query().Get("tool_name")
		limit := 50
		if n, err := fmt.Sscanf(r.URL.Query().Get("limit"), "%d", &limit); n != 1 || err != nil || limit <= 0 {
			limit = 50
		}
		if limit > 500 {
			limit = 500
		}
			page, _ := strconv.Atoi(r.URL.Query().Get("page"))
		if page < 1 {
			page = 1
		}
		offset := (page - 1) * limit
		calls, total := toolRegistry.GetCallLogs(toolName, limit, offset)
		if calls == nil {
			calls = []plgManager.CallLogRecord{}
		}
		totalPages := total / limit
		if total%limit != 0 {
			totalPages++
		}
		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(map[string]interface{}{
			"calls": calls, "total": total, "total_pages": totalPages,
			"page": page, "limit": limit,
		})
		})

	// 工具调用统计
	mux.HandleFunc("/api/v1/tools/calls/stats", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodGet {
			w.WriteHeader(http.StatusMethodNotAllowed)
			return
		}
		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(toolRegistry.GetCallStats())
	})

	// OS 环境监控端点
	mux.HandleFunc("/api/v1/system/info", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodGet {
			w.WriteHeader(http.StatusMethodNotAllowed)
			return
		}
		result := map[string]interface{}{
			"os_enabled": osManager != nil,
		}
		if osManager != nil {
			result["backend"] = osManager.BackendName()
			result["system"] = osManager.SystemInfo()
			if disk, err := osManager.DiskUsage("/"); err == nil {
				result["disk"] = disk
			}
		}
		if hostManager != nil {
			result["host"] = map[string]interface{}{
				"backend": hostManager.BackendName(),
				"system":  hostManager.SystemInfo(),
			}
		}
		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(result)
	})

	// Debug: 全链路 HTTP 请求日志
	debugMux := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()
		crashlog.WrapHTTP(mux).ServeHTTP(w, r)
		elapsed := time.Since(start)
		if elapsed > 2*time.Second || r.URL.Path != "/api/v1/health" {
			log.Printf("[http] %s %s %v", r.Method, r.URL.Path, elapsed.Round(time.Millisecond))
		}
	})

	// 启动HTTP服务（全局 panic 恢复 + 崩溃日志）
	srv := &http.Server{
		Addr:    ":" + cfg.Port,
		Handler: debugMux,
	}

	go func() {
		log.Printf("🚀 AI-Core 服务已启动在端口 %s", cfg.Port)
		if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			log.Fatalf("服务启动失败: %v", err)
		}
	}()

	// Debug: 每30秒输出内存和goroutine统计
	go func() {
		ticker := time.NewTicker(30 * time.Second)
		defer ticker.Stop()
		var m runtime.MemStats
		for range ticker.C {
			runtime.ReadMemStats(&m)
			log.Printf("[stats] goroutines=%d heap=%dMB sys=%dMB gc=%d",
				runtime.NumGoroutine(),
				m.HeapAlloc/1024/1024,
				m.Sys/1024/1024,
				m.NumGC)
		}
	}()

	// 优雅关闭
	quit := make(chan os.Signal, 1)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
	<-quit
	log.Println("正在关闭 AI-Core 服务...")

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()
	srv.Shutdown(ctx)
	log.Println("AI-Core 服务已关闭")
}

// Config AI-Core配置
type Config struct {
	Port             string
	PersonaDir       string
	LLMBaseURL       string
	LLMAPIKey        string
	LLMModel         string
	LLMFallbackModel string
	DatabaseURL      string
	IoTServiceURL    string
	AdminNickname    string // 昔涟对管理员用户的基本称呼
}

func loadConfig() Config {
	return Config{
		Port:             getEnv("AI_CORE_PORT", "8081"),
		PersonaDir:       getEnv("PERSONA_DIR", "./internal/persona"),
		LLMBaseURL:       getEnv("LLM_API_URL", "https://api.openai.com/v1"),
		LLMAPIKey:        getEnv("LLM_API_KEY", ""),
		LLMModel:         getEnv("LLM_MODEL", "gpt-4o"),
		LLMFallbackModel: getEnv("LLM_FALLBACK_MODEL", "gpt-4o-mini"),
		DatabaseURL:      buildDatabaseURL(),
		IoTServiceURL:    getEnvWithFallback("IOT_SERVICE_URL", "IOT_DEBUG_SERVICE_URL", ""),
		AdminNickname:    getEnv("ADMIN_NICKNAME", "管理员"),
	}
}

func buildDatabaseURL() string {
	host := getEnv("POSTGRES_HOST", "localhost")
	port := getEnv("POSTGRES_PORT", "5432")
	user := getEnv("POSTGRES_USER", "cyrene")
	password := getEnv("POSTGRES_PASSWORD", "cyrene_pass")
	dbname := getEnv("POSTGRES_DB", "cyrene_ai")
	sslmode := getEnv("POSTGRES_SSLMODE", "disable")

	return fmt.Sprintf("postgres://%s:%s@%s:%s/%s?sslmode=%s",
		user, password, host, port, dbname, sslmode)
}

func getEnv(key, fallback string) string {
	if v := os.Getenv(key); v != "" {
		return v
	}
	return fallback
}

// getEnvWithFallback 获取环境变量，优先使用 primaryKey，如果为空则回退到 fallbackKey
func getEnvWithFallback(primaryKey, fallbackKey, defaultVal string) string {
	if v := os.Getenv(primaryKey); v != "" {
		return v
	}
	if v := os.Getenv(fallbackKey); v != "" {
		return v
	}
	return defaultVal
}

func getEnvBool(key string, fallback bool) bool {
	v := os.Getenv(key)
	if v == "" {
		return fallback
	}
	switch strings.ToLower(v) {
	case "true", "1", "yes", "on":
		return true
	case "false", "0", "no", "off":
		return false
	default:
		return fallback
	}
}

// createOSManager 根据 HOST_EXEC_BACKEND 环境变量创建完整OS环境管理器。
// 支持 "wsl" 和 "docker" 两种后端。返回 nil 表示未配置或配置无效。
func createOSManager() *host.Manager {
	backend := strings.ToLower(os.Getenv("HOST_EXEC_BACKEND"))
	switch backend {
	case "wsl":
		distro := getEnv("WSL_DISTRO", "Ubuntu-22.04")
		username := getEnv("WSL_USER", "cyrene")
		password := os.Getenv("WSL_USER_PASSWORD")
		maxTimeout := time.Duration(getEnvInt("HOST_EXEC_MAX_TIMEOUT", 300)) * time.Second
		wslBackend := host.NewWSLBackend(distro, username, password, maxTimeout)
		return host.NewManager(wslBackend)
	case "docker":
		container := getEnv("SANDBOX_CONTAINER", "cyrene-sandbox")
		image := getEnv("SANDBOX_IMAGE", "ubuntu:22.04")
		maxTimeout := time.Duration(getEnvInt("HOST_EXEC_MAX_TIMEOUT", 300)) * time.Second
		dockerBackend := host.NewDockerBackend(container, image, maxTimeout)
		return host.NewManager(dockerBackend)
	default:
		return nil
	}
}

// getEnvInt 获取整数类型的环境变量
func getEnvInt(key string, fallback int) int {
	v := os.Getenv(key)
	if v == "" {
		return fallback
	}
	n, err := strconv.Atoi(v)
	if err != nil || n <= 0 {
		return fallback
	}
	return n
}

// getFallbackMessage returns a natural-sounding fallback when LLM is unavailable.
func getFallbackMessage() string {
	messages := []string{
		"开拓者…我现在好像有点恍惚…稍等一下好吗？",
		"唔…忆庭的投影好像出了点小问题。等我一下下♪",
		"信号有点不太好呢…你稍等我一下哦？",
	}
	return messages[time.Now().UnixNano()%int64(len(messages))]
}


// registerPluginTools 从插件实例注册其所有工具到注册中心
func registerPluginTools(registry *plgManager.ToolRegistry, plugin plgSDK.Plugin) {
	for _, t := range plugin.Tools() {
		if err := registry.Register(t); err != nil {
			log.Printf("⚠ 注册工具失败: %v", err)
		}
	}
}

// wrapTool 包装 ai-core 旧 ToolExecutor 为 sdk.Tool
func wrapTool(executor tools.ToolExecutor, id, displayName, category string) plgSDK.Tool {
	return &toolAdapter{
		executor: executor,
		def: plgSDK.ToolDefinition{
			ID: id, Name: id, DisplayName: displayName,
			Description: executor.Definition().Description,
			Category:    category,
			Complexity:  plgSDK.ComplexitySimple,
			Parameters:  executor.Definition().Parameters,
		},
	}
}

type toolAdapter struct {
	plgSDK.BaseTool
	executor tools.ToolExecutor
	def      plgSDK.ToolDefinition
}

func (a *toolAdapter) Definition() plgSDK.ToolDefinition { return a.def }
func (a *toolAdapter) Execute(ctx context.Context, args map[string]interface{}) (*plgSDK.ToolResult, error) {
	result, err := a.executor.Execute(ctx, args)
	if err != nil {
		return nil, err
	}
	return &plgSDK.ToolResult{
		ToolName: result.ToolName, Success: result.Success,
		Output: result.Data, Error: result.Error,
	}, nil
}

// buildOpenAITools 将工具注册中心的定义转换为 LLM 层的 OpenAITool 格式
func buildOpenAITools(registry *plgManager.ToolRegistry) []llm.OpenAITool {
	if registry == nil || !registry.IsEnabled() {
		return nil
	}
	defs := registry.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
}

// handleChat 处理对话请求（SSE 流式响应）— 使用新 Orchestrator v2.0
func handleChat(
	w http.ResponseWriter,
	r *http.Request,
	orch *orchestrator.Orchestrator,
	ctxBuilder *ctxbuild.Builder,
	_ *persona.Loader,
	_ *memory.Retriever,
	_ *memory.Extractor,
	iotClient *tools.IoTClient,
	thinker *background.Thinker,
	_ *plgManager.ToolRegistry,
	adminSessionID string,
) {
	if r.Method != http.MethodPost {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	// 解析请求
	var req struct {
		UserID    string   `json:"user_id"`
		SessionID string   `json:"session_id"`
		Message   string   `json:"message"`
		Images    []string `json:"images,omitempty"` // 图片 base64 data URL
		VideoURLs []string `json:"video_urls,omitempty"` // 视频 URL (多模态)
		VoiceURLs []string `json:"voice_urls,omitempty"` // 语音 URL (ASR 转录)
		Mode      string   `json:"mode"`
		Nickname  string   `json:"nickname,omitempty"`
		IsAdmin   bool     `json:"is_admin"`
			Source    struct {
				Platform    string `json:"platform"`
				ChannelID   string `json:"channel_id"`
				ChannelType string `json:"channel_type"`
				SenderName  string `json:"sender_name"`
				OriginalUID string `json:"original_uid"`
				BotUID      string `json:"bot_uid"`
				GroupName   string `json:"group_name,omitempty"`
				AdapterName string `json:"adapter_name,omitempty"`
			} `json:"source,omitempty"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "无效的请求体", http.StatusBadRequest)
		return
	}

	if req.Mode == "" {
		req.Mode = "text"
	}

	// 平台静默观察模式：只记录消息、提取记忆、触发后台思考，不生成回复。
	if req.Mode == "group_ambient" {
			// 群聊环境消息 — 审查是否值得回应
			message := req.Message
			ctxBuilder.CacheMessage(req.SessionID, model.RoleUser, message)
			if thinker != nil {
				thinker.RecordUserMessage(req.SessionID)
			}
			// 在消息前加审查指令，让 LLM 自己判断是否插话
			req.Message = "【群聊环境消息】" + message + "\n\n你不需要回复这条消息，除非你真有特别相关的话要说。如果不想说话，只用 <action> 在心里轻轻想一下即可，不用发消息。"
			req.Mode = "text"
		}
		if req.Mode == "platform_silent" {
		if thinker != nil {
			thinker.RecordUserMessage(req.SessionID)
			if req.Source.Platform != "" && req.Source.BotUID != "" {
				adapterKey := req.Source.Platform
			if req.Source.AdapterName != "" {
				adapterKey = req.Source.AdapterName
			}
			thinker.SetBotUID(adapterKey, req.Source.BotUID)
			}
			if req.Source.Platform != "" && req.Source.ChannelID != "" {
				adapterKey := req.Source.Platform
				if req.Source.AdapterName != "" {
					adapterKey = req.Source.AdapterName
				}
				thinker.SetBotUID(adapterKey, req.Source.BotUID)
				thinker.AddOrUpdatePlatformChannel(req.Source.Platform, req.Source.ChannelType, req.Source.ChannelID, req.Source.GroupName, "", req.Source.AdapterName)
			}
		}
		// 图片预处理：静默观察时也分析图片内容，供后台思考使用
		message := req.Message
		if len(req.Images) > 0 {
			startTime := time.Now()
			augmented := orch.PreprocessImages(r.Context(), message, req.Images)
			if augmented != message {
				message = augmented
				log.Printf("[silent] 图片预处理耗时: %%v", time.Since(startTime))
			}
		}
		ctxBuilder.CacheMessage(req.SessionID, model.RoleUser, message)
		// 从观察到的群聊消息中提取记忆。
		orch.ExtractMemoriesOnly(r.Context(), req.UserID, req.SessionID, message)
		if thinker != nil {
			thinker.TriggerPostChatThink()
		}
		w.Header().Set("Content-Type", "application/json")
		w.Write([]byte(`{"status":"silent_processed"}`))
		return
	}

	ctx, cancel := context.WithTimeout(r.Context(), 120*time.Second)
	defer cancel()

	// Inject admin flag for tool access control.
	ctx = context.WithValue(ctx, plgManager.CtxKeyIsAdmin, req.IsAdmin)

	// 0. 记录用户活动（重置闲置计时器）
	if thinker != nil {
		thinker.RecordUserMessage(req.SessionID)
		if req.Source.Platform != "" && req.Source.BotUID != "" {
			adapterKey := req.Source.Platform
			if req.Source.AdapterName != "" {
				adapterKey = req.Source.AdapterName
			}
			thinker.SetBotUID(adapterKey, req.Source.BotUID)
		}
		if req.Source.Platform != "" && req.Source.ChannelID != "" {
			thinker.AddOrUpdatePlatformChannel(req.Source.Platform, req.Source.ChannelType, req.Source.ChannelID, req.Source.GroupName, "", req.Source.AdapterName)
		}
	}

	// Admin private messages: redirect to the main admin session so conversation
	// history is shared across platforms (OBv11, web UI, etc.).
	if req.IsAdmin && req.Source.ChannelType == "direct" && adminSessionID != "" {
		req.SessionID = adminSessionID
	}

	// 确定用户昵称
	userNickname := req.Nickname
	if userNickname == "" {
		userNickname = cfg.AdminNickname
	}

	// 1. 设置 SSE 响应头
	w.Header().Set("Content-Type", "text/event-stream")
	w.Header().Set("Cache-Control", "no-cache")
	w.Header().Set("Connection", "keep-alive")
	w.Header().Set("X-Accel-Buffering", "no")

	// Trace: message received
	AddTraceEvent("msg_received", req.SessionID, req.UserID, "收到消息: "+req.Message, "success", fmt.Sprintf("platform=%s", req.Source.Platform), 0, nil)

	flusher, ok := w.(http.Flusher)
	if !ok {
		http.Error(w, "Streaming not supported", http.StatusInternalServerError)
		return
	}

	// 1.5 缓存用户消息到会话历史（在 Orchestrator 之前，确保顺序正确：user → assistant）
	// 管理员主会话聚合多平台消息，加平台标签让 LLM 知道消息来源
	userMsgForCache := req.Message
	if req.IsAdmin && req.SessionID == adminSessionID && req.Source.AdapterName != "" {
		userMsgForCache = fmt.Sprintf("[来自 %s] %s", req.Source.AdapterName, req.Message)
	}
	ctxBuilder.CacheMessage(req.SessionID, model.RoleUser, userMsgForCache)

	// 2. 调用 Orchestrator 处理（替代原有的线性处理流程）
	// Orchestrator 内部处理：意图分析 → 子会话分派 → 结果汇总 → 综合生成回复
	eventCh, err := orch.ProcessInput(ctx, orchestrator.ProcessParams{
		UserID:      req.UserID,
		SessionID:   req.SessionID,
		Message:     req.Message,
		Images:      req.Images,
			VideoURLs:   req.VideoURLs,
		VoiceURLs:   req.VoiceURLs,
		Mode:        req.Mode,
		Nickname:    userNickname,
		ChannelType: req.Source.ChannelType,
		ChannelID:   req.Source.ChannelID,
		BotUID:      req.Source.BotUID,
		AdapterName: req.Source.AdapterName,
	})
	if err != nil {
		fallback := getFallbackMessage()
		log.Printf("[chat] ProcessInput 失败，使用降级回复: %v", err)
		fallbackData, _ := json.Marshal(map[string]string{"delta": fallback, "status": "fallback"})
		fmt.Fprintf(w, "data: %s\n\n", fallbackData)
		flusher.Flush()
		return
	}

	messageID := fmt.Sprintf("msg-%d", time.Now().UnixNano())

	// 3. 流式输出 SSE
	var fullContent string
	for event := range eventCh {
		switch event.Type {
		case model.StreamToolProgress:
			tp := event.ToolProgress
			progressData, _ := json.Marshal(map[string]interface{}{
				"type":       "tool_progress",
				"tool_name":  tp.ToolName,
				"status":     tp.Status,
				"progress":   tp.Progress,
				"message":    tp.Message,
				"message_id": messageID,
			})
			fmt.Fprintf(w, "data: %s\n\n", progressData)
			flusher.Flush()

		case model.StreamError:
			log.Printf("[chat] 流式错误: %v", event.Error)
		fallback := getFallbackMessage()
		log.Printf("[chat] 流式事件错误，发送降级回复: %v", event.Error)
		fallbackData, _ := json.Marshal(map[string]string{"delta": fallback, "status": "fallback"})
		fmt.Fprintf(w, "data: %s\n\n", fallbackData)
			flusher.Flush()
			return

		case model.StreamDelta:
			fullContent += event.Delta
			deltaData, _ := json.Marshal(map[string]string{
				"delta":      event.Delta,
				"message_id": messageID,
			})
			fmt.Fprintf(w, "data: %s\n\n", deltaData)
			flusher.Flush()

		case model.StreamSegments:
			// 发送断句信息
			segData, _ := json.Marshal(map[string]interface{}{
				"message_id": messageID,
				"mode":       req.Mode,
				"segments":   event.Segments,
			})
			fmt.Fprintf(w, "data: %s\n\n", segData)
			flusher.Flush()

		case model.StreamReview:
			// 发送审查后的结构化消息（动作消息 + 聊天消息）
			reviewData, _ := json.Marshal(map[string]interface{}{
				"message_id":      messageID,
				"review_messages": event.ReviewMessages,
			})
			fmt.Fprintf(w, "data: %s\n\n", reviewData)
			flusher.Flush()

		case model.StreamDone:
			// 下发结束标记
			endData, _ := json.Marshal(map[string]interface{}{
				"message_id": messageID,
				"mode":       req.Mode,
				"done":       true,
			})
			fmt.Fprintf(w, "data: %s\n\n", endData)
			flusher.Flush()
			fmt.Fprintf(w, "data: [DONE]\n\n")
			flusher.Flush()
		}
	}

	// 4. 对话完成后触发昔涟的自主思考（事件驱动，非定时）
	if thinker != nil {
		thinker.TriggerPostChatThink()
	}

}


// handleMemorySearch 处理记忆搜索请求
func handleMemorySearch(
	w http.ResponseWriter,
	r *http.Request,
	memRetriever *memory.Retriever,
) {
	if r.Method != http.MethodGet {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	userID := r.URL.Query().Get("user_id")
	if userID == "" {
		http.Error(w, "缺少 user_id 参数", http.StatusBadRequest)
		return
	}

	query := r.URL.Query().Get("q")
	if query == "" {
		http.Error(w, "缺少 q 参数", http.StatusBadRequest)
		return
	}

	if memRetriever == nil {
		log.Printf("[memory] 记忆检索器未初始化: 数据库不可用")
		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(map[string]interface{}{
			"user_id":   userID,
			"query":     query,
			"memories":  []interface{}{},
			"error":     "记忆系统未就绪",
			"errorType": "memory_store_unavailable",
			"hint":      "PostgreSQL 数据库不可用，请检查数据库连接配置",
		})
		return
	}

	ctx := r.Context()
	memories, err := memRetriever.Retrieve(ctx, userID, query)
	if err != nil {
		log.Printf("[memory] 检索失败: %v", err)
		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(map[string]interface{}{
			"user_id":   userID,
			"query":     query,
			"memories":  []interface{}{},
			"error":     fmt.Sprintf("检索失败: %v", err),
			"errorType": "retrieve_failed",
		})
		return
	}

	if memories == nil {
		memories = []memory.MemoryEntry{}
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]interface{}{
		"user_id":  userID,
		"query":    query,
		"memories": memories,
		"total":    len(memories),
	})
}

// handleMemoryCRUD 处理记忆的 CRUD 操作
func handleMemoryCRUD(
	w http.ResponseWriter,
	r *http.Request,
	memStore *memory.Store,
	memExtractor *memory.Extractor,
) {
	switch r.Method {
	case http.MethodGet:
		// 列出用户的所有记忆
		userID := r.URL.Query().Get("user_id")
		if userID == "" {
			http.Error(w, "缺少 user_id 参数", http.StatusBadRequest)
			return
		}

		if memStore == nil {
			log.Printf("[memory] 记忆存储未初始化: 数据库不可用")
			w.Header().Set("Content-Type", "application/json")
			json.NewEncoder(w).Encode(map[string]interface{}{
				"user_id":   userID,
				"memories":  []interface{}{},
				"error":     "记忆系统未就绪",
				"errorType": "memory_store_unavailable",
				"hint":      "PostgreSQL 数据库不可用，请检查数据库连接配置",
			})
			return
		}

		ctx := r.Context()
		memories, err := memStore.Query(ctx, model.MemoryQuery{
			UserID: userID,
			Limit:  50,
		})
		if err != nil {
			log.Printf("[memory] 查询失败: %v", err)
			w.Header().Set("Content-Type", "application/json")
			json.NewEncoder(w).Encode(map[string]interface{}{
				"user_id":   userID,
				"memories":  []interface{}{},
				"error":     fmt.Sprintf("查询失败: %v", err),
				"errorType": "query_failed",
			})
			return
		}

		if memories == nil {
			memories = []model.MemoryEntry{}
		}

		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(map[string]interface{}{
			"user_id":  userID,
			"memories": memories,
			"total":    len(memories),
		})

	case http.MethodDelete:
		// 删除单条记忆: DELETE /api/v1/memory?id=xxx
		memoryID := r.URL.Query().Get("id")
		if memoryID == "" {
			http.Error(w, "缺少 id 参数", http.StatusBadRequest)
			return
		}

		if memStore == nil {
			log.Printf("[memory] 记忆存储未初始化: 无法删除")
			w.Header().Set("Content-Type", "application/json")
			w.WriteHeader(http.StatusServiceUnavailable)
			json.NewEncoder(w).Encode(map[string]interface{}{
				"error":     "记忆系统未就绪",
				"errorType": "memory_store_unavailable",
				"hint":      "PostgreSQL 数据库不可用，请检查数据库连接配置",
			})
			return
		}

		ctx := r.Context()
		if err := memStore.Delete(ctx, memoryID); err != nil {
			log.Printf("[memory] 删除失败: %v", err)
			w.Header().Set("Content-Type", "application/json")
			w.WriteHeader(http.StatusInternalServerError)
			json.NewEncoder(w).Encode(map[string]interface{}{
				"error":     fmt.Sprintf("删除失败: %v", err),
				"errorType": "delete_failed",
			})
			return
		}

		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(map[string]interface{}{
			"status":    "deleted",
			"memory_id": memoryID,
		})

	case http.MethodPost:
		// 手动添加记忆
		var req struct {
			UserID   string `json:"user_id"`
			Content  string `json:"content"`
			Category string `json:"category"`
			Priority int    `json:"priority"`
		}
		if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
			http.Error(w, "无效的请求体", http.StatusBadRequest)
			return
		}

		if req.UserID == "" || req.Content == "" {
			http.Error(w, "缺少 user_id 或 content", http.StatusBadRequest)
			return
		}

		if req.Category == "" {
			req.Category = "other"
		}
		if req.Priority <= 0 {
			req.Priority = 1
		}

		if memStore == nil {
			log.Printf("[memory] 记忆存储未初始化: 无法保存")
			w.Header().Set("Content-Type", "application/json")
			w.WriteHeader(http.StatusServiceUnavailable)
			json.NewEncoder(w).Encode(map[string]interface{}{
				"error":     "记忆系统未就绪",
				"errorType": "memory_store_unavailable",
				"hint":      "PostgreSQL 数据库不可用，请检查数据库连接配置",
			})
			return
		}

		entry := &model.MemoryEntry{
			UserID:   req.UserID,
			Content:  req.Content,
			Category: model.MemoryCategory(req.Category),
			Priority: model.MemoryPriority(req.Priority),
		}

		ctx := r.Context()
		if err := memStore.Save(ctx, entry); err != nil {
			log.Printf("[memory] 保存失败: %v", err)
			w.Header().Set("Content-Type", "application/json")
			w.WriteHeader(http.StatusInternalServerError)
			json.NewEncoder(w).Encode(map[string]interface{}{
				"error":     fmt.Sprintf("保存失败: %v", err),
				"errorType": "save_failed",
			})
			return
		}

		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusCreated)
		json.NewEncoder(w).Encode(map[string]interface{}{
			"status": "saved",
			"memory": entry,
		})

	default:
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
	}

}
