package v1

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/pingcap-inc/pingcat/internal/apiserver/store"
	"github.com/pingcap-inc/pingcat/internal/pkg/code"
	v1 "github.com/pingcap-inc/pingcat/model/v1"

	"github.com/pingcap-inc/pchub/pkg/errors"
	"github.com/pingcap-inc/pchub/pkg/log"
)

type ITChatAPPService struct {
	store store.Factory
}

type ITChatAPPSrv interface {
	HandleLarkMsg(c context.Context, me *v1.LarkMsgEvent, trimStr string)
}

var _ ITChatAPPSrv = (*ITChatAPPService)(nil)

func NewITChatAPP(store store.Factory) *ITChatAPPService {
	return &ITChatAPPService{store: store}
}

func (s *ITChatAPPService) unmarkMsgOnIt(c context.Context, chatSrv *ChatService, me *v1.LarkMsgEvent, larkReaction *v1.LarkReactionResData) {
	if larkReaction != nil {
		err := chatSrv.unmarkMsgOnIt(c, me, larkReaction)
		if err != nil {
			log.L(c).Error(errors.Wrap(err, "Error when unmark msg OnIt via lark reaction").Error())
		}
	}
}

func (s *ITChatAPPService) HandleLarkMsg(c context.Context, me *v1.LarkMsgEvent, trimStr string) {
	cmdPrefix := regexp.MustCompile(`^/it\s+`)
	stripStr := cmdPrefix.ReplaceAllString(trimStr, "")

	chatSrv := NewChat(s.store)

	if stripStr == "" {
		_, _ = chatSrv.SendTextLarkMsg(c, me, "Please input what you want to ask about IT, e.g. /it xxx")

		return
	}

	kbAnswer := &v1.KBAnswer{
		ID:                me.Message.MessageID,
		SenderLarkID:      me.Sender.SenderID.UserID,
		QuestionMsgOpenID: me.Message.MessageID,
		Question:          stripStr,
	}
	_, err := s.store.KBAnswer().Create(c, kbAnswer)
	if err != nil {
		log.L(c).Error(errors.Wrap(err, "Error when create kb answer in db").Error())

		return
	}

	larkReaction, err := chatSrv.markMsgOnIt(c, me)
	if err != nil {
		log.L(c).Error(errors.Wrap(err, "Error when mark msg OnIt via lark reaction").Error())
	}

	res, err := s.searchITKB(c, stripStr)
	if err != nil {
		_, _ = chatSrv.SendTextLarkMsg(c, me, "Search IT KB failed, please try again later.\n\n"+err.Error())
		s.unmarkMsgOnIt(c, chatSrv, me, larkReaction)

		return
	}
	s.unmarkMsgOnIt(c, chatSrv, me, larkReaction)

	ans := ""
	refs := make([]KBRef, 0)
	if res != nil && res.Answer != "" && res.ContextNodes != nil && len(res.ContextNodes) > 0 {
		ans = res.Answer
		// parse and format refs
		hasSeenRef := make(map[string]bool)
		for _, node := range res.ContextNodes {
			sid := node.Node.ExtraInfo.SourceID
			if _, ok := hasSeenRef[sid]; ok {
				continue
			}
			hasSeenRef[sid] = true
			refs = append(refs, KBRef{
				Name: node.Node.ExtraInfo.SourceDesc,
				URL:  "https://applink.feishu.cn/client/web_url/open?mode=sidebar-semi&url=https%3A%2F%2Fpinternalkb.pingcap.net%2Fkb%2F" + sid,
			})
		}
	}

	// group or single
	idType := "user_id"
	receiveid := me.Sender.SenderID.UserID
	if me.Message.ChatType == v1.ChatTypeGroup || me.Message.ChatType == v1.ChatTypeTopicGroup {
		idType = "chat_id"
		receiveid = me.Message.ChatID
	}

	if ans != "" {
		// reply msg
		larkMsg, err := s.BotSendAnswerCardLarkMsg(c, idType, receiveid, ans, stripStr, refs)
		if err != nil {
			log.L(c).Error(errors.Wrap(err, "Error when send answer card msg").Error())

			return
		}
		// update msg to db
		var kbrefs []byte
		if len(refs) > 0 {
			kbrefs, err = json.Marshal(refs)
			if err != nil {
				log.L(c).Error(errors.Wrap(err, "Error when marshal kbrefs").Error())
			}
		}
		kbAnswer.Answer = ans
		kbAnswer.AnswerMsgOpenID = larkMsg.MessageID
		kbAnswer.KBRefs = string(kbrefs)
		kbAnswer.Principal = res.Principal
		_, err = s.store.KBAnswer().Update(c, kbAnswer)
		if err != nil {
			log.L(c).Error(errors.Wrap(err, "Error when update kb answer in db").Error())
		}
	} else {
		_, _ = chatSrv.SendTextLarkMsg(c, me, "No corresponding content was found in the IT KB. I will contact IT to handle it for you.")
		kbAnswer.Principal = res.Principal
		_, err = s.store.KBAnswer().Update(c, kbAnswer)
		if err != nil {
			log.L(c).Error(errors.Wrap(err, "Error when update kb answer in db").Error())
		}

		_ = s.TransferManualService(c, kbAnswer)
	}
}

type ITRes struct {
	Answer       string        `json:"answer"`
	ContextNodes []ContextNode `json:"context_nodes"`
	Principal    string        `json:"principal"`
}
type ContextNode struct {
	Node  ContextNodeItem `json:"node"`
	Score float64         `json:"score"`
}
type ContextNodeItem struct {
	Text      string               `json:"text"`
	ExtraInfo ContextNodeExtraInfo `json:"extra_info"`
}

type ContextNodeExtraInfo struct {
	SourceDesc string `json:"source_desc"`
	SourceID   string `json:"source_id"`
}

type ITReqData struct {
	Query string `json:"query"`
}

const (
	ITToken                      = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IklBTlRIRVJFQUwiLCJpYXQiOjE1MTYyMzkwMjJ9.G_L9LzRyEtK43QfZjFQldN_00vJFbwbY69f3lMp28wM"
	SearchITURL                  = "https://moss.pingcap.net/it/ask"
	DefaultITHandlePersonLarkUID = "2f93dfgb" // 张争
	CMSToken                     = "a67ddeda8e938e570070f9b6e77d2d9116673281333a4a64d40a130aecd7bf733e10beee842449761f08e3a3c42d105623fb364d57f76e036659da75dfc4863641bb0e82d975a2f31b442ba14146147d6114bad63da071e8fa2a3c0764916c5efcb37b87422c8c5f76301be45680aba03b058ed26b75d6a926b3c207814e794f"
	CMSURL                       = "https://internalkb.pingcap.net"
	// LarkAnyCrossEPBasic          = "cGluZ2NhdDp0ZTdNa3BRakVuejg=".
)

func (s *ITChatAPPService) searchITKB(c context.Context, q string) (*ITRes, error) {
	log.L(c).Info("ITChatAPPService.searchITKB function called.")
	reqBody := ITReqData{
		Query: q,
	}
	reqb, err := json.Marshal(&reqBody)
	if err != nil {
		log.L(c).Error(err.Error())

		return nil, errors.WithCode(code.ErrEncodingJSON, err.Error())
	}
	req, _ := http.NewRequestWithContext(c, "POST", SearchITURL, bytes.NewReader(reqb))
	req.Header.Set("Authorization", "Bearer "+ITToken)
	req.Header.Set("Content-Type", "application/json")
	resp, err := (&http.Client{}).Do(req)
	if err != nil {
		log.L(c).Error(err.Error())

		return nil, errors.WithCode(code.ErrAutoXRequest, "error when search it kb in moss api, err: \n%s", err.Error())
	}
	defer func() {
		_ = resp.Body.Close()
	}()

	b, _ := io.ReadAll(resp.Body)
	if resp.StatusCode != http.StatusOK {
		log.L(c).Error(string(b))

		return nil, errors.WithCode(code.ErrAutoXRequest, "error when search it kb in moss api, err: \n%s", string(b))
	}

	var res ITRes
	if err := json.Unmarshal(b, &res); err != nil {
		log.L(c).Error(err.Error())

		return nil, errors.WithCode(code.ErrDecodingJSON, "error when unmarshal moss api err res, err: \n%s", err.Error())
	}

	return &res, nil
}

type KBRef struct {
	Name string `json:"name"`
	URL  string `json:"url"`
}

func (s *ITChatAPPService) BotSendAnswerCardLarkMsg(ctx context.Context, idType, receiveid, msg, ques string, refs []KBRef) (*v1.LarkReplyMsgResData, error) {
	cont := s.ConstructITKBCardCont(msg, ques, "", refs)
	contStr, _ := json.Marshal(&cont)

	reqb, err := json.Marshal(&v1.LarkSendMsgReqData{
		MsgType:   "interactive",
		Content:   string(contStr),
		ReceiveId: receiveid,
	})
	if err != nil {
		log.L(ctx).Errorf("error when marshal send-msg request body, err: %s", err.Error())

		return nil, errors.WithCode(code.ErrEncodingJSON, "")
	}

	return s.botSendAnswerCardLarkMsg(ctx, idType, reqb)
}

func (s *ITChatAPPService) ConstructITKBCardCont(ans, ques, reaction string, refs []KBRef) *v1.AnsCard {
	refsStr := ""
	for _, ref := range refs {
		refsStr += fmt.Sprintf("• [%s](%s)\n", ref.Name, ref.URL)
	}
	if refsStr != "" {
		refsStr = "**Reference KB:**\n" + refsStr
	}
	// slice question
	limitLen := 120
	question := []rune(ques)
	if len(question) < limitLen {
		limitLen = len(question)
	}
	q := strings.Trim(string(question[:limitLen]), " ")
	var card v1.AnsCard
	eels := []v1.AnsCardElement{
		{
			Tag:     "markdown",
			Content: ans,
		},
	}
	if refsStr != "" {
		eels = append(eels, v1.AnsCardElement{
			Tag:     "markdown",
			Content: refsStr,
		}, v1.AnsCardElement{
			Tag: "hr",
		})
	} else {
		eels = append(eels, v1.AnsCardElement{
			Tag: "hr",
		})
	}
	h := v1.AnsCardHeader{
		Template: "blue",
		Title: v1.AnsCardTagContent{
			Tag:     "plain_text",
			Content: q,
		},
	}
	if reaction == "" { // initial card
		card.Config.WideScreenMode = true
		slot := v1.AnsCardElement{
			Tag: "action",
			Actions: []v1.AnsCardAction{
				{
					Tag: "button",
					Text: v1.AnsCardTagContent{
						Tag:     "plain_text",
						Content: "🥳 Helpful",
					},
					Type: "default",
					Value: map[string]string{
						"fb": "good",
					},
				},
				{
					Tag: "button",
					Text: v1.AnsCardTagContent{
						Tag:     "plain_text",
						Content: "😵 Unhelpful, Contact IT",
					},
					Type: "default",
					Value: map[string]string{
						"fb": "bad",
					},
				},
			},
		}
		eels = append(eels, slot)
		card.Header = h
		card.Elements = eels
	} else if reaction == v1.ReactionGood {
		h.Template = "green"
		slot := v1.AnsCardElement{
			Tag: "div",
			Text: v1.AnsCardTagContent{
				Tag:     "plain_text",
				Content: "🎉 Glad to help you.",
			},
		}
		eels = append(eels, slot)
		card.Header = h
		card.Elements = eels
	} else if reaction == v1.ReactionBad {
		h.Template = "red"
		slot := v1.AnsCardElement{
			Tag: "div",
			Text: v1.AnsCardTagContent{
				Tag:     "plain_text",
				Content: "😔 Hope to be able to help you next time.",
			},
		}
		eels = append(eels, slot)
		card.Header = h
		card.Elements = eels
	} else {
		return nil
	}

	return &card
}

func (s *ITChatAPPService) botSendAnswerCardLarkMsg(ctx context.Context, idType string, reqb []byte) (*v1.LarkReplyMsgResData, error) {
	cs := &LarkAPIService{store: s.store}
	jsonraw, err := cs.ReqLarkAPI(ctx, "POST", LarkOpenAPIBase+"/im/v1/messages?receive_id_type="+idType, bytes.NewReader(reqb), nil)
	if err != nil {
		log.L(ctx).Errorf("error when call lark send msg api, err: %s", err.Error())

		return nil, errors.WithCode(code.ErrHttpRequest, "")
	}

	var larkm v1.LarkReplyMsgResData
	if err = json.Unmarshal(jsonraw, &larkm); err != nil {
		log.L(ctx).Errorf("error when unmarshal lark msg, err: %s", err.Error())

		return nil, errors.WithCode(code.ErrDecodingJSON, "")
	}

	return &larkm, nil
}

func (s *ITChatAPPService) TransferManualService(ctx context.Context, kba *v1.KBAnswer) error {
	log.L(ctx).Info("ITChatAPPSrv.TransferManualService called.")

	handlePerson := DefaultITHandlePersonLarkUID
	if kba.Principal != "" {
		hp, err := s.store.User().GetByEmail(ctx, kba.Principal)
		if err != nil {
			log.L(ctx).Error(errors.Wrap(err, "Error when get user by email: "+kba.Principal).Error())
		} else {
			handlePerson = hp.LarkUserID
		}
	}

	chatID, err := s.createChat(ctx, []string{handlePerson, kba.SenderLarkID}, kba.Question)
	if err != nil {
		log.L(ctx).Error(errors.Wrap(err, "Error when create chat").Error())

		return err
	}
	kba.LarkChatID = chatID
	_, err = s.store.KBAnswer().Update(ctx, kba)
	if err != nil {
		log.L(ctx).Error(err.Error())
	}

	msgList := make([]string, 0)
	msgList = append(msgList, kba.QuestionMsgOpenID)
	if kba.AnswerMsgOpenID != "" {
		msgList = append(msgList, kba.AnswerMsgOpenID)
	}
	_ = s.forwardMergeMsgs2Chat(ctx, chatID, msgList)

	shareLink, err := s.getChatShareLink(ctx, chatID)
	if err != nil {
		log.L(ctx).Error(errors.Wrap(err, "Error when get share-chat-link").Error())

		return err
	}

	kbID, err := s.createKB(ctx, kba.Question, shareLink)
	if err != nil {
		log.L(ctx).Error(errors.Wrap(err, "Error when create kb").Error())

		return err
	}
	kbEditPageURL := fmt.Sprintf("https://internalkb.pingcap.net/admin/content-manager/collectionType/api::knowledge.knowledge/%d?plugins[i18n][locale]=zh-CN", kbID)

	//_ = s.createLarkTask(ctx, handlePerson, kba.Question, kbEditPageURL)
	_ = s.createLarkBiTableRecord(ctx, handlePerson, kba.Question, kbEditPageURL, chatID)

	return nil
}

type AddMemberReqBody struct {
	IDList []string `json:"id_list"`
}

func (s *ITChatAPPService) AddChatMember(ctx context.Context, larkUID, larkChatID string) error {
	log.L(ctx).Info("ITChatAPPSrv.AddChatMember called.")

	cs := &LarkAPIService{store: s.store}
	req := AddMemberReqBody{
		IDList: []string{larkUID},
	}
	reqData, _ := json.Marshal(req)
	_, err := cs.ReqLarkAPI(ctx, "POST", LarkOpenAPIBase+"/im/v1/chats/"+larkChatID+"/members?member_id_type=user_id&succeed_type=1", bytes.NewReader(reqData), nil)
	if err != nil {
		log.L(ctx).Errorf("error when call lark add chat member api, err: %s", err.Error())

		return errors.WithCode(code.ErrHttpRequest, "")
	}

	return nil
}

func (s *ITChatAPPService) forwardMergeMsgs2Chat(ctx context.Context, larkChatID string, MessageIDList []string) error {
	log.L(ctx).Info("ITChatAPPSrv.forwardMergeMsgs2Chat called.")

	cs := &LarkAPIService{store: s.store}
	reqBoday := struct {
		ReceiveID     string   `json:"receive_id"`
		MessageIDList []string `json:"message_id_list"`
	}{
		ReceiveID:     larkChatID,
		MessageIDList: MessageIDList,
	}
	reqb, _ := json.Marshal(reqBoday)
	_, err := cs.ReqLarkAPI(ctx, "POST", LarkOpenAPIBase+"/im/v1/messages/merge_forward?receive_id_type=chat_id", bytes.NewReader(reqb), nil)
	if err != nil {
		log.L(ctx).Errorf("error when call lark msg merge_forward api, err: %s", err.Error())

		return errors.WithCode(code.ErrHttpRequest, "")
	}

	return nil
}

type ChatLinkData struct {
	ShareLink   string `json:"share_link"`
	ExpireTime  string `json:"expire_time"`
	IsPermanent bool   `json:"is_permanent"`
}

func (s *ITChatAPPService) getChatShareLink(ctx context.Context, larkChatID string) (string, error) {
	log.L(ctx).Info("ITChatAPPSrv.forwardMergeMsgs2Chat called.")

	cs := &LarkAPIService{store: s.store}
	jsonRaw, err := cs.ReqLarkAPI(ctx, "POST", LarkOpenAPIBase+"/im/v1/chats/"+larkChatID+"/link", nil, nil)
	if err != nil {
		log.L(ctx).Errorf("error when call lark get-chat-link api, err: %s", err.Error())

		return "", errors.WithCode(code.ErrHttpRequest, "")
	}
	var cdata ChatLinkData
	if err := json.Unmarshal(jsonRaw, &cdata); err != nil {
		log.L(ctx).Errorf("error when unmarshal lark get-chat-link api data, err: %s", err.Error())

		return "", errors.WithCode(code.ErrDecodingJSON, "")
	}

	return cdata.ShareLink, nil
}

type CreateChatReqBody struct {
	Name       string   `json:"name"`
	UserIDList []string `json:"user_id_list"`
}
type CreateChatResData struct {
	ChatID string `json:"chat_id"`
}

func (s *ITChatAPPService) createChat(ctx context.Context, userIDList []string, name string) (string, error) {
	log.L(ctx).Info("ITChatAPPSrv.createChat called.")

	cs := &LarkAPIService{store: s.store}
	req := CreateChatReqBody{
		Name:       name,
		UserIDList: userIDList,
	}
	reqb, _ := json.Marshal(req)
	jsonRaw, err := cs.ReqLarkAPI(ctx, "POST", LarkOpenAPIBase+"/im/v1/chats?user_id_type=user_id", bytes.NewReader(reqb), nil)
	if err != nil {
		log.L(ctx).Errorf("error when call lark create-chat api, err: %s", err.Error())

		return "", errors.WithCode(code.ErrHttpRequest, "")
	}
	var cdata CreateChatResData
	if err := json.Unmarshal(jsonRaw, &cdata); err != nil {
		log.L(ctx).Errorf("error when unmarshal lark create-chat api data, err: %s", err.Error())

		return "", errors.WithCode(code.ErrDecodingJSON, "")
	}

	return cdata.ChatID, nil
}

type TaskReqBodyMember struct {
	ID   string `json:"id"`
	Type string `json:"type"`
	Role string `json:"role"`
}

type TaskReqBody struct {
	Summary     string `json:"summary"`
	Description string `json:"description"`
	Due         struct {
		Timestamp string `json:"timestamp"`
		IsAllDay  bool   `json:"is_all_day"`
	} `json:"due"`
	Reminders []struct {
		RelativeFireMinute int `json:"relative_fire_minute"`
	} `json:"reminders"`
	Members []TaskReqBodyMember `json:"members"`
}

func (s *ITChatAPPService) createLarkTask(ctx context.Context, larkUID, question, kbEditURL string) error {
	log.L(ctx).Info("ITChatAPPSrv.createLarkTask called.")

	cs := &LarkAPIService{store: s.store}
	mems := make([]TaskReqBodyMember, 0)
	mems = append(mems, TaskReqBodyMember{
		ID:   larkUID,
		Type: "user",
		Role: "assignee",
	})
	req := TaskReqBody{
		Summary:     "完善 KB 内容：" + question,
		Description: "KB 编辑链接：" + kbEditURL,
		Due: struct {
			Timestamp string `json:"timestamp"`
			IsAllDay  bool   `json:"is_all_day"`
		}{
			Timestamp: strconv.FormatInt(time.Now().Add(time.Hour*24*5).UnixMilli(), 10),
			IsAllDay:  false,
		},
		Members: mems,
		Reminders: []struct {
			RelativeFireMinute int `json:"relative_fire_minute"`
		}{
			{
				RelativeFireMinute: 60 * 24 * 4,
			},
		},
	}
	reqb, _ := json.Marshal(req)
	_, err := cs.ReqLarkAPI(ctx, "POST", LarkOpenAPIBase+"/task/v2/tasks?user_id_type=user_id", bytes.NewReader(reqb), nil)
	if err != nil {
		log.L(ctx).Errorf("error when call lark create-task api, err: %s", err.Error())

		return errors.WithCode(code.ErrHttpRequest, "")
	}

	return nil
}

type KBReqBody struct {
	Data KBReqBodyData `json:"data"`
}

type KBReqBodyData struct {
	Question string `json:"question"`
	Answer   string `json:"answer"`
}

type KBRes struct {
	Data struct {
		ID         int `json:"id"`
		Attributes struct {
			Question  string      `json:"question"`
			Answer    string      `json:"answer"`
			CreatedAt time.Time   `json:"createdAt"`
			UpdatedAt time.Time   `json:"updatedAt"`
			Locale    string      `json:"locale"`
			VectorId  interface{} `json:"vector_id"`
		} `json:"attributes"`
	} `json:"data"`
}

func (s *ITChatAPPService) createKB(ctx context.Context, question, shareChatLink string) (int, error) {
	log.L(ctx).Info("ITChatAPPSrv.createKB called.")

	reqBodyData := KBReqBodyData{
		Question: question,
		Answer:   fmt.Sprintf("请基于以下群聊信息完善该 KB 内容：\n\n%s", shareChatLink),
	}
	reqBody := KBReqBody{
		Data: reqBodyData,
	}
	reqb, _ := json.Marshal(reqBody)
	req, _ := http.NewRequestWithContext(ctx, "POST", CMSURL+"/api/knowledges", bytes.NewReader(reqb))
	req.Header.Set("Authorization", "Bearer "+CMSToken)
	req.Header.Set("Content-Type", "application/json")
	resp, err := (&http.Client{}).Do(req)
	if err != nil {
		log.L(ctx).Error(err.Error())

		return 0, errors.WithCode(code.ErrHttpRequest, "error when request cms create api, err: %s", err.Error())
	}
	defer func() {
		_ = resp.Body.Close()
	}()
	b, _ := io.ReadAll(resp.Body)
	var res KBRes
	if err := json.Unmarshal(b, &res); err != nil {
		log.L(ctx).Error(err.Error())

		return 0, errors.WithCode(code.ErrDecodingJSON, "error when unmarshal cms api res, err: %s", err.Error())
	}

	return res.Data.ID, nil
}

type LarkITKBReminderFields struct {
	Question string `json:"问题标题"`
	Owner    []struct {
		ID string `json:"id"`
	} `json:"负责人"`
	Status    string `json:"KB 草稿状态"`
	Timestamp int64  `json:"通知时间"`
	KBLink    struct {
		Text string `json:"text"`
		Link string `json:"link"`
	} `json:"KB 链接地址"`
	Chat []struct {
		ID string `json:"id"`
	} `json:"服务群地址"`
}

type LarkITKBReminderReqBody struct {
	Fields LarkITKBReminderFields `json:"fields"`
}

func (s *ITChatAPPService) createLarkBiTableRecord(ctx context.Context, larkUID, question, kbEditURL, chatID string) error {
	log.L(ctx).Info("ITChatAPPSrv.createLarkBiTableRecord called.")

	url := fmt.Sprintf("%s/bitable/v1/apps/%s/tables/%s/records?user_id_type=user_id", LarkOpenAPIBase, "UwL3bMipBafGsnslzUtcR1ExnAc", "tblZKkg5Pjltiqmx")
	cs := &LarkAPIService{store: s.store}
	reqFields := LarkITKBReminderFields{
		Question: question,
		Owner: []struct {
			ID string `json:"id"`
		}{
			{
				ID: larkUID,
			},
		},
		Status:    "未完成",
		Timestamp: time.Now().UnixMilli(),
		KBLink: struct {
			Text string `json:"text"`
			Link string `json:"link"`
		}{
			Text: question,
			Link: kbEditURL,
		},
		Chat: []struct {
			ID string `json:"id"`
		}{
			{
				ID: chatID,
			},
		},
	}
	req := LarkITKBReminderReqBody{
		Fields: reqFields,
	}
	reqData, _ := json.Marshal(req)
	_, err := cs.ReqLarkAPI(ctx, "POST", url, bytes.NewReader(reqData), nil)
	if err != nil {
		log.L(ctx).Errorf("error when call lark add bitable-record api, err: %s", err.Error())

		return errors.WithCode(code.ErrHttpRequest, "")
	}

	return nil
}
