package app

import (
	"bufio"
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"strings"
	"time"

	"git.echol.cn/loser/st/server/global"
	"git.echol.cn/loser/st/server/model/app"
	"git.echol.cn/loser/st/server/model/app/request"
	"git.echol.cn/loser/st/server/model/app/response"
	"gorm.io/datatypes"
	"gorm.io/gorm"
)

type ConversationService struct{}

// CreateConversation 创建对话
func (s *ConversationService) CreateConversation(userID uint, req *request.CreateConversationRequest) (*response.ConversationResponse, error) {
	// 验证角色卡是否存在且有权访问
	var character app.AICharacter
	err := global.GVA_DB.Where("id = ? AND (user_id = ? OR is_public = ?)", req.CharacterID, userID, true).
		First(&character).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errors.New("角色卡不存在或无权访问")
		}
		return nil, err
	}

	// 生成对话标题
	title := req.Title
	if title == "" {
		title = "与 " + character.Name + " 的对话"
	}

	// 获取默认 AI 配置
	var aiConfig app.AIConfig
	err = global.GVA_DB.Where("is_active = ?", true).
		Order("is_default DESC, created_at DESC").
		First(&aiConfig).Error

	// 设置 AI 配置
	aiProvider := req.AIProvider
	model := req.Model

	if err == nil {
		// 如果找到了默认配置,使用它
		if aiProvider == "" {
			aiProvider = aiConfig.Provider
		}
		if model == "" {
			model = aiConfig.DefaultModel
		}
		global.GVA_LOG.Info(fmt.Sprintf("创建对话使用 AI 配置: %s (Provider: %s, Model: %s)", aiConfig.Name, aiProvider, model))
	} else {
		// 如果没有找到配置,使用默认值
		if aiProvider == "" {
			aiProvider = "openai"
		}
		if model == "" {
			model = "gpt-4"
		}
		global.GVA_LOG.Warn("未找到默认 AI 配置,使用硬编码默认值")
	}

	// 创建对话
	conversation := app.Conversation{
		UserID:           userID,
		CharacterID:      req.CharacterID,
		Title:            title,
		PresetID:         req.PresetID,
		WorldbookID:      req.WorldbookID,
		WorldbookEnabled: req.WorldbookEnabled,
		AIProvider:       aiProvider,
		Model:            model,
		Settings:         datatypes.JSON("{}"),
	}

	err = global.GVA_DB.Create(&conversation).Error
	if err != nil {
		return nil, err
	}

	// 如果角色有开场白,创建开场白消息
	if character.FirstMes != "" {
		// 获取用户信息
		var user app.AppUser
		err = global.GVA_DB.Where("id = ?", userID).First(&user).Error
		if err != nil {
			global.GVA_LOG.Warn(fmt.Sprintf("获取用户信息失败: %v", err))
		}
		userName := user.Username
		if userName == "" {
			userName = user.NickName
		}

		// 【重要】不再应用正则脚本处理开场白，保留原始内容
		// 让前端来处理 <Status_block> 和 <maintext> 的渲染
		processedFirstMes := character.FirstMes
		global.GVA_LOG.Info(fmt.Sprintf("[开场白] 保留原始内容，长度=%d", len(processedFirstMes)))

		firstMessage := app.Message{
			ConversationID: conversation.ID,
			Role:           "assistant",
			Content:        processedFirstMes,
			TokenCount:     len(processedFirstMes) / 4,
		}
		err = global.GVA_DB.Create(&firstMessage).Error
		if err != nil {
			global.GVA_LOG.Warn(fmt.Sprintf("创建开场白消息失败: %v", err))
		} else {
			// 更新对话统计
			conversation.MessageCount = 1
			conversation.TokenCount = firstMessage.TokenCount
			global.GVA_DB.Model(&conversation).Updates(map[string]interface{}{
				"message_count": 1,
				"token_count":   firstMessage.TokenCount,
			})
		}
	}

	resp := response.ToConversationResponse(&conversation)
	return &resp, nil
}

// GetConversationList 获取对话列表
func (s *ConversationService) GetConversationList(userID uint, req *request.GetConversationListRequest) (*response.ConversationListResponse, error) {
	var conversations []app.Conversation
	var total int64

	db := global.GVA_DB.Model(&app.Conversation{}).Where("user_id = ?", userID)

	// 统计总数
	err := db.Count(&total).Error
	if err != nil {
		return nil, err
	}

	// 分页查询
	offset := (req.Page - 1) * req.PageSize
	err = db.Order("updated_at DESC").Offset(offset).Limit(req.PageSize).Find(&conversations).Error
	if err != nil {
		return nil, err
	}

	// 收集所有角色ID
	characterIDs := make([]uint, 0, len(conversations))
	for _, conv := range conversations {
		characterIDs = append(characterIDs, conv.CharacterID)
	}

	// 批量查询角色信息（只查询必要字段）
	var characters []app.AICharacter
	if len(characterIDs) > 0 {
		err = global.GVA_DB.Select("id, name, avatar, description, created_at, updated_at").
			Where("id IN ?", characterIDs).
			Find(&characters).Error
		if err != nil {
			return nil, err
		}
	}

	// 创建角色ID到角色的映射
	characterMap := make(map[uint]*app.AICharacter)
	for i := range characters {
		characterMap[characters[i].ID] = &characters[i]
	}

	// 转换响应（使用轻量级结构）
	list := make([]response.ConversationListItemResponse, len(conversations))
	for i, conv := range conversations {
		character := characterMap[conv.CharacterID]
		list[i] = response.ToConversationListItemResponse(&conv, character)
	}

	return &response.ConversationListResponse{
		List:     list,
		Total:    total,
		Page:     req.Page,
		PageSize: req.PageSize,
	}, nil
}

// GetConversationByID 获取对话详情
func (s *ConversationService) GetConversationByID(userID, conversationID uint) (*response.ConversationResponse, error) {
	var conversation app.Conversation

	err := global.GVA_DB.Where("id = ? AND user_id = ?", conversationID, userID).
		First(&conversation).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errors.New("对话不存在或无权访问")
		}
		return nil, err
	}

	resp := response.ToConversationResponse(&conversation)
	return &resp, nil
}

// UpdateConversationSettings 更新对话设置
func (s *ConversationService) UpdateConversationSettings(userID, conversationID uint, req *request.UpdateConversationSettingsRequest) error {
	var conversation app.Conversation
	err := global.GVA_DB.Where("id = ? AND user_id = ?", conversationID, userID).First(&conversation).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return errors.New("对话不存在或无权访问")
		}
		return err
	}

	updates := make(map[string]interface{})

	// 更新设置
	if req.Settings != nil {
		settingsJSON, err := json.Marshal(req.Settings)
		if err != nil {
			return err
		}
		updates["settings"] = datatypes.JSON(settingsJSON)
	}

	// 更新世界书ID
	if req.WorldbookID != nil {
		updates["worldbook_id"] = req.WorldbookID
	}

	// 更新世界书启用状态
	if req.WorldbookEnabled != nil {
		updates["worldbook_enabled"] = *req.WorldbookEnabled
	}

	if len(updates) == 0 {
		return nil
	}

	return global.GVA_DB.Model(&conversation).Updates(updates).Error
}

// DeleteConversation 删除对话
func (s *ConversationService) DeleteConversation(userID, conversationID uint) error {
	// 开启事务
	return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
		// 删除对话的所有消息
		err := tx.Where("conversation_id = ?", conversationID).Delete(&app.Message{}).Error
		if err != nil {
			return err
		}

		// 删除对话
		result := tx.Where("id = ? AND user_id = ?", conversationID, userID).Delete(&app.Conversation{})
		if result.Error != nil {
			return result.Error
		}
		if result.RowsAffected == 0 {
			return errors.New("对话不存在或无权删除")
		}

		return nil
	})
}

// GetMessageList 获取消息列表
func (s *ConversationService) GetMessageList(userID, conversationID uint, req *request.GetMessageListRequest) (*response.MessageListResponse, error) {
	// 验证对话权限
	var conversation app.Conversation
	err := global.GVA_DB.Where("id = ? AND user_id = ?", conversationID, userID).
		First(&conversation).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errors.New("对话不存在或无权访问")
		}
		return nil, err
	}

	var messages []app.Message
	var total int64

	db := global.GVA_DB.Model(&app.Message{}).Where("conversation_id = ?", conversationID)

	// 统计总数
	err = db.Count(&total).Error
	if err != nil {
		return nil, err
	}

	// 分页查询
	offset := (req.Page - 1) * req.PageSize
	err = db.Order("created_at ASC").Offset(offset).Limit(req.PageSize).Find(&messages).Error
	if err != nil {
		return nil, err
	}

	// 转换响应
	list := make([]response.MessageResponse, len(messages))
	for i, msg := range messages {
		list[i] = response.ToMessageResponse(&msg)
	}

	return &response.MessageListResponse{
		List:     list,
		Total:    total,
		Page:     req.Page,
		PageSize: req.PageSize,
	}, nil
}

// SendMessage 发送消息并获取 AI 回复
func (s *ConversationService) SendMessage(userID, conversationID uint, req *request.SendMessageRequest) (*response.MessageResponse, error) {
	// 验证对话权限
	var conversation app.Conversation
	err := global.GVA_DB.Where("id = ? AND user_id = ?", conversationID, userID).
		First(&conversation).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errors.New("对话不存在或无权访问")
		}
		return nil, err
	}

	// 获取角色卡信息
	var character app.AICharacter
	err = global.GVA_DB.Where("id = ?", conversation.CharacterID).First(&character).Error
	if err != nil {
		return nil, errors.New("角色卡不存在")
	}

	// 获取用户信息
	var user app.AppUser
	err = global.GVA_DB.Where("id = ?", userID).First(&user).Error
	if err != nil {
		global.GVA_LOG.Warn(fmt.Sprintf("获取用户信息失败: %v", err))
	}
	userName := user.Username
	if userName == "" {
		userName = user.NickName
	}

	// 应用输入阶段的正则脚本 (Placement 0)
	processedContent := req.Content
	var regexService RegexScriptService
	global.GVA_LOG.Info(fmt.Sprintf("查询输入阶段正则脚本: userID=%d, placement=0, charID=%d", userID, conversation.CharacterID))
	inputScripts, err := regexService.GetScriptsForPlacement(userID, 0, &conversation.CharacterID, nil)
	if err != nil {
		global.GVA_LOG.Error(fmt.Sprintf("查询输入阶段正则脚本失败: %v", err))
	} else {
		global.GVA_LOG.Info(fmt.Sprintf("找到 %d 个输入阶段正则脚本", len(inputScripts)))
		if len(inputScripts) > 0 {
			processedContent = regexService.ExecuteScripts(inputScripts, processedContent, userName, character.Name)
			global.GVA_LOG.Info(fmt.Sprintf("应用了 %d 个输入阶段正则脚本，原文: %s, 处理后: %s", len(inputScripts), req.Content, processedContent))
		}
	}

	// 保存用户消息
	userMessage := app.Message{
		ConversationID: conversationID,
		Role:           "user",
		Content:        processedContent,
		TokenCount:     len(processedContent) / 4, // 简单估算
	}

	err = global.GVA_DB.Create(&userMessage).Error
	if err != nil {
		return nil, err
	}

	// 获取完整对话历史（context 管理由 callAIService 内部处理）
	var messages []app.Message
	err = global.GVA_DB.Where("conversation_id = ?", conversationID).
		Order("created_at ASC").
		Find(&messages).Error
	if err != nil {
		return nil, err
	}

	// 调用 AI 服务获取回复
	aiResponse, err := s.callAIService(conversation, character, messages)
	if err != nil {
		return nil, err
	}

	// 保存 AI 回复
	assistantMessage := app.Message{
		ConversationID: conversationID,
		Role:           "assistant",
		Content:        aiResponse,
		TokenCount:     len(aiResponse) / 4, // 简单估算
	}

	err = global.GVA_DB.Create(&assistantMessage).Error
	if err != nil {
		return nil, err
	}

	// 更新对话统计
	err = global.GVA_DB.Model(&conversation).Updates(map[string]interface{}{
		"message_count": gorm.Expr("message_count + ?", 2),
		"token_count":   gorm.Expr("token_count + ?", userMessage.TokenCount+assistantMessage.TokenCount),
	}).Error
	if err != nil {
		return nil, err
	}

	// 提取并保存变量 (从 AI 回复中提取 {{setvar::key::value}})
	newVars, cleanedContent := regexService.ExtractSetVars(assistantMessage.Content)
	if len(newVars) > 0 {
		// 加载现有变量
		var existingVars map[string]string
		if len(conversation.Variables) > 0 {
			json.Unmarshal(conversation.Variables, &existingVars)
		}
		if existingVars == nil {
			existingVars = make(map[string]string)
		}

		// 合并新变量
		for k, v := range newVars {
			existingVars[k] = v
		}

		// 保存回数据库
		varsJSON, _ := json.Marshal(existingVars)
		global.GVA_DB.Model(&conversation).Update("variables", datatypes.JSON(varsJSON))
		global.GVA_LOG.Info(fmt.Sprintf("提取并保存了 %d 个变量: %v", len(newVars), newVars))
	}

	// 先替换 {{getvar::}} 为实际变量值（在应用正则脚本之前）
	var currentVars map[string]string
	if len(conversation.Variables) > 0 {
		json.Unmarshal(conversation.Variables, &currentVars)
	}
	displayContent := cleanedContent // 使用清理后的内容（移除了 {{setvar::}}）
	if currentVars != nil {
		displayContent = regexService.SubstituteGetVars(displayContent, currentVars)
		global.GVA_LOG.Info(fmt.Sprintf("替换了 {{getvar::}} 变量"))
	}

	// 注意：此时 displayContent 中的 <Status_block> 已经被 Placement 1 正则脚本
	// 替换成了包含 YAML 数据的 HTML 模板，所以不需要再提取和保护
	// 直接返回给前端即可

	resp := response.ToMessageResponse(&assistantMessage)
	resp.Content = displayContent // 使用处理后的显示内容
	return &resp, nil
}

// callAIService 调用 AI 服务
func (s *ConversationService) callAIService(conversation app.Conversation, character app.AICharacter, messages []app.Message) (string, error) {
	// 获取 AI 配置

	var aiConfig app.AIConfig
	var err error

	// 1. 尝试从对话设置中获取指定的 AI 配置 ID
	var configID uint
	if len(conversation.Settings) > 0 {
		var settings map[string]interface{}
		if err := json.Unmarshal(conversation.Settings, &settings); err == nil {
			if id, ok := settings["aiConfigId"].(float64); ok {
				configID = uint(id)
			}
		}
	}

	if configID > 0 {
		// 使用用户指定的 AI 配置
		global.GVA_LOG.Info(fmt.Sprintf("使用用户指定的 AI 配置 ID: %d", configID))
		err = global.GVA_DB.Where("id = ? AND is_active = ?", configID, true).First(&aiConfig).Error
		if err != nil {
			global.GVA_LOG.Error(fmt.Sprintf("未找到指定的 AI 配置 ID: %d, 错误: %v", configID, err))
		}
	}

	if err != nil || configID == 0 {
		// 使用默认 AI 配置
		global.GVA_LOG.Info("尝试使用默认 AI 配置")
		err = global.GVA_DB.Where("is_active = ?", true).
			Order("is_default DESC, created_at DESC").
			First(&aiConfig).Error
		if err != nil {
			global.GVA_LOG.Error(fmt.Sprintf("未找到默认 AI 配置, 错误: %v", err))
		}
	}

	if err != nil {
		return "", errors.New("未找到可用的 AI 配置，请在管理后台添加并激活 AI 配置")
	}

	global.GVA_LOG.Info(fmt.Sprintf("使用 AI 配置: %s (Provider: %s, Model: %s)", aiConfig.Name, aiConfig.Provider, aiConfig.DefaultModel))

	// 2. 尝试从对话设置中获取预设 ID 并加载预设参数
	var preset *app.AIPreset
	var presetID uint
	if len(conversation.Settings) > 0 {
		var settings map[string]interface{}
		if err := json.Unmarshal(conversation.Settings, &settings); err == nil {
			if id, ok := settings["presetId"].(float64); ok {
				presetID = uint(id)
			}
		}
	}

	// 加载预设
	if presetID > 0 {
		var loadedPreset app.AIPreset
		if err := global.GVA_DB.First(&loadedPreset, presetID).Error; err == nil {
			preset = &loadedPreset
			global.GVA_LOG.Info(fmt.Sprintf("使用预设: %s (Temperature: %.2f, TopP: %.2f)", preset.Name, preset.Temperature, preset.TopP))

			// 增加预设使用次数
			global.GVA_DB.Model(&preset).Update("use_count", gorm.Expr("use_count + ?", 1))
		} else {
			global.GVA_LOG.Warn(fmt.Sprintf("未找到预设 ID: %d, 使用默认参数", presetID))
		}
	}

	// 构建消息列表（含 context 预算管理）
	var presetSysPrompt string
	if preset != nil {
		presetSysPrompt = preset.SystemPrompt
	}
	wbEngine := &WorldbookEngine{}
	apiMessages := s.buildAPIMessagesWithContextManagement(
		messages, character, presetSysPrompt, wbEngine, conversation, &aiConfig, preset,
	)

	// 从 apiMessages 中提取 systemPrompt，供 Anthropic 独立参数使用
	systemPrompt := ""
	if len(apiMessages) > 0 && apiMessages[0]["role"] == "system" {
		systemPrompt = apiMessages[0]["content"]
	}

	// 打印发送给AI的完整内容
	global.GVA_LOG.Info("========== 发送给AI的完整内容 ==========")
	global.GVA_LOG.Info(fmt.Sprintf("系统提示词长度: %d 字符", len(systemPrompt)))
	global.GVA_LOG.Info(fmt.Sprintf("历史消息条数: %d", len(apiMessages)-1))
	global.GVA_LOG.Info("==========================================")

	// 确定使用的模型：如果用户在设置中指定了AI配置，则使用该配置的默认模型
	// 否则使用对话创建时的模型（向后兼容）
	model := aiConfig.DefaultModel
	if model == "" {
		// 如果AI配置没有默认模型，才使用对话表中的模型
		model = conversation.Model
	}
	if model == "" {
		// 最后的兜底
		model = "gpt-4"
	}

	global.GVA_LOG.Info(fmt.Sprintf("使用模型: %s (来源: AI配置 %s)", model, aiConfig.Name))

	// 根据提供商调用不同的 API
	var aiResponse string

	switch aiConfig.Provider {
	case "openai", "custom":
		aiResponse, err = s.callOpenAIAPI(&aiConfig, model, apiMessages, preset)
	case "anthropic":
		aiResponse, err = s.callAnthropicAPI(&aiConfig, model, apiMessages, systemPrompt, preset)
	default:
		return "", fmt.Errorf("不支持的 AI 提供商: %s", aiConfig.Provider)
	}

	// 打印AI返回的完整内容
	if err != nil {
		global.GVA_LOG.Error(fmt.Sprintf("========== AI返回错误 ==========\n%v\n==========================================", err))
		return "", err
	}
	global.GVA_LOG.Info(fmt.Sprintf("========== AI返回的完整内容 ==========\n%s\n==========================================", aiResponse))

	// 应用输出阶段的正则脚本 (Placement 1)
	// 这里会把 <Status_block> 替换成 HTML 模板，并注入 YAML 数据
	var regexService RegexScriptService
	global.GVA_LOG.Info(fmt.Sprintf("查询输出阶段正则脚本: userID=%d, placement=1, charID=%d", conversation.UserID, conversation.CharacterID))
	outputScripts, err := regexService.GetScriptsForPlacement(conversation.UserID, 1, &conversation.CharacterID, nil)
	if err != nil {
		global.GVA_LOG.Error(fmt.Sprintf("查询输出阶段正则脚本失败: %v", err))
	} else {
		global.GVA_LOG.Info(fmt.Sprintf("找到 %d 个输出阶段正则脚本", len(outputScripts)))
		if len(outputScripts) > 0 {
			// 获取用户信息
			var user app.AppUser
			err = global.GVA_DB.Where("id = ?", conversation.UserID).First(&user).Error
			userName := ""
			if err == nil {
				userName = user.Username
				if userName == "" {
					userName = user.NickName
				}
			}

			originalResponse := aiResponse
			aiResponse = regexService.ExecuteScripts(outputScripts, aiResponse, userName, character.Name)
			global.GVA_LOG.Info(fmt.Sprintf("应用了 %d 个输出阶段正则脚本，原始长度: %d, 处理后长度: %d", len(outputScripts), len(originalResponse), len(aiResponse)))
		}
	}

	return aiResponse, nil
}

// buildSystemPrompt 构建系统提示词
func (s *ConversationService) buildSystemPrompt(character app.AICharacter) string {
	prompt := fmt.Sprintf("你是 %s。", character.Name)

	if character.Description != "" {
		prompt += fmt.Sprintf("\n\n描述：%s", character.Description)
	}

	if character.Personality != "" {
		prompt += fmt.Sprintf("\n\n性格：%s", character.Personality)
	}

	if character.Scenario != "" {
		prompt += fmt.Sprintf("\n\n场景：%s", character.Scenario)
	}

	if character.FirstMes != "" {
		prompt += fmt.Sprintf("\n\n开场白：%s", character.FirstMes)
	}

	if character.MesExample != "" {
		prompt += fmt.Sprintf("\n\n对话示例：\n%s", character.MesExample)
	}

	if character.SystemPrompt != "" {
		prompt += fmt.Sprintf("\n\n系统提示：%s", character.SystemPrompt)
	}

	// 处理世界书 (Character Book)
	if len(character.CharacterBook) > 0 {
		var characterBook map[string]interface{}
		if err := json.Unmarshal(character.CharacterBook, &characterBook); err == nil {
			if entries, ok := characterBook["entries"].([]interface{}); ok && len(entries) > 0 {
				prompt += "\n\n世界设定："
				for _, entry := range entries {
					if entryMap, ok := entry.(map[string]interface{}); ok {
						// 默认启用，除非明确设置为false
						enabled := true
						if enabledVal, ok := entryMap["enabled"].(bool); ok {
							enabled = enabledVal
						}
						if !enabled {
							continue
						}
						// 添加世界书条目内容
						if content, ok := entryMap["content"].(string); ok && content != "" {
							prompt += fmt.Sprintf("\n- %s", content)
						}
					}
				}
			}
		}
	}

	prompt += "\n\n请根据以上设定进行角色扮演，保持角色的性格和说话方式。"

	// 应用MVU变量替换
	prompt = s.applyMacroVariables(prompt, character)

	return prompt
}

// applyMacroVariables 应用宏变量替换 (MVU功能)
func (s *ConversationService) applyMacroVariables(text string, character app.AICharacter) string {
	// 获取当前时间
	now := time.Now()

	// 基础变量
	replacements := map[string]string{
		"{{char}}":          character.Name,
		"{{user}}":          "用户", // 可以从用户信息中获取
		"{{time}}":          now.Format("15:04"),
		"{{date}}":          now.Format("2006-01-02"),
		"{{datetime}}":      now.Format("2006-01-02 15:04:05"),
		"{{weekday}}":       s.getWeekdayInChinese(now.Weekday()),
		"{{idle_duration}}": "0分钟",
	}

	// 执行替换
	result := text
	for macro, value := range replacements {
		result = strings.ReplaceAll(result, macro, value)
	}

	return result
}

// getWeekdayInChinese 获取中文星期
func (s *ConversationService) getWeekdayInChinese(weekday time.Weekday) string {
	weekdays := map[time.Weekday]string{
		time.Sunday:    "星期日",
		time.Monday:    "星期一",
		time.Tuesday:   "星期二",
		time.Wednesday: "星期三",
		time.Thursday:  "星期四",
		time.Friday:    "星期五",
		time.Saturday:  "星期六",
	}
	return weekdays[weekday]
}

// SendMessageStream 流式发送消息并获取 AI 回复
func (s *ConversationService) SendMessageStream(ctx context.Context, userID, conversationID uint, req *request.SendMessageRequest, streamChan chan string, doneChan chan bool) error {
	defer close(streamChan)
	defer close(doneChan)

	// 验证对话权限
	var conversation app.Conversation
	err := global.GVA_DB.Where("id = ? AND user_id = ?", conversationID, userID).
		First(&conversation).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return errors.New("对话不存在或无权访问")
		}
		return err
	}

	// 获取角色卡信息
	var character app.AICharacter
	err = global.GVA_DB.Where("id = ?", conversation.CharacterID).First(&character).Error
	if err != nil {
		return errors.New("角色卡不存在")
	}

	// 获取用户信息
	var user app.AppUser
	err = global.GVA_DB.Where("id = ?", userID).First(&user).Error
	if err != nil {
		global.GVA_LOG.Warn(fmt.Sprintf("[流式传输] 获取用户信息失败: %v", err))
	}
	userName := user.Username
	if userName == "" {
		userName = user.NickName
	}

	// 应用输入阶段的正则脚本 (Placement 0)
	processedContent := req.Content
	var regexService RegexScriptService
	global.GVA_LOG.Info(fmt.Sprintf("[流式传输] 查询输入阶段正则脚本: userID=%d, placement=0, charID=%d", userID, conversation.CharacterID))
	inputScripts, err := regexService.GetScriptsForPlacement(userID, 0, &conversation.CharacterID, nil)
	if err != nil {
		global.GVA_LOG.Error(fmt.Sprintf("[流式传输] 查询输入阶段正则脚本失败: %v", err))
	} else {
		global.GVA_LOG.Info(fmt.Sprintf("[流式传输] 找到 %d 个输入阶段正则脚本", len(inputScripts)))
		if len(inputScripts) > 0 {
			processedContent = regexService.ExecuteScripts(inputScripts, processedContent, userName, character.Name)
			global.GVA_LOG.Info(fmt.Sprintf("[流式传输] 应用了 %d 个输入阶段正则脚本", len(inputScripts)))
		}
	}

	// 保存用户消息
	userMessage := app.Message{
		ConversationID: conversationID,
		Role:           "user",
		Content:        processedContent,
		TokenCount:     len(processedContent) / 4,
	}

	err = global.GVA_DB.Create(&userMessage).Error
	if err != nil {
		return err
	}

	// 获取完整对话历史（context 管理由 buildAPIMessagesWithContextManagement 处理）
	var messages []app.Message
	err = global.GVA_DB.Where("conversation_id = ?", conversationID).
		Order("created_at ASC").
		Find(&messages).Error
	if err != nil {
		return err
	}

	// 获取 AI 配置
	var aiConfig app.AIConfig
	var configID uint
	if len(conversation.Settings) > 0 {
		var settings map[string]interface{}
		if err := json.Unmarshal(conversation.Settings, &settings); err == nil {
			if id, ok := settings["aiConfigId"].(float64); ok {
				configID = uint(id)
			}
		}
	}

	if configID > 0 {
		err = global.GVA_DB.Where("id = ? AND is_active = ?", configID, true).First(&aiConfig).Error
	}

	if err != nil || configID == 0 {
		err = global.GVA_DB.Where("is_active = ?", true).
			Order("is_default DESC, created_at DESC").
			First(&aiConfig).Error
	}

	if err != nil {
		return errors.New("未找到可用的 AI 配置")
	}

	// 加载预设
	var streamPreset *app.AIPreset
	var streamPresetID uint
	if len(conversation.Settings) > 0 {
		var settings map[string]interface{}
		if err := json.Unmarshal(conversation.Settings, &settings); err == nil {
			if id, ok := settings["presetId"].(float64); ok {
				streamPresetID = uint(id)
			}
		}
	}
	if streamPresetID > 0 {
		var loadedPreset app.AIPreset
		if err := global.GVA_DB.First(&loadedPreset, streamPresetID).Error; err == nil {
			streamPreset = &loadedPreset
			global.GVA_LOG.Info(fmt.Sprintf("[流式传输] 使用预设: %s (Temperature: %.2f)", streamPreset.Name, streamPreset.Temperature))
			global.GVA_DB.Model(streamPreset).Update("use_count", gorm.Expr("use_count + ?", 1))
		}
	}

	// 构建消息列表（含 context 预算管理）
	var streamPresetSysPrompt string
	if streamPreset != nil {
		streamPresetSysPrompt = streamPreset.SystemPrompt
	}
	streamWbEngine := &WorldbookEngine{}
	apiMessages := s.buildAPIMessagesWithContextManagement(
		messages, character, streamPresetSysPrompt, streamWbEngine, conversation, &aiConfig, streamPreset,
	)

	// 从 apiMessages 中提取 systemPrompt，供 Anthropic 独立参数使用
	systemPrompt := ""
	if len(apiMessages) > 0 && apiMessages[0]["role"] == "system" {
		systemPrompt = apiMessages[0]["content"]
	}

	// 打印发送给AI的完整内容（流式传输）
	global.GVA_LOG.Info("========== [流式传输] 发送给AI的完整内容 ==========")
	global.GVA_LOG.Info(fmt.Sprintf("[流式传输] 系统提示词长度: %d 字符", len(systemPrompt)))
	global.GVA_LOG.Info(fmt.Sprintf("[流式传输] 历史消息条数: %d", len(apiMessages)-1))
	global.GVA_LOG.Info("==========================================")

	// 确定使用的模型
	model := aiConfig.DefaultModel
	if model == "" {
		model = conversation.Model
	}
	if model == "" {
		model = "gpt-4"
	}

	global.GVA_LOG.Info(fmt.Sprintf("[流式传输] 使用模型: %s (Provider: %s)", model, aiConfig.Provider))

	// 调用流式 API
	var fullContent string
	switch aiConfig.Provider {
	case "openai", "custom":
		fullContent, err = s.callOpenAIAPIStream(ctx, &aiConfig, model, apiMessages, streamPreset, streamChan)
	case "anthropic":
		fullContent, err = s.callAnthropicAPIStream(ctx, &aiConfig, model, apiMessages, systemPrompt, streamPreset, streamChan)
	default:
		return fmt.Errorf("不支持的 AI 提供商: %s", aiConfig.Provider)
	}

	if err != nil {
		global.GVA_LOG.Error(fmt.Sprintf("========== [流式传输] AI返回错误 ==========\n%v\n==========================================", err))
		// AI 调用失败，回滚已写入的用户消息，避免孤立记录残留在数据库
		if delErr := global.GVA_DB.Delete(&userMessage).Error; delErr != nil {
			global.GVA_LOG.Error(fmt.Sprintf("[流式传输] 回滚用户消息失败: %v", delErr))
		} else {
			global.GVA_LOG.Info("[流式传输] 已回滚用户消息")
		}
		return err
	}

	// 打印AI返回的完整内容
	global.GVA_LOG.Info(fmt.Sprintf("========== [流式传输] AI返回的完整内容 ==========\n%s\n==========================================", fullContent))

	// 应用输出阶段的正则脚本 (Placement 1)
	global.GVA_LOG.Info(fmt.Sprintf("[流式传输] 查询输出阶段正则脚本: userID=%d, placement=1, charID=%d", userID, conversation.CharacterID))
	outputScripts, err := regexService.GetScriptsForPlacement(userID, 1, &conversation.CharacterID, nil)
	if err != nil {
		global.GVA_LOG.Error(fmt.Sprintf("[流式传输] 查询输出阶段正则脚本失败: %v", err))
	} else {
		global.GVA_LOG.Info(fmt.Sprintf("[流式传输] 找到 %d 个输出阶段正则脚本", len(outputScripts)))
		if len(outputScripts) > 0 {
			fullContent = regexService.ExecuteScripts(outputScripts, fullContent, userName, character.Name)
			global.GVA_LOG.Info(fmt.Sprintf("[流式传输] 应用了 %d 个输出阶段正则脚本", len(outputScripts)))
		}
	}

	// 保存 AI 回复
	assistantMessage := app.Message{
		ConversationID: conversationID,
		Role:           "assistant",
		Content:        fullContent,
		TokenCount:     len(fullContent) / 4,
	}

	err = global.GVA_DB.Create(&assistantMessage).Error
	if err != nil {
		return err
	}

	// 更新对话统计
	err = global.GVA_DB.Model(&conversation).Updates(map[string]interface{}{
		"message_count": gorm.Expr("message_count + ?", 2),
		"token_count":   gorm.Expr("token_count + ?", userMessage.TokenCount+assistantMessage.TokenCount),
	}).Error

	doneChan <- true
	return err
}

// callOpenAIAPIStream 调用 OpenAI API 流式传输
func (s *ConversationService) callOpenAIAPIStream(ctx context.Context, config *app.AIConfig, model string, messages []map[string]string, preset *app.AIPreset, streamChan chan string) (string, error) {
	// 不设 Timeout：生命周期由调用方传入的 ctx 控制（客户端断连时自动取消）
	client := &http.Client{}

	if model == "" {
		model = config.DefaultModel
	}
	if model == "" {
		model = "gpt-4"
	}

	// 应用预设参数
	temperature := 0.7
	maxTokens := 2000
	var topP *float64
	var frequencyPenalty *float64
	var presencePenalty *float64
	var stopSequences []string

	if preset != nil {
		temperature = preset.Temperature
		maxTokens = preset.MaxTokens
		if preset.TopP > 0 {
			topP = &preset.TopP
		}
		if preset.FrequencyPenalty != 0 {
			frequencyPenalty = &preset.FrequencyPenalty
		}
		if preset.PresencePenalty != 0 {
			presencePenalty = &preset.PresencePenalty
		}
		if len(preset.StopSequences) > 0 {
			json.Unmarshal(preset.StopSequences, &stopSequences)
		}
	}

	// 构建请求体，启用流式传输
	requestBody := map[string]interface{}{
		"model":       model,
		"messages":    messages,
		"temperature": temperature,
		"max_tokens":  maxTokens,
		"stream":      true,
	}

	if topP != nil {
		requestBody["top_p"] = *topP
	}
	if frequencyPenalty != nil {
		requestBody["frequency_penalty"] = *frequencyPenalty
	}
	if presencePenalty != nil {
		requestBody["presence_penalty"] = *presencePenalty
	}
	if len(stopSequences) > 0 {
		requestBody["stop"] = stopSequences
	}

	bodyBytes, err := json.Marshal(requestBody)
	if err != nil {
		return "", fmt.Errorf("序列化请求失败: %v", err)
	}

	endpoint := config.BaseURL + "/chat/completions"
	req, err := http.NewRequestWithContext(ctx, "POST", endpoint, bytes.NewBuffer(bodyBytes))
	if err != nil {
		return "", fmt.Errorf("创建请求失败: %v", err)
	}

	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "Bearer "+config.APIKey)

	resp, err := client.Do(req)
	if err != nil {
		// 客户端主动断开时 ctx 被取消，不算真正的错误
		if ctx.Err() != nil {
			return "", nil
		}
		return "", fmt.Errorf("请求失败: %v", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		body, _ := io.ReadAll(resp.Body)
		return "", fmt.Errorf("API 返回错误 %d: %s", resp.StatusCode, string(body))
	}

	// 读取流式响应
	var fullContent strings.Builder
	reader := bufio.NewReader(resp.Body)

	for {
		line, err := reader.ReadString('\n')
		// 先处理本次读到的数据（EOF 时可能仍携带最后一行内容）
		if line != "" {
			trimmed := strings.TrimSpace(line)
			if trimmed != "" && trimmed != "data: [DONE]" && strings.HasPrefix(trimmed, "data: ") {
				data := strings.TrimPrefix(trimmed, "data: ")

				var streamResp struct {
					Choices []struct {
						Delta struct {
							Content string `json:"content"`
						} `json:"delta"`
					} `json:"choices"`
				}

				if jsonErr := json.Unmarshal([]byte(data), &streamResp); jsonErr == nil {
					if len(streamResp.Choices) > 0 {
						content := streamResp.Choices[0].Delta.Content
						if content != "" {
							fullContent.WriteString(content)
							streamChan <- content
						}
					}
				}
			}
		}
		// 再检查读取错误
		if err != nil {
			if err == io.EOF {
				break
			}
			// ctx 被取消（客户端断开）时不算真正的流读取错误
			if ctx.Err() != nil {
				return fullContent.String(), nil
			}
			return "", fmt.Errorf("读取流失败: %v", err)
		}
	}

	return fullContent.String(), nil
}

// callAnthropicAPIStream 调用 Anthropic API 流式传输
func (s *ConversationService) callAnthropicAPIStream(ctx context.Context, config *app.AIConfig, model string, messages []map[string]string, systemPrompt string, preset *app.AIPreset, streamChan chan string) (string, error) {
	// 不设 Timeout：生命周期由调用方传入的 ctx 控制（客户端断连时自动取消）
	client := &http.Client{}

	if model == "" {
		model = config.DefaultModel
	}
	if model == "" {
		model = "claude-3-sonnet-20240229"
	}

	// Anthropic API 不支持 system role
	apiMessages := make([]map[string]string, 0)
	for _, msg := range messages {
		if msg["role"] != "system" {
			apiMessages = append(apiMessages, msg)
		}
	}

	// 应用预设参数
	maxTokens := 2000
	var temperature *float64
	var topP *float64
	var stopSequences []string

	if preset != nil {
		maxTokens = preset.MaxTokens
		if preset.Temperature > 0 {
			temperature = &preset.Temperature
		}
		if preset.TopP > 0 {
			topP = &preset.TopP
		}
		if len(preset.StopSequences) > 0 {
			json.Unmarshal(preset.StopSequences, &stopSequences)
		}
	}

	requestBody := map[string]interface{}{
		"model":      model,
		"messages":   apiMessages,
		"system":     systemPrompt,
		"max_tokens": maxTokens,
		"stream":     true,
	}

	if temperature != nil {
		requestBody["temperature"] = *temperature
	}
	if topP != nil {
		requestBody["top_p"] = *topP
	}
	if len(stopSequences) > 0 {
		requestBody["stop_sequences"] = stopSequences
	}

	bodyBytes, err := json.Marshal(requestBody)
	if err != nil {
		return "", fmt.Errorf("序列化请求失败: %v", err)
	}

	endpoint := config.BaseURL + "/messages"
	req, err := http.NewRequestWithContext(ctx, "POST", endpoint, bytes.NewBuffer(bodyBytes))
	if err != nil {
		return "", fmt.Errorf("创建请求失败: %v", err)
	}

	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("x-api-key", config.APIKey)
	req.Header.Set("anthropic-version", "2023-06-01")

	resp, err := client.Do(req)
	if err != nil {
		// 客户端主动断开时 ctx 被取消，不算真正的错误
		if ctx.Err() != nil {
			return "", nil
		}
		return "", fmt.Errorf("请求失败: %v", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		body, _ := io.ReadAll(resp.Body)
		return "", fmt.Errorf("API 返回错误 %d: %s", resp.StatusCode, string(body))
	}

	// 读取流式响应
	var fullContent strings.Builder
	reader := bufio.NewReader(resp.Body)

	for {
		line, err := reader.ReadString('\n')
		// 先处理本次读到的数据（EOF 时可能仍携带最后一行内容）
		if line != "" {
			trimmed := strings.TrimSpace(line)
			if trimmed != "" && strings.HasPrefix(trimmed, "data: ") {
				data := strings.TrimPrefix(trimmed, "data: ")

				var streamResp struct {
					Type  string `json:"type"`
					Delta struct {
						Type string `json:"type"`
						Text string `json:"text"`
					} `json:"delta"`
				}

				if jsonErr := json.Unmarshal([]byte(data), &streamResp); jsonErr == nil {
					if streamResp.Type == "content_block_delta" && streamResp.Delta.Text != "" {
						fullContent.WriteString(streamResp.Delta.Text)
						streamChan <- streamResp.Delta.Text
					}
				}
			}
		}
		// 再检查读取错误
		if err != nil {
			if err == io.EOF {
				break
			}
			// ctx 被取消（客户端断开）时不算真正的流读取错误
			if ctx.Err() != nil {
				return fullContent.String(), nil
			}
			return "", fmt.Errorf("读取流失败: %v", err)
		}
	}

	return fullContent.String(), nil
}

// RegenerateMessage 重新生成最后一条 AI 回复（非流式）
func (s *ConversationService) RegenerateMessage(userID, conversationID uint) (*response.MessageResponse, error) {
	var conversation app.Conversation
	err := global.GVA_DB.Where("id = ? AND user_id = ?", conversationID, userID).First(&conversation).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errors.New("对话不存在或无权访问")
		}
		return nil, err
	}

	var character app.AICharacter
	err = global.GVA_DB.Where("id = ?", conversation.CharacterID).First(&character).Error
	if err != nil {
		return nil, errors.New("角色卡不存在")
	}

	// 删除最后一条 AI 回复
	var lastAssistantMsg app.Message
	if err = global.GVA_DB.Where("conversation_id = ? AND role = ?", conversationID, "assistant").
		Order("created_at DESC").First(&lastAssistantMsg).Error; err == nil {
		global.GVA_DB.Delete(&lastAssistantMsg)
		global.GVA_DB.Model(&conversation).Updates(map[string]interface{}{
			"message_count": gorm.Expr("GREATEST(message_count - 1, 0)"),
			"token_count":   gorm.Expr("GREATEST(token_count - ?, 0)", lastAssistantMsg.TokenCount),
		})
	}

	// 获取删除后的完整消息历史（context 管理由 callAIService 内部处理）
	var messages []app.Message
	err = global.GVA_DB.Where("conversation_id = ?", conversationID).
		Order("created_at ASC").Find(&messages).Error
	if err != nil {
		return nil, err
	}
	if len(messages) == 0 {
		return nil, errors.New("没有可用的消息历史")
	}

	aiResponse, err := s.callAIService(conversation, character, messages)
	if err != nil {
		return nil, err
	}

	assistantMessage := app.Message{
		ConversationID: conversationID,
		Role:           "assistant",
		Content:        aiResponse,
		TokenCount:     len(aiResponse) / 4,
	}
	if err = global.GVA_DB.Create(&assistantMessage).Error; err != nil {
		return nil, err
	}

	global.GVA_DB.Model(&conversation).Updates(map[string]interface{}{
		"message_count": gorm.Expr("message_count + ?", 1),
		"token_count":   gorm.Expr("token_count + ?", assistantMessage.TokenCount),
	})

	resp := response.ToMessageResponse(&assistantMessage)
	return &resp, nil
}

// RegenerateMessageStream 流式重新生成最后一条 AI 回复
func (s *ConversationService) RegenerateMessageStream(ctx context.Context, userID, conversationID uint, streamChan chan string, doneChan chan bool) error {
	defer close(streamChan)
	defer close(doneChan)

	var conversation app.Conversation
	err := global.GVA_DB.Where("id = ? AND user_id = ?", conversationID, userID).First(&conversation).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return errors.New("对话不存在或无权访问")
		}
		return err
	}

	var character app.AICharacter
	err = global.GVA_DB.Where("id = ?", conversation.CharacterID).First(&character).Error
	if err != nil {
		return errors.New("角色卡不存在")
	}

	// 删除最后一条 AI 回复
	var lastAssistantMsg app.Message
	if err = global.GVA_DB.Where("conversation_id = ? AND role = ?", conversationID, "assistant").
		Order("created_at DESC").First(&lastAssistantMsg).Error; err == nil {
		global.GVA_DB.Delete(&lastAssistantMsg)
		global.GVA_DB.Model(&conversation).Updates(map[string]interface{}{
			"message_count": gorm.Expr("GREATEST(message_count - 1, 0)"),
			"token_count":   gorm.Expr("GREATEST(token_count - ?, 0)", lastAssistantMsg.TokenCount),
		})
	}

	// 获取删除后的完整消息历史（context 管理由 buildAPIMessagesWithContextManagement 处理）
	var messages []app.Message
	err = global.GVA_DB.Where("conversation_id = ?", conversationID).
		Order("created_at ASC").Find(&messages).Error
	if err != nil {
		return err
	}
	if len(messages) == 0 {
		return errors.New("没有可用的消息历史")
	}

	// 获取 AI 配置
	var aiConfig app.AIConfig
	var configID uint
	if len(conversation.Settings) > 0 {
		var settings map[string]interface{}
		if err := json.Unmarshal(conversation.Settings, &settings); err == nil {
			if id, ok := settings["aiConfigId"].(float64); ok {
				configID = uint(id)
			}
		}
	}
	if configID > 0 {
		err = global.GVA_DB.Where("id = ? AND is_active = ?", configID, true).First(&aiConfig).Error
	}
	if err != nil || configID == 0 {
		err = global.GVA_DB.Where("is_active = ?", true).
			Order("is_default DESC, created_at DESC").
			First(&aiConfig).Error
	}
	if err != nil {
		return errors.New("未找到可用的 AI 配置")
	}

	// 加载预设
	var preset *app.AIPreset
	var presetID uint
	if len(conversation.Settings) > 0 {
		var settings map[string]interface{}
		if err := json.Unmarshal(conversation.Settings, &settings); err == nil {
			if id, ok := settings["presetId"].(float64); ok {
				presetID = uint(id)
			}
		}
	}
	if presetID > 0 {
		var loadedPreset app.AIPreset
		if err := global.GVA_DB.First(&loadedPreset, presetID).Error; err == nil {
			preset = &loadedPreset
		}
	}

	// 构建消息列表（含 context 预算管理）
	var regenPresetSysPrompt string
	if preset != nil {
		regenPresetSysPrompt = preset.SystemPrompt
	}
	regenWbEngine := &WorldbookEngine{}
	apiMessages := s.buildAPIMessagesWithContextManagement(
		messages, character, regenPresetSysPrompt, regenWbEngine, conversation, &aiConfig, preset,
	)

	// 从 apiMessages 中提取 systemPrompt，供 Anthropic 独立参数使用
	systemPrompt := ""
	if len(apiMessages) > 0 && apiMessages[0]["role"] == "system" {
		systemPrompt = apiMessages[0]["content"]
	}

	model := aiConfig.DefaultModel
	if model == "" {
		model = conversation.Model
	}
	if model == "" {
		model = "gpt-4"
	}

	var fullContent string
	switch aiConfig.Provider {
	case "openai", "custom":
		fullContent, err = s.callOpenAIAPIStream(ctx, &aiConfig, model, apiMessages, preset, streamChan)
	case "anthropic":
		fullContent, err = s.callAnthropicAPIStream(ctx, &aiConfig, model, apiMessages, systemPrompt, preset, streamChan)
	default:
		return fmt.Errorf("不支持的 AI 提供商: %s", aiConfig.Provider)
	}

	if err != nil {
		// AI 调用失败，恢复刚才删除的 assistant 消息，避免数据永久丢失
		if lastAssistantMsg.ID > 0 {
			if restoreErr := global.GVA_DB.Unscoped().Model(&lastAssistantMsg).Update("deleted_at", nil).Error; restoreErr != nil {
				global.GVA_LOG.Error(fmt.Sprintf("[重新生成] 恢复 assistant 消息失败: %v", restoreErr))
			} else {
				// 回滚 conversation 统计
				global.GVA_DB.Model(&conversation).Updates(map[string]interface{}{
					"message_count": gorm.Expr("message_count + 1"),
					"token_count":   gorm.Expr("token_count + ?", lastAssistantMsg.TokenCount),
				})
				global.GVA_LOG.Info("[重新生成] 已恢复 assistant 消息")
			}
		}
		return err
	}

	assistantMessage := app.Message{
		ConversationID: conversationID,
		Role:           "assistant",
		Content:        fullContent,
		TokenCount:     len(fullContent) / 4,
	}
	if err = global.GVA_DB.Create(&assistantMessage).Error; err != nil {
		return err
	}

	global.GVA_DB.Model(&conversation).Updates(map[string]interface{}{
		"message_count": gorm.Expr("message_count + ?", 1),
		"token_count":   gorm.Expr("token_count + ?", assistantMessage.TokenCount),
	})

	doneChan <- true
	return nil
}

func (s *ConversationService) buildAPIMessages(messages []app.Message, systemPrompt string) []map[string]string {
	apiMessages := make([]map[string]string, 0, len(messages)+1)

	// 添加系统消息（OpenAI 格式）
	apiMessages = append(apiMessages, map[string]string{
		"role":    "system",
		"content": systemPrompt,
	})

	// 添加历史消息
	for _, msg := range messages {
		if msg.Role == "system" {
			continue // 跳过已有的系统消息
		}
		apiMessages = append(apiMessages, map[string]string{
			"role":    msg.Role,
			"content": msg.Content,
		})
	}

	return apiMessages
}

// estimateTokens 粗略估算文本的 token 数（字符数 / 3，适用于中英混合文本）
func estimateTokens(text string) int {
	if text == "" {
		return 0
	}
	// 中文字符约 1 char = 1 token，英文约 4 chars = 1 token
	// 取中间值 1 char ≈ 0.75 token，即 chars * 4 / 3 的倒数 ≈ chars / 1.5
	// 保守估算用 chars / 2 防止超出
	n := len([]rune(text))
	return (n + 1) / 2
}

// contextConfig 保存从 AIConfig.Settings 中解析出的上下文配置
type contextConfig struct {
	contextLength int // 模型上下文窗口大小（token 数）
	maxTokens     int // 最大输出 token 数
}

// getContextConfig 从 AIConfig 中读取上下文配置，如果没有配置则使用默认值
func getContextConfig(aiConfig *app.AIConfig, preset *app.AIPreset) contextConfig {
	cfg := contextConfig{
		contextLength: 200000, // 保守默认值
		maxTokens:     2000,
	}

	// 从 preset 读取 max_tokens
	if preset != nil && preset.MaxTokens > 0 {
		cfg.maxTokens = preset.MaxTokens
	}

	// 从 AIConfig.Settings 读取 context_length
	if len(aiConfig.Settings) > 0 {
		var settings map[string]interface{}
		if err := json.Unmarshal(aiConfig.Settings, &settings); err == nil {
			if cl, ok := settings["context_length"].(float64); ok && cl > 0 {
				cfg.contextLength = int(cl)
			}
		}
	}

	return cfg
}

// buildContextManagedSystemPrompt 按优先级构建 system prompt，超出 budget 时截断低优先级内容
// 优先级（从高到低）：
//  1. 核心人设（Name/Description/Personality/Scenario/SystemPrompt）
//  2. Preset.SystemPrompt
//  3. Worldbook 触发条目
//  4. CharacterBook 内嵌条目
//  5. MesExample（对话示例，最容易被截断）
//
// 返回构建好的 systemPrompt 以及消耗的 token 数
func (s *ConversationService) buildContextManagedSystemPrompt(
	character app.AICharacter,
	presetSystemPrompt string,
	worldbookEngine *WorldbookEngine,
	conversation app.Conversation,
	messageContents []string,
	budget int,
) (string, int) {
	used := 0

	// ── 优先级1：核心人设 ─────────────────────────────────────────────
	core := fmt.Sprintf("你是 %s。", character.Name)
	if character.Description != "" {
		core += fmt.Sprintf("\n\n描述：%s", character.Description)
	}
	if character.Personality != "" {
		core += fmt.Sprintf("\n\n性格：%s", character.Personality)
	}
	if character.Scenario != "" {
		core += fmt.Sprintf("\n\n场景：%s", character.Scenario)
	}
	if character.SystemPrompt != "" {
		core += fmt.Sprintf("\n\n系统提示：%s", character.SystemPrompt)
	}
	core += "\n\n请根据以上设定进行角色扮演，保持角色的性格和说话方式。"
	core = s.applyMacroVariables(core, character)

	coreTokens := estimateTokens(core)
	if coreTokens >= budget {
		// 极端情况：核心人设本身就超出 budget，截断到 budget
		runes := []rune(core)
		limit := budget * 2
		if limit > len(runes) {
			limit = len(runes)
		}
		core = string(runes[:limit])
		global.GVA_LOG.Warn(fmt.Sprintf("[context] 核心人设超出 budget，已截断至 %d chars", limit))
		return core, budget
	}
	used += coreTokens
	prompt := core

	// ── 优先级2：Preset.SystemPrompt ────────────────────────────────
	if presetSystemPrompt != "" {
		tokens := estimateTokens(presetSystemPrompt)
		if used+tokens <= budget {
			prompt += "\n\n" + presetSystemPrompt
			used += tokens
		} else {
			// 尝试部分插入
			remaining := budget - used
			if remaining > 50 {
				runes := []rune(presetSystemPrompt)
				limit := remaining * 2
				if limit > len(runes) {
					limit = len(runes)
				}
				prompt += "\n\n" + string(runes[:limit])
				used = budget
			}
			global.GVA_LOG.Warn(fmt.Sprintf("[context] Preset.SystemPrompt 因 budget 不足被截断（需要 %d tokens，剩余 %d）", tokens, budget-used))
		}
	}

	if used >= budget {
		return prompt, used
	}

	// ── 优先级3：世界书触发条目 ──────────────────────────────────────
	if conversation.WorldbookEnabled && conversation.WorldbookID != nil && worldbookEngine != nil {
		triggeredEntries, wbErr := worldbookEngine.ScanAndTrigger(*conversation.WorldbookID, messageContents)
		if wbErr != nil {
			global.GVA_LOG.Warn(fmt.Sprintf("[context] 世界书触发失败: %v", wbErr))
		} else if len(triggeredEntries) > 0 {
			wbHeader := "\n\n[World Information]"
			wbSection := wbHeader
			for _, te := range triggeredEntries {
				if te.Entry == nil || te.Entry.Content == "" {
					continue
				}
				line := fmt.Sprintf("\n- %s", te.Entry.Content)
				lineTokens := estimateTokens(line)
				if used+estimateTokens(wbSection)+lineTokens <= budget {
					wbSection += line
					used += lineTokens
				} else {
					global.GVA_LOG.Warn(fmt.Sprintf("[context] 世界书条目 (id=%d) 因 budget 不足被跳过", te.Entry.ID))
					break
				}
			}
			if wbSection != wbHeader {
				prompt += wbSection
			}
		}
	}

	if used >= budget {
		return prompt, used
	}

	// ── 优先级4：CharacterBook 内嵌条目 ──────────────────────────────
	if len(character.CharacterBook) > 0 {
		var characterBook map[string]interface{}
		if err := json.Unmarshal(character.CharacterBook, &characterBook); err == nil {
			if entries, ok := characterBook["entries"].([]interface{}); ok && len(entries) > 0 {
				cbSection := "\n\n世界设定："
				addedAny := false
				for _, entry := range entries {
					entryMap, ok := entry.(map[string]interface{})
					if !ok {
						continue
					}
					enabled := true
					if enabledVal, ok := entryMap["enabled"].(bool); ok {
						enabled = enabledVal
					}
					if !enabled {
						continue
					}
					content, ok := entryMap["content"].(string)
					if !ok || content == "" {
						continue
					}
					line := fmt.Sprintf("\n- %s", content)
					lineTokens := estimateTokens(line)
					if used+estimateTokens(cbSection)+lineTokens <= budget {
						cbSection += line
						used += lineTokens
						addedAny = true
					} else {
						global.GVA_LOG.Warn("[context] CharacterBook 条目因 budget 不足被跳过")
						break
					}
				}
				if addedAny {
					prompt += cbSection
				}
			}
		}
	}

	if used >= budget {
		return prompt, used
	}

	// ── 优先级5：MesExample（对话示例，最低优先级）──────────────────
	if character.MesExample != "" {
		mesTokens := estimateTokens(character.MesExample)
		prefix := "\n\n对话示例：\n"
		prefixTokens := estimateTokens(prefix)
		if used+prefixTokens+mesTokens <= budget {
			prompt += prefix + character.MesExample
			used += prefixTokens + mesTokens
		} else {
			// 尝试截断 MesExample
			remaining := budget - used - prefixTokens
			if remaining > 100 {
				runes := []rune(character.MesExample)
				limit := remaining * 2
				if limit > len(runes) {
					limit = len(runes)
				}
				prompt += prefix + string(runes[:limit])
				used = budget
				global.GVA_LOG.Warn(fmt.Sprintf("[context] MesExample 被截断（原始 %d tokens，保留约 %d tokens）", mesTokens, remaining))
			} else {
				global.GVA_LOG.Warn("[context] MesExample 因 budget 不足被完全跳过")
			}
		}
	}

	return prompt, used
}

// trimMessagesToBudget 从历史消息中按 token 预算选取最近的消息
// 优先保留最新的消息，从后往前丢弃旧消息直到 token 数在 budget 内
func trimMessagesToBudget(messages []app.Message, budget int) []app.Message {
	if budget <= 0 {
		return nil
	}

	// messages 已经是从旧到新的顺序
	// 从最新消息开始往前累加，直到超出 budget
	selected := make([]app.Message, 0, len(messages))
	used := 0

	for i := len(messages) - 1; i >= 0; i-- {
		msg := messages[i]
		if msg.Role == "system" {
			continue
		}
		t := estimateTokens(msg.Content)
		if used+t > budget {
			global.GVA_LOG.Warn(fmt.Sprintf("[context] 历史消息已截断，保留最近 %d 条（共 %d 条），使用 %d tokens", len(selected), len(messages), used))
			break
		}
		used += t
		selected = append([]app.Message{msg}, selected...) // 保持时序
	}

	return selected
}

// buildAPIMessagesWithContextManagement 整合 context 管理，构建最终的 messages 列表
// 返回 apiMessages 及各部分 token 统计日志
func (s *ConversationService) buildAPIMessagesWithContextManagement(
	allMessages []app.Message,
	character app.AICharacter,
	presetSystemPrompt string,
	worldbookEngine *WorldbookEngine,
	conversation app.Conversation,
	aiConfig *app.AIConfig,
	preset *app.AIPreset,
) []map[string]string {
	cfg := getContextConfig(aiConfig, preset)

	// 安全边际：为输出保留 max_tokens，另加 200 token 缓冲
	inputBudget := cfg.contextLength - cfg.maxTokens - 200
	if inputBudget <= 0 {
		inputBudget = cfg.contextLength / 2
	}

	// 为历史消息分配预算：system prompt 最多占用 60% 的 input budget
	systemBudget := inputBudget * 60 / 100
	historyBudget := inputBudget - systemBudget

	// 提取消息内容用于世界书扫描
	var messageContents []string
	for _, msg := range allMessages {
		messageContents = append(messageContents, msg.Content)
	}

	// 构建 system prompt（含 worldbook 注入，按优先级截断）
	systemPrompt, systemTokens := s.buildContextManagedSystemPrompt(
		character,
		presetSystemPrompt,
		worldbookEngine,
		conversation,
		messageContents,
		systemBudget,
	)

	// 如果 system prompt 实际用量比预算少，把节省的预算让给历史消息
	if systemTokens < systemBudget {
		historyBudget += systemBudget - systemTokens
	}

	global.GVA_LOG.Info(fmt.Sprintf("[context] 配置：context_length=%d, max_tokens=%d, input_budget=%d, system=%d tokens, history_budget=%d",
		cfg.contextLength, cfg.maxTokens, inputBudget, systemTokens, historyBudget))

	// 按 token 预算裁剪历史消息
	trimmedMessages := trimMessagesToBudget(allMessages, historyBudget)

	// 构建最终 messages
	apiMessages := make([]map[string]string, 0, len(trimmedMessages)+1)
	apiMessages = append(apiMessages, map[string]string{
		"role":    "system",
		"content": systemPrompt,
	})
	for _, msg := range trimmedMessages {
		if msg.Role == "system" {
			continue
		}
		apiMessages = append(apiMessages, map[string]string{
			"role":    msg.Role,
			"content": msg.Content,
		})
	}

	return apiMessages
}

// callOpenAIAPI 调用 OpenAI API
func (s *ConversationService) callOpenAIAPI(config *app.AIConfig, model string, messages []map[string]string, preset *app.AIPreset) (string, error) {
	client := &http.Client{Timeout: 10 * time.Minute}

	// 使用配置的模型或默认模型
	if model == "" {
		model = config.DefaultModel
	}
	if model == "" {
		model = "gpt-4"
	}

	// 应用预设参数（如果有预设）
	temperature := 0.7
	maxTokens := 2000
	var topP *float64
	var frequencyPenalty *float64
	var presencePenalty *float64
	var stopSequences []string

	if preset != nil {
		temperature = preset.Temperature
		maxTokens = preset.MaxTokens
		if preset.TopP > 0 {
			topP = &preset.TopP
		}
		if preset.FrequencyPenalty != 0 {
			frequencyPenalty = &preset.FrequencyPenalty
		}
		if preset.PresencePenalty != 0 {
			presencePenalty = &preset.PresencePenalty
		}
		// 解析停止序列
		if len(preset.StopSequences) > 0 {
			json.Unmarshal(preset.StopSequences, &stopSequences)
		}
		global.GVA_LOG.Info(fmt.Sprintf("应用预设参数: Temperature=%.2f, MaxTokens=%d, TopP=%.2f", temperature, maxTokens, preset.TopP))
	}

	// 构建请求体
	requestBody := map[string]interface{}{
		"model":       model,
		"messages":    messages,
		"temperature": temperature,
		"max_tokens":  maxTokens,
	}

	// 添加可选参数
	if topP != nil {
		requestBody["top_p"] = *topP
	}
	if frequencyPenalty != nil {
		requestBody["frequency_penalty"] = *frequencyPenalty
	}
	if presencePenalty != nil {
		requestBody["presence_penalty"] = *presencePenalty
	}
	if len(stopSequences) > 0 {
		requestBody["stop"] = stopSequences
	}

	bodyBytes, err := json.Marshal(requestBody)
	if err != nil {
		return "", fmt.Errorf("序列化请求失败: %v", err)
	}

	// 创建请求
	endpoint := config.BaseURL + "/chat/completions"
	req, err := http.NewRequest("POST", endpoint, bytes.NewBuffer(bodyBytes))
	if err != nil {
		return "", fmt.Errorf("创建请求失败: %v", err)
	}

	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "Bearer "+config.APIKey)

	// 发送请求
	resp, err := client.Do(req)
	if err != nil {
		return "", fmt.Errorf("请求失败: %v", err)
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", fmt.Errorf("读取响应失败: %v", err)
	}

	if resp.StatusCode != http.StatusOK {
		return "", fmt.Errorf("API 返回错误 %d: %s", resp.StatusCode, string(body))
	}

	// 解析响应
	var result struct {
		Choices []struct {
			Message struct {
				Content string `json:"content"`
			} `json:"message"`
		} `json:"choices"`
		Error *struct {
			Message string `json:"message"`
		} `json:"error"`
	}

	err = json.Unmarshal(body, &result)
	if err != nil {
		return "", fmt.Errorf("解析响应失败: %v", err)
	}

	if result.Error != nil {
		return "", fmt.Errorf("API 错误: %s", result.Error.Message)
	}

	if len(result.Choices) == 0 {
		return "", errors.New("API 未返回任何回复")
	}

	return result.Choices[0].Message.Content, nil
}

// callAnthropicAPI 调用 Anthropic API
func (s *ConversationService) callAnthropicAPI(config *app.AIConfig, model string, messages []map[string]string, systemPrompt string, preset *app.AIPreset) (string, error) {
	client := &http.Client{Timeout: 10 * time.Minute}

	// 使用配置的模型或默认模型
	if model == "" {
		model = config.DefaultModel
	}
	if model == "" {
		model = "claude-3-sonnet-20240229"
	}

	// Anthropic API 不支持 system role，需要单独传递
	apiMessages := make([]map[string]string, 0)
	for _, msg := range messages {
		if msg["role"] != "system" {
			apiMessages = append(apiMessages, msg)
		}
	}

	// 应用预设参数（如果有预设）
	maxTokens := 2000
	var temperature *float64
	var topP *float64
	var stopSequences []string

	if preset != nil {
		maxTokens = preset.MaxTokens
		if preset.Temperature > 0 {
			temperature = &preset.Temperature
		}
		if preset.TopP > 0 {
			topP = &preset.TopP
		}
		// 解析停止序列
		if len(preset.StopSequences) > 0 {
			json.Unmarshal(preset.StopSequences, &stopSequences)
		}
		global.GVA_LOG.Info(fmt.Sprintf("应用预设参数: Temperature=%.2f, MaxTokens=%d, TopP=%.2f", preset.Temperature, maxTokens, preset.TopP))
	}

	// 构建请求体
	requestBody := map[string]interface{}{
		"model":      model,
		"messages":   apiMessages,
		"system":     systemPrompt,
		"max_tokens": maxTokens,
	}

	// 添加可选参数
	if temperature != nil {
		requestBody["temperature"] = *temperature
	}
	if topP != nil {
		requestBody["top_p"] = *topP
	}
	if len(stopSequences) > 0 {
		requestBody["stop_sequences"] = stopSequences
	}

	bodyBytes, err := json.Marshal(requestBody)
	if err != nil {
		return "", fmt.Errorf("序列化请求失败: %v", err)
	}

	// 创建请求
	endpoint := config.BaseURL + "/messages"
	req, err := http.NewRequest("POST", endpoint, bytes.NewBuffer(bodyBytes))
	if err != nil {
		return "", fmt.Errorf("创建请求失败: %v", err)
	}

	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("x-api-key", config.APIKey)
	req.Header.Set("anthropic-version", "2023-06-01")

	// 发送请求
	resp, err := client.Do(req)
	if err != nil {
		return "", fmt.Errorf("请求失败: %v", err)
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", fmt.Errorf("读取响应失败: %v", err)
	}

	if resp.StatusCode != http.StatusOK {
		return "", fmt.Errorf("API 返回错误 %d: %s", resp.StatusCode, string(body))
	}

	// 解析响应
	var result struct {
		Content []struct {
			Text string `json:"text"`
		} `json:"content"`
		Error *struct {
			Message string `json:"message"`
		} `json:"error"`
	}

	err = json.Unmarshal(body, &result)
	if err != nil {
		return "", fmt.Errorf("解析响应失败: %v", err)
	}

	if result.Error != nil {
		return "", fmt.Errorf("API 错误: %s", result.Error.Message)
	}

	if len(result.Content) == 0 {
		return "", errors.New("API 未返回任何回复")
	}

	return result.Content[0].Text, nil
}
