package biz

import (
	"ai_scheduler/internal/biz/llm_service/third_party"
	"ai_scheduler/internal/data/constants"
	"ai_scheduler/internal/data/mongo_model"

	"ai_scheduler/internal/data/impl"
	dbmodel "ai_scheduler/internal/data/model"
	"ai_scheduler/internal/entitys"
	"ai_scheduler/internal/pkg"
	"ai_scheduler/utils"
	"context"
	"encoding/json"
	"errors"
	"fmt"

	"github.com/google/uuid"
	"github.com/volcengine/volcengine-go-sdk/service/arkruntime/model"
	"github.com/volcengine/volcengine-go-sdk/service/arkruntime/model/responses"
	"github.com/volcengine/volcengine-go-sdk/volcengine"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo/options"
	"xorm.io/builder"

	"strings"
	"time"
)

type AdviceChatBiz struct {
	hsyq                 *third_party.Hsyq
	rdb                  *utils.Rdb
	aiAdviceSessionImpl  *impl.AiAdviceSessionImpl
	aiAdviceModelSupImpl *impl.AiAdviceModelSupImpl
	advicerChatHisMongo  *mongo_model.AdvicerChatHisMongo
	mongo                *pkg.Mongo
}

func NewAdviceChatBiz(
	hsyq *third_party.Hsyq,
	rdb *utils.Rdb,
	aiAdviceSessionImpl *impl.AiAdviceSessionImpl,
	aiAdviceModelSupImpl *impl.AiAdviceModelSupImpl,
	advicerChatHisMongo *mongo_model.AdvicerChatHisMongo,
	mongo *pkg.Mongo,
) *AdviceChatBiz {
	return &AdviceChatBiz{
		hsyq:                 hsyq,
		rdb:                  rdb,
		aiAdviceSessionImpl:  aiAdviceSessionImpl,
		aiAdviceModelSupImpl: aiAdviceModelSupImpl,
		advicerChatHisMongo:  advicerChatHisMongo,
		mongo:                mongo,
	}
}

func (a *AdviceChatBiz) contextCache(ctx context.Context, chatData *entitys.ChatData, req *entitys.AdvicerChatRegistReq, projectInfo *entitys.AdvicerProjectInfoRes) (promptJson string, contextCache string, err error) {
	switch constants.Mode(projectInfo.ModelInfo.Mode) {
	case constants.ModeResponse:
		prompt, err := a.buildBasePromptResponse(ctx, chatData, req)
		if err != nil {
			return "", "", err
		}
		cache, err := a.hsyq.CreateResponse(ctx, projectInfo.ModelInfo.Key, projectInfo.ModelInfo.ChatModel, prompt, "", true)
		if err != nil {
			return "", "", err
		}
		contextCache = cache.Id
		promptJson = pkg.JsonStringIgonErr(prompt)
	case constants.ModeContext:
		prompt, err := a.buildBasePromptContext(ctx, chatData, req)
		if err != nil {
			return "", "", err
		}
		contextCache, err = a.hsyq.CreateContextCache(ctx, projectInfo.ModelInfo.Key, projectInfo.ModelInfo.ChatModel, prompt)
		if err != nil {
			return "", "", err
		}
		promptJson = pkg.JsonStringIgonErr(prompt)
	default:
		return "", "", fmt.Errorf("未知的mode类型：%d", projectInfo.ModelInfo.Mode)
	}
	return
}

func (a *AdviceChatBiz) Regis(ctx context.Context, chatData *entitys.ChatData, req *entitys.AdvicerChatRegistReq, projectInfo *entitys.AdvicerProjectInfoRes) (string, error) {
	promptJson, contextCache, err := a.contextCache(ctx, chatData, req, projectInfo)
	if err != nil {
		return "", err
	}
	sessionId := uuid.New().String()
	//创建会话
	_, err = a.aiAdviceSessionImpl.Add(&dbmodel.AiAdviceSession{
		SessionID:        sessionId,
		ProjectID:        projectInfo.Base.ProjectID,
		SupID:            projectInfo.Base.ModelSupID,
		AdvicerVersionID: req.AdvicerVersionId,
		ClientID:         req.ClientId,
		TalkSkillID:      req.TalkSkillId,
		Mission:          req.Mission,
		ContextCache:     contextCache,
		CreateAt:         time.Now(),
	})
	if err != nil {
		return "", err
	}
	err = a.rdb.Rdb.SetEx(ctx, sessionId, promptJson, 3600*time.Second).Err()

	return sessionId, err
}

func (a *AdviceChatBiz) Chat(ctx context.Context, chat *entitys.AdvicerChatReq) (assistant mongo_model.Assistant, err error) {
	var session dbmodel.AiAdviceSession
	cond := builder.NewCond()
	cond = cond.And(builder.Eq{"session_id": chat.SessionId})
	err = a.aiAdviceSessionImpl.GetOneBySearchToStrut(&cond, &session)
	if err != nil {
		return assistant, err
	}
	if session.SessionID == "" {
		return assistant, errors.New("未找到会话信息")
	}
	if len(chat.Content) == 0 {
		return assistant, nil
	}
	var modelInfo dbmodel.AiAdviceModelSup
	cond = builder.NewCond()
	cond = cond.And(builder.Eq{"sup_id": session.SupID})
	err = a.aiAdviceModelSupImpl.GetOneBySearchToStrut(&cond, &modelInfo)
	if err != nil {
		return assistant, err
	}
	if modelInfo.SupID == 0 {
		return assistant, errors.New("未找到模型信息")
	}
	//basePromptJson, err := a.getChatDataFromStringSessionId(ctx, chat.SessionId)
	//if err != nil {
	//	return nil, err
	//}
	chatHis, err := a.getChatHis(ctx, session.SessionID, 6)
	prompt, err := a.buildChatPromptResponse(ctx, chat, &session, chatHis)
	if err != nil {
		return assistant, err
	}
	resContent, err := a.callLlmResponse(ctx, prompt, modelInfo.Key, modelInfo.ChatModel, session.ContextCache)
	if err != nil {
		return assistant, err
	}

	result := resContent.Output[0].GetOutputMessage().Content[0].GetText().GetText()
	if err = json.Unmarshal([]byte(result), &assistant); err != nil {
		return assistant, err
	}
	chatCtx, cancel := context.WithCancel(context.Background())
	go func(session dbmodel.AiAdviceSession) {
		defer cancel()
		_, _ = a.mongo.Co(a.advicerChatHisMongo).InsertOne(chatCtx, &mongo_model.AdvicerChatHisMongo{
			SessionId: chat.SessionId,
			User:      chat.Content,
			Assistant: assistant,
			InToken:   resContent.Usage.InputTokens,
			OutToken:  resContent.Usage.OutputTokens,
			CreatAt:   time.Now(),
		})
		if assistant.MissionStatus == "fail" || assistant.MissionStatus == "complete" {
			cond = builder.NewCond()
			cond = cond.And(builder.Eq{"session_id": chat.SessionId})
			session.MissionStatus = assistant.MissionStatus
			session.MissionCompleteDesc = assistant.MissionCompleteDesc
			_ = a.aiAdviceSessionImpl.UpdateByCond(&cond, session)
		}
	}(session)
	return
}

func (a *AdviceChatBiz) buildChatPromptResponse(ctx context.Context, chat *entitys.AdvicerChatReq, session *dbmodel.AiAdviceSession, chatList []mongo_model.AdvicerChatHisMongoEntity) ([]*responses.InputItem, error) {

	var message = make([]*responses.InputItem, 3)
	message[0] = &responses.InputItem{
		Union: &responses.InputItem_EasyMessage{
			EasyMessage: &responses.ItemEasyMessage{
				Role:    responses.MessageRole_system,
				Content: &responses.MessageContent{Union: &responses.MessageContent_StringValue{StringValue: a.taskPrompt(session)}},
			},
		},
	}
	message[1] = &responses.InputItem{
		Union: &responses.InputItem_EasyMessage{
			EasyMessage: &responses.ItemEasyMessage{
				Role:    responses.MessageRole_system,
				Content: &responses.MessageContent{Union: &responses.MessageContent_StringValue{StringValue: "历史聊天记录：\n" + pkg.JsonStringIgonErr(chatList)}},
			},
		},
	}
	message[2] = &responses.InputItem{
		Union: &responses.InputItem_EasyMessage{
			EasyMessage: &responses.ItemEasyMessage{
				Role:    responses.MessageRole_user,
				Content: &responses.MessageContent{Union: &responses.MessageContent_StringValue{StringValue: chat.Content}},
			},
		},
	}

	return message, nil
}
func (a *AdviceChatBiz) getChatHis(ctx context.Context, sessionId string, limit int64) (chatList []mongo_model.AdvicerChatHisMongoEntity, err error) {
	chatList = make([]mongo_model.AdvicerChatHisMongoEntity, 0)
	filter := bson.M{}
	filter["sessionId"] = sessionId
	cursor, err := a.mongo.Co(a.advicerChatHisMongo).Find(ctx, filter, options.Find().SetLimit(limit))
	if err != nil {
		return chatList, err
	}
	for cursor.Next(ctx) {
		var chatHIS mongo_model.AdvicerChatHisMongo
		if err := cursor.Decode(&chatHIS); err != nil {
			return nil, err
		}
		chatList = append(chatList, chatHIS.Entity())
	}

	if err := cursor.Err(); err != nil {
		return nil, err
	}
	return
}

func (a *AdviceChatBiz) buildChatPrompt(ctx context.Context, chat *entitys.AdvicerChatReq, session *dbmodel.AiAdviceSession, modelInfo *dbmodel.AiAdviceModelSup) (model.ContextChatCompletionRequest, error) {
	var message = make([]*model.ChatCompletionMessage, 2)
	message[0] = &model.ChatCompletionMessage{
		Role: model.ChatMessageRoleUser,
		Content: &model.ChatCompletionMessageContent{
			StringValue: volcengine.String(chat.Content),
		},
	}
	message[1] = &model.ChatCompletionMessage{
		Role: model.ChatMessageRoleAssistant,
		Content: &model.ChatCompletionMessageContent{
			StringValue: volcengine.String(a.taskPrompt(session)),
		},
	}

	req := model.ContextChatCompletionRequest{
		ContextID: session.ContextCache,
		Model:     modelInfo.ChatModel,
		Messages:  message,
		Stream:    false,
	}
	return req, nil
}

func (a *AdviceChatBiz) taskPrompt(session *dbmodel.AiAdviceSession) string {
	//type mission struct {
	//	missionName         string
	//	status              string
	//	missionCompleteDesc string
	//}
	//var m = &mission{
	//	missionName:         session.Mission,
	//	status:              pkg.Ter(session.MissionStatus == 1, "进行中", "已完成"),
	//	missionCompleteDesc: session.MissionCompleteDesc,
	//}
	//missionJon, _ := json.Marshal(m)
	return "[当前时间]" + time.Now().Format("2006-01-02 15:04:05")
}

func (a *AdviceChatBiz) buildBasePromptContext(ctx context.Context, chatData *entitys.ChatData, req *entitys.AdvicerChatRegistReq) ([]*model.ChatCompletionMessage, error) {
	var message = make([]*model.ChatCompletionMessage, 2)
	message[0] = &model.ChatCompletionMessage{
		Role: model.ChatMessageRoleSystem,
		Content: &model.ChatCompletionMessageContent{
			StringValue: volcengine.String(a.sysPrompt(chatData, req)),
		},
	}
	message[1] = &model.ChatCompletionMessage{
		Role: model.ChatMessageRoleSystem,
		Content: &model.ChatCompletionMessageContent{
			StringValue: volcengine.String(a.assistantPrompt(chatData)),
		},
	}
	return message, nil
}

func (a *AdviceChatBiz) buildBasePromptResponse(ctx context.Context, chatData *entitys.ChatData, req *entitys.AdvicerChatRegistReq) ([]*responses.InputItem, error) {
	var message = make([]*responses.InputItem, 2)
	message[0] = &responses.InputItem{
		Union: &responses.InputItem_EasyMessage{
			EasyMessage: &responses.ItemEasyMessage{
				Role:    responses.MessageRole_system,
				Content: &responses.MessageContent{Union: &responses.MessageContent_StringValue{StringValue: a.sysPrompt(chatData, req)}},
			},
		},
	}
	message[1] = &responses.InputItem{
		Union: &responses.InputItem_EasyMessage{
			EasyMessage: &responses.ItemEasyMessage{
				Role:    responses.MessageRole_system,
				Content: &responses.MessageContent{Union: &responses.MessageContent_StringValue{StringValue: a.assistantPrompt(chatData)}},
			},
		},
	}
	return message, nil
}

func (a *AdviceChatBiz) setContent(ctx context.Context, basePromptJson string, content string, session *dbmodel.AiAdviceSession) ([]*model.ChatCompletionMessage, error) {
	promptJson := strings.ReplaceAll(basePromptJson, "{{chat_content}}", content)
	var basePrompt []*model.ChatCompletionMessage
	err := json.Unmarshal([]byte(promptJson), &basePrompt)
	if err != nil {
		return nil, err
	}

	return basePrompt, nil
}

func (a *AdviceChatBiz) sysPrompt(chatData *entitys.ChatData, req *entitys.AdvicerChatRegistReq) string {
	var prompt strings.Builder
	prompt.WriteString(constants.BasePrompt)
	prompt.WriteString(req.Mission)
	prompt.WriteString(constants.BasePrompt2)

	return prompt.String()

}

func (a *AdviceChatBiz) assistantPrompt(chatData *entitys.ChatData) string {
	return pkg.JsonStringIgonErr(chatData)
}

func (a *AdviceChatBiz) getChatDataFromStringSessionId(ctx context.Context, sessionId string) (basePromptJson string, err error) {
	cache := a.rdb.Rdb.Get(ctx, sessionId)
	if cache.Err() != nil {
		err = cache.Err()
		return
	}

	return cache.Val(), cache.Err()
}

func (a *AdviceChatBiz) callLlm(ctx context.Context, request model.ContextChatCompletionRequest, key string) (string, error) {
	res, err := a.hsyq.ChatWithRequest(ctx, key, request)
	if err != nil {
		return "", err
	}
	return *res.Choices[0].Message.Content.StringValue, nil
}

func (a *AdviceChatBiz) callLlmResponse(ctx context.Context, request []*responses.InputItem, key string, modelName string, id string) (*responses.ResponseObject, error) {
	res, err := a.hsyq.CreateResponse(ctx, key, modelName, request, id, false)
	if err != nil {
		return nil, err
	}
	return res, nil
}
