package biz

import (
	"ai_scheduler/internal/config"
	"ai_scheduler/internal/data/constants"
	"ai_scheduler/internal/data/impl"
	"ai_scheduler/internal/domain/tools/common/knowledge_base"
	"ai_scheduler/internal/pkg/dingtalk"
	"ai_scheduler/internal/pkg/util"
	"ai_scheduler/internal/pkg/utils_ollama"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"strings"

	"gitea.cdlsxd.cn/self-tools/l-dingtalk-stream-sdk-go/card"
	"gitea.cdlsxd.cn/self-tools/l-dingtalk-stream-sdk-go/chatbot"
	"github.com/alibabacloud-go/dingtalk/card_1_0"
	"github.com/alibabacloud-go/tea/tea"
	"github.com/gofiber/fiber/v2/log"
	"github.com/ollama/ollama/api"
)

type CallbackBiz struct {
	cfg                *config.Config
	ollamaClient       *utils_ollama.Client
	dingtalkCardClient *dingtalk.CardClient
	botConfigImpl      *impl.BotConfigImpl
}

func NewCallbackBiz(
	cfg *config.Config,
	ollamaClient *utils_ollama.Client,
	dingtalkCardClient *dingtalk.CardClient,
	botConfigImpl *impl.BotConfigImpl,
) *CallbackBiz {
	return &CallbackBiz{
		cfg:                cfg,
		ollamaClient:       ollamaClient,
		dingtalkCardClient: dingtalkCardClient,
		botConfigImpl:      botConfigImpl,
	}
}

// IssueHandlingGroup 问题处理群机器人回调
// 能力1： 通过[内容提取] 宏，分析用户QA问题，调出QA表单卡片
// 能力2： 通过[QA收集] 宏，收集用户反馈，写入知识库
// 能力3： 通过[知识库查询] 宏，查询知识库，返回答案
func (c *CallbackBiz) IssueHandlingGroup(data chatbot.BotCallbackDataModel) error {
	// 能力1、2：分析用户QA问题，写入知识库
	if strings.Contains(data.Text.Content, "[内容提取]") || strings.Contains(data.Text.Content, "[QA收集]") {
		c.issueHandlingExtractContent(data)
	}
	// 能力3：查询知识库，返回答案
	if strings.Contains(data.Text.Content, "[知识库查询]") {
		c.issueHandlingQueryKnowledgeBase(data)
	}

	return nil
}

// 问题处理群机器人内容提取
func (c *CallbackBiz) issueHandlingExtractContent(data chatbot.BotCallbackDataModel) {
	// 1.提取用户输入
	prompt := fmt.Sprintf(constants.IssueHandlingExtractContentPrompt, data.Text.Content)
	log.Infof("问题提取提示词: %s", prompt)
	// LLM 提取
	generateResp, err := c.ollamaClient.Generation(context.Background(), &api.GenerateRequest{
		Model:  c.cfg.Ollama.GenerateModel,
		Prompt: prompt,
		Stream: util.AnyToPoint(false),
		Think:  &api.ThinkValue{Value: false},
	})
	if err != nil {
		log.Errorf("问题提取失败: %v", err)
		return
	}

	// 尝试清理 JSON 内容（有时模型会返回 markdown 块）
	generateResp.Response = strings.TrimPrefix(generateResp.Response, "```json")
	generateResp.Response = strings.TrimSuffix(generateResp.Response, "```")
	generateResp.Response = strings.TrimSpace(generateResp.Response)

	// 解析 JSON 响应
	var resp struct {
		Question   string `json:"question"`
		Answer     string `json:"answer"`
		Confidence string `json:"confidence"`
	}
	if err := json.Unmarshal([]byte(generateResp.Response), &resp); err != nil {
		log.Errorf("解析 JSON 响应失败: %v", err)
		return
	}

	// 2.获取应用AppKey
	appKey, err := c.botConfigImpl.GetRobotAppKey(data.RobotCode)
	if err != nil {
		log.Errorf("获取应用配置失败: %v", err)
		return
	}

	// 3.创建并投放卡片
	outTrackId := constants.BuildCardOutTrackId(data.ConversationId, data.RobotCode) // 构建卡片 OutTrackId
	_, err = c.dingtalkCardClient.CreateAndDeliver(
		appKey,
		&card_1_0.CreateAndDeliverRequest{
			CardTemplateId:   tea.String(c.cfg.Dingtalk.Card.Template.ContentCollect),
			OutTrackId:       tea.String(outTrackId),
			CallbackType:     tea.String("HTTP"),
			CallbackRouteKey: tea.String(c.cfg.Dingtalk.Card.CallbackRouteKey),
			CardData: &card_1_0.CreateAndDeliverRequestCardData{
				CardParamMap: map[string]*string{
					"_CARD_DEBUG_TOOL_ENTRY": tea.String(c.cfg.Dingtalk.Card.DebugToolEntryShow), // 调试字段
					"title":                  tea.String("QA知识收集"),
					"button_display":         tea.String("true"),
					"textarea_display":       tea.String("normal"),
					"action_id":              tea.String("collect_qa"),
					"tenant_id":              tea.String(constants.KnowledgeTenantIdDefault),
					"question":               tea.String(resp.Question),
					"answer":                 tea.String(resp.Answer),
				},
			},
			ImGroupOpenSpaceModel: &card_1_0.CreateAndDeliverRequestImGroupOpenSpaceModel{
				SupportForward: tea.Bool(false),
			},
			OpenSpaceId: tea.String("dtv1.card//im_group." + data.ConversationId),
			ImGroupOpenDeliverModel: &card_1_0.CreateAndDeliverRequestImGroupOpenDeliverModel{
				RobotCode: tea.String(c.cfg.Dingtalk.SceneGroup.GroupTemplateRobotIDIssueHandling),
			},
		},
	)

}

// 问题处理群机器人查询知识库
func (c *CallbackBiz) issueHandlingQueryKnowledgeBase(data chatbot.BotCallbackDataModel) {
	// 获取应用配置
	appKey, err := c.botConfigImpl.GetRobotAppKey(data.RobotCode)
	if err != nil {
		log.Errorf("应用机器人配置不存在: %s, err: %v", data.RobotCode, err)
		return
	}
	// 创建卡片
	outTrackId := constants.BuildCardOutTrackId(data.ConversationId, data.RobotCode)
	_, err = c.dingtalkCardClient.CreateAndDeliver(
		appKey,
		&card_1_0.CreateAndDeliverRequest{
			CardTemplateId: tea.String(c.cfg.Dingtalk.Card.Template.BaseMsg),
			CardData: &card_1_0.CreateAndDeliverRequestCardData{
				CardParamMap: map[string]*string{
					"title":    tea.String(data.Text.Content),
					"markdown": tea.String("知识库检索中..."),
				},
			},
			OutTrackId: tea.String(outTrackId),
			ImGroupOpenSpaceModel: &card_1_0.CreateAndDeliverRequestImGroupOpenSpaceModel{
				SupportForward: tea.Bool(false),
			},
			OpenSpaceId: tea.String("dtv1.card//im_group." + data.ConversationId),
			ImGroupOpenDeliverModel: &card_1_0.CreateAndDeliverRequestImGroupOpenDeliverModel{
				RobotCode: tea.String(data.RobotCode),
			},
		},
	)

	// 查询知识库
	knowledgeBase := knowledge_base.New(c.cfg.KnowledgeConfig)
	knowledgeResp, err := knowledgeBase.Query(&knowledge_base.QueryRequest{
		TenantID: constants.KnowledgeTenantIdDefault,
		Query:    data.Text.Content,
		Mode:     constants.KnowledgeModeMix,
		Stream:   false,
		Think:    false,
		OnlyRAG:  true,
	})
	if err != nil {
		log.Errorf("查询知识库失败: %v", err)
		return
	}
	knowledgeRespBytes, err := io.ReadAll(knowledgeResp)
	if err != nil {
		log.Errorf("读取知识库响应失败: %v", err)
		return
	}

	// 卡片更新
	message, isRetrieved, err := knowledge_base.ParseOpenAIHTTPData(string(knowledgeRespBytes))
	if err != nil {
		log.Errorf("读取知识库 SSE 数据失败: %v", err)
		return
	}

	content := message.Content
	if !isRetrieved {
		content = "知识库未检测到匹配信息，请核查知识库数据是否正确。"
	}

	// 卡片更新
	_, err = c.dingtalkCardClient.UpdateCard(
		appKey,
		&card_1_0.UpdateCardRequest{
			OutTrackId: tea.String(outTrackId),
			CardData: &card_1_0.UpdateCardRequestCardData{
				CardParamMap: map[string]*string{
					"markdown": tea.String(content),
				},
			},
			CardUpdateOptions: &card_1_0.UpdateCardRequestCardUpdateOptions{
				UpdateCardDataByKey: tea.Bool(true),
			},
		},
	)
	if err != nil {
		log.Errorf("更新卡片失败: %v", err)
		return
	}
}

// IssueHandlingCollectQA 问题处理群机器人 QA 收集回调
func (c *CallbackBiz) IssueHandlingCollectQA(data card.CardRequest) *card.CardResponse {
	// 确认提交，文本写入知识库
	var question, answer string
	if data.CardActionData.CardPrivateData.Params["submit"] == "submit" {
		question = data.CardActionData.CardPrivateData.Params["question"].(string)
		answer = data.CardActionData.CardPrivateData.Params["answer"].(string)
		tenantID := data.CardActionData.CardPrivateData.Params["tenant_id"].(string)

		// 协程执行耗时操作，防止阻塞
		util.SafeGo("inject_knowledge_base", func() {
			knowledgeBase := knowledge_base.New(c.cfg.KnowledgeConfig)
			err := knowledgeBase.IngestBatchQA(&knowledge_base.IngestBacthQARequest{
				TenantID: tenantID,
				QAList: []*knowledge_base.QA{
					{
						Question: question,
						Answer:   answer,
					},
				},
			})
			if err != nil {
				log.Errorf("注入知识库失败: %v", err)
			} else {
				log.Infof("注入知识库成功: tenantID=%s", tenantID)
			}

			// 解析当前卡片的 ConversationId 和 robotCode
			conversationId, robotCode := constants.ParseCardOutTrackId(data.OutTrackId)

			// 获取应用配置
			appKey, err := c.botConfigImpl.GetRobotAppKey(robotCode)
			if err != nil {
				log.Errorf("获取应用机器人配置失败: %v", err)
				return
			}

			// 发送卡片通知用户注入成功
			outTrackId := constants.BuildCardOutTrackId(conversationId, robotCode)
			c.dingtalkCardClient.CreateAndDeliver(
				appKey,
				&card_1_0.CreateAndDeliverRequest{
					CardTemplateId: tea.String(c.cfg.Dingtalk.Card.Template.BaseMsg),
					OutTrackId:     tea.String(outTrackId),
					CardData: &card_1_0.CreateAndDeliverRequestCardData{
						CardParamMap: map[string]*string{
							"title":    tea.String("QA知识收集结果"),
							"markdown": tea.String("[Get] **成功**"),
						},
					},
					ImGroupOpenSpaceModel: &card_1_0.CreateAndDeliverRequestImGroupOpenSpaceModel{
						SupportForward: tea.Bool(false),
					},
					OpenSpaceId: tea.String("dtv1.card//im_group." + conversationId),
					ImGroupOpenDeliverModel: &card_1_0.CreateAndDeliverRequestImGroupOpenDeliverModel{
						RobotCode: tea.String(robotCode),
					},
				},
			)
		})
	}

	// 取消提交，禁用输入框
	resp := &card.CardResponse{
		CardUpdateOptions: &card.CardUpdateOptions{
			UpdateCardDataByKey: true,
		},
		CardData: &card.CardDataDto{
			CardParamMap: map[string]string{
				"textarea_display": "disabled",
			},
		},
	}
	if question != "" {
		resp.CardData.CardParamMap["question"] = question
	}
	if answer != "" {
		resp.CardData.CardParamMap["answer"] = answer
	}

	return resp
}
