package openaisvc

import (
	"context"
	"encoding/json"
	"strconv"
	"time"

	"github.com/tidbcloud/aipower/commons/chatgpttemplate"
	"github.com/tidbcloud/aipower/commons/utils"
	"github.com/tidbcloud/aipower/internal/bean/dto"
	"github.com/tidbcloud/aipower/internal/bean/entity"
	"github.com/tidbcloud/aipower/internal/dao"

	"github.com/google/uuid"
	"github.com/tidbcloud/tiinf/errs"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
)

func CheckInteractReqeustValid(req *dto.InteractRequest) error {
	if req.Question == "" {
		return errs.NewBadRequest("question is empty", nil)
	}
	/*
		if req.BotType == "" {
			return errs.NewBadRequest("BotType is empty", nil)
		}
	*/
	if req.Question == "" {
		return errs.NewBadRequest("Question is empty", nil)
	}
	return nil
}

func saveRequestRecord(
	ctx context.Context, req *dto.InteractRequest, prompt chatgpttemplate.Prompt,
	completion string, createdBy string, errorMsg string, executeTime int64,
) {
	record := createRecord(req, prompt, completion, createdBy, errorMsg, strconv.FormatInt(executeTime, 10))
	tilog.Info(ctx, "service interact bot: create interact record", zap.Any("rec", record))
	_, sqlErr := dao.CreateBotInteractRecord(record)
	if sqlErr != nil {
		tilog.Error(ctx, "service interact bot: db create interact record error", zap.Any("err", sqlErr))
	}
}

func InteractBot(ctx context.Context, req *dto.InteractRequest, createdBy string) (resp *dto.InteractResponse, err error) {
	startTime := time.Now()

	tilog.Info(ctx, "service interact bot: start", zap.Any("req", req), zap.Any("createdBy", createdBy))
	if req.SessionID != "" {
		recs, err := dao.GetBotInteractRecordBySessionIDCreatedBy(req.SessionID, createdBy)
		if err != nil {
			tilog.Error(ctx, "service interact bot: get interact record by session_id created_by error", zap.Any("err", err))
			return nil, errs.NewInternalDB("failed to query session_id", err)
		}
		if len(recs) == 0 {
			tilog.Warn(ctx, "service interact bot: session_id created_by relate record not found in db", zap.Any("session_id", req.SessionID), zap.Any("created_by", createdBy))
			return nil, errs.NewBadRequest("session_id invalid", nil)
		}
	}
	if req.SessionID == "" {
		req.SessionID = uuid.New().String()
		tilog.Info(ctx, "service interact bot: open a new session", zap.Any("sessionID", req.SessionID))
	}

	prompt, completion, result, err := CreateCompletionWithReq(ctx, req)
	tilog.Info(ctx, "service interact bot: create completion result", zap.Any("prompt", prompt), zap.String("completion", completion), zap.Any("result", result), zap.Any("err", err))

	var (
		tokenUsed int
		jobID     string
	)
	if result != nil {
		tokenUsed = result.Usage.TotalTokens
		jobID = result.JobID
	}
	endTime := time.Now()
	executeTime := endTime.Sub(startTime).Milliseconds()

	resp = &dto.InteractResponse{
		SessionID:     req.SessionID,
		Question:      req.Question,
		Prompt:        prompt.Content,
		PromptVersion: prompt.Version,
		Response:      completion,
		StartTime:     startTime.Format("2006-01-02 15:04:05"),
		EndTime:       endTime.Format("2006-01-02 15:04:05"),
		StartTimeMS:   startTime.UnixMilli(),
		EndTimeMS:     endTime.UnixMilli(),
		ExecuteTime:   executeTime,
		TokenUsed:     tokenUsed,
		Database:      req.Database,
		JobID:         jobID,
	}

	if err != nil {
		tilog.Info(ctx, "service interact bot: create completion error", zap.String("prompt", prompt.Content), zap.Any("err", err))
		// there are many error type
		if !req.Async {
			var errorMsg string
			if newErr, ok := errs.AsTiError(err); ok && newErr.Cause() != nil {
				errorMsg = newErr.ErrMsg() + ":" + newErr.Cause().Error()
			} else {
				errorMsg = err.Error()
			}
			saveRequestRecord(ctx, req, prompt, completion, createdBy, errorMsg, executeTime)
		}
		return resp, ConvertToTiErr(err)
	}

	tilog.Info(ctx, "service interact bot: gen rsp", zap.Any("rsp", resp))

	return resp, nil
}

func createRecord(question *dto.InteractRequest, prompt chatgpttemplate.Prompt, completion, createdBy string, errStr string, executeTime string) *entity.BotInteractRecord {
	marshal, _ := json.Marshal(question)

	now := utils.JsonTime{Time: time.Now()}
	record := entity.BotInteractRecord{
		SessionID:     question.SessionID,
		AIModel:       question.BotType,
		Question:      question.Question,
		QuestionCtx:   string(marshal),
		Prompt:        prompt.Content,
		PromptVersion: prompt.Version,
		Response:      completion,
		Error:         errStr,
		CreatedBy:     createdBy,
		CreatedAt:     now,
		UpdatedAt:     now,
		ExecuteTime:   executeTime,
	}
	return &record
}

func ListInteractBotRecord(_ context.Context, param *dto.ListInteractRecParam) (*dto.ListInteractRecResult, error) {
	return dao.ListBotInteractRecord(param)
}
