package v1

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

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

	"github.com/pingcap-inc/security/internal/alert-server/store"
	v1 "github.com/pingcap-inc/security/model/v1"
)

type docCheckService struct {
	store store.Factory
}

type DocCheckSrv interface {
	DocCheck(ctx context.Context)
}

var _ DocCheckSrv = (*docCheckService)(nil)

func newDocCheck(srv *service) *docCheckService {
	return &docCheckService{store: srv.store}
}

const (
	AppToken        = "KOxgbMsTxaqdmRswBkrctFwHnwg"
	DocTableId      = "tblqNJICnJh0wjGg"
	RegTableId      = "tblAqoywVct3LMUL"
	Doc             = 0
	Regular         = 1
	DocApprovalCode = "27D08745-46F4-4E24-840B-23097358A33B"
)

func (dcs *docCheckService) DocCheck(ctx context.Context) {
	comSrv := newCommon()

	var err error
	token, err = comSrv.GetLarkToken(ctx, 0)
	if err != nil {
		log.L(ctx).Errorf("GetLarkToken error: %v", err)

		return
	}

	// check approval status
	err = dcs.checkApprStatus(ctx)
	if err != nil {
		return
	}

	// unchecked docs
	unWikiDocs, err := dcs.store.Wiki().GetWikiDocsWithReason(ctx, 0)
	if err != nil {
		log.L(ctx).Errorf("get wiki docs error: %v", err)

		return
	}

	wikiRegular, err := dcs.store.Wiki().GetWikiRegularByOrder(ctx)
	if err != nil {
		log.L(ctx).Errorf("GetWikiRegular error: %v", err)

		return
	}

	wikiDocUnsafeMap := make(map[string][]v1.WikiDoc)
	wikiDocChecked := make([]v1.WikiDoc, 0)
	// save wiki docs
	for _, wikiDoc := range unWikiDocs {
		deContend, err := Decompress(wikiDoc.Content)
		if err != nil {
			log.L(ctx).Errorf("Decompress error: %v", err)

			return
		}
		ru := regularCheck(ctx, deContend, wikiRegular)
		wikiDoc.Level = ru.Level
		wikiDoc.Reason = 1
		wikiDoc.RegID = ru.ID
		wikiDocChecked = append(wikiDocChecked, wikiDoc)

		if wikiDoc.Level > 2 {
			if _, ok := wikiDocUnsafeMap[wikiDoc.Owner]; !ok {
				wikiDocUnsafeMap[wikiDoc.Owner] = make([]v1.WikiDoc, 0)
			}
			wikiDocUnsafeMap[wikiDoc.Owner] = append(wikiDocUnsafeMap[wikiDoc.Owner], wikiDoc)
		}
	}

	log.L(ctx).Infof("wikiDocChecked: %d", len(wikiDocChecked))

	err = dcs.store.Wiki().SaveDelWikiDocs(ctx, wikiDocChecked, nil)
	if err != nil {
		log.L(ctx).Errorf("save wiki docs error: %v", err)

		return
	}

	// notify owner
	err = dcs.NotifyOwner(ctx, wikiDocUnsafeMap)
	if err != nil {
		return
	}

	// fresh bitable
	err = dcs.freshDocBitable(ctx)
	if err != nil {
		log.L(ctx).Errorf("fresh bitable error: %v", err)

		return
	}

	err = dcs.freshRegBitable(ctx)
	if err != nil {
		log.L(ctx).Errorf("fresh bitable error: %v", err)

		return
	}

	_ = dcs.SetWikiDocPermission(ctx)

	return
}

func regularCheck(_ context.Context, docInfo string, wikiRegular []v1.WikiRegular) *v1.WikiRegular {

	for _, rule := range wikiRegular {
		ru := matchRule(rule, docInfo)
		if ru != nil {
			return ru
		}
	}

	return &v1.WikiRegular{
		Level: 2,
		ID:    0,
	} // default level
}

func matchRule(rule v1.WikiRegular, content string) *v1.WikiRegular {

	return &rule
	// compile, err := pcre2.Compile(rule.Content, 0)
	// if err != nil {
	// 	log.L(context.Background()).Errorf("compile rule error: %v", err)
	//
	// 	return nil
	// }
	// matcher := compile.NewMatcherString(content, 0)
	//
	// if matcher.Matches {
	// 	return &rule
	// }
	//
	// return nil
}

type Owner struct {
	Email  string `json:"email"`
	EnName string `json:"en_name"`
	ID     string `json:"id"`
	Name   string `json:"name"`
}

type DocFields struct {
	Level     string  `json:"Level,omitempty"`
	Owner     []Owner `json:"Owner,omitempty"`
	Reason    string  `json:"Reason,omitempty"`
	SpaceName string  `json:"SpaceName,omitempty"`
	Space     string  `json:"Space,omitempty"`
	NodeToken string  `json:"NodeToken,omitempty"`
	Title     string  `json:"Title,omitempty"`
	URL       string  `json:"URL,omitempty"`
}

type DocItem struct {
	Fields   DocFields `json:"fields"`
	ID       string    `json:"id,omitempty" `
	RecordID string    `json:"record_id"`
}

type DocItems struct {
	Items []DocItem `json:"items"`
}

func (dcs *docCheckService) freshDocBitable(ctx context.Context) error {
	comSrv := newCommon()

	url := "/bitable/v1/apps/" + AppToken + "/tables/" + DocTableId + "/records"
	pageToken := ""

	reasonBiMap := NewBiMap()

	reasonBiMap.Put("Unchecked", 0)
	reasonBiMap.Put("System Scan", 1)
	reasonBiMap.Put("User Confirm", 2)

	levelBiMap := NewBiMap()
	levelBiMap.Put("0-low risk", 0)
	levelBiMap.Put("1-low", 1)
	levelBiMap.Put("2-low", 2)
	levelBiMap.Put("3-medium", 3)
	levelBiMap.Put("4-high", 4)

	docInfo := make([]DocItem, 0)
	for {
		params := map[string]string{
			"page_size":  "500",
			"page_token": pageToken,
		}

		resp, err := comSrv.GetDataByReqLarkAPI(ctx, "GET", LarkOpenAPIBase+url, nil, params, token)
		if err != nil {
			return err
		}

		var resData respData
		err = json.Unmarshal(resp, &resData)
		if err != nil {
			log.L(ctx).Errorf("unmarshal response error: %v", err)

			return err
		}
		var docItems DocItems
		err = json.Unmarshal(resp, &docItems)
		if err != nil {
			log.L(ctx).Errorf("unmarshal response error: %v", err)

			return err
		}

		docInfo = append(docInfo, docItems.Items...)

		if resData.PageToken == "" {
			break
		} else {
			pageToken = resData.PageToken
		}
	}

	spaces, err := dcs.store.Wiki().GetWikiSpace(ctx)
	if err != nil {
		return err
	}

	spaceMap := make(map[string]string)
	for _, space := range spaces {
		spaceMap[space.SpaceID] = space.Name
	}

	docsDb, err := dcs.store.Wiki().GetWikiDocWithoutContent(ctx)
	if err != nil {
		return err
	}

	dbDocMap := make(map[string]v1.WikiDoc)
	dbDocTagMap := make(map[string]int)
	for _, doc := range docsDb {
		dbDocMap[doc.NodeToken] = doc
		dbDocTagMap[doc.NodeToken] = 0
	}

	addDoc := make([]DocItem, 0)
	updateDoc := make([]DocItem, 0)
	delDoc := make([]string, 0)
	updateDocDb := make([]v1.WikiDoc, 0)
	for _, doc := range docInfo {
		wikiDocDb, ok := dbDocMap[doc.Fields.NodeToken]
		if !ok {
			delDoc = append(delDoc, doc.RecordID)
		} else {
			if wikiDocDb.Owner != doc.Fields.Owner[0].ID ||
				wikiDocDb.SpaceID != doc.Fields.Space ||
				wikiDocDb.URL != doc.Fields.URL {
				updateDoc = append(updateDoc, DocItem{
					Fields: DocFields{
						Owner:     []Owner{{ID: wikiDocDb.Owner}},
						SpaceName: spaceMap[wikiDocDb.SpaceID],
						Space:     wikiDocDb.SpaceID,
						NodeToken: wikiDocDb.NodeToken,
						Title:     wikiDocDb.Title,
						URL:       wikiDocDb.URL,
					},
					RecordID: doc.RecordID,
				})
			}
			if wikiDocDb.Reason != reasonBiMap.GetByKey(doc.Fields.Reason) ||
				wikiDocDb.Level != levelBiMap.GetByKey(doc.Fields.Level) {
				wikiDocDb.Reason = reasonBiMap.GetByKey(doc.Fields.Reason)
				wikiDocDb.Level = levelBiMap.GetByKey(doc.Fields.Level)
				updateDocDb = append(updateDocDb, wikiDocDb)
			}
		}
		dbDocTagMap[doc.Fields.NodeToken] = 1
	}

	for nodeToken, tag := range dbDocTagMap {
		if tag == 0 {
			wikiDocDb := dbDocMap[nodeToken]
			addDoc = append(addDoc, DocItem{
				Fields: DocFields{
					Level:     levelBiMap.GetByValue(wikiDocDb.Level),
					Reason:    reasonBiMap.GetByValue(wikiDocDb.Reason),
					Owner:     []Owner{{ID: wikiDocDb.Owner}},
					SpaceName: spaceMap[wikiDocDb.SpaceID],
					Space:     wikiDocDb.SpaceID,
					NodeToken: wikiDocDb.NodeToken,
					Title:     wikiDocDb.Title,
					URL:       wikiDocDb.URL,
				},
			})
		}
	}

	if len(delDoc) != 0 {
		err = delBiRecord(ctx, delDoc, 0)
		if err != nil {
			return err
		}
	}

	if len(addDoc) != 0 {
		err = addBiRecord(ctx, addDoc, 0, true)
		if err != nil {
			return err
		}
	}

	if len(updateDoc) != 0 {
		err = addBiRecord(ctx, updateDoc, 0, false)

		if err != nil {
			return err
		}
	}

	if len(updateDocDb) != 0 {
		err = dcs.store.Wiki().UpdateWikiDocs(ctx, updateDocDb)
		if err != nil {
			log.L(ctx).Errorf("save wiki docs error: %v", err)
		}
	}

	return err
}

type RegFields struct {
	Level     string `json:"Level,omitempty"`
	Name      string `json:"Name,omitempty"`
	GroupID   string `json:"GroupID,omitempty"`
	GroupName string `json:"GroupName,omitempty"`
	Attribute string `json:"Attribute,omitempty"`
	Enable    string `json:"Enable,omitempty"`
	Remark    string `json:"Remark,omitempty"`
	Content   string `json:"Content,omitempty"`
}

type RegItem struct {
	Fields   RegFields `json:"fields"`
	ID       string    `json:"id,omitempty" `
	RecordID string    `json:"record_id"`
}

type RegItems struct {
	Items []RegItem `json:"items"`
}

func (dcs *docCheckService) freshRegBitable(ctx context.Context) error {

	comSrv := newCommon()

	url := "/bitable/v1/apps/" + AppToken + "/tables/" + RegTableId + "/records"
	pageToken := ""

	regInfo := make([]RegItem, 0)
	for {
		params := map[string]string{
			"page_token": pageToken,
			"page_size":  "500",
		}

		resp, err := comSrv.GetDataByReqLarkAPI(ctx, "GET", LarkOpenAPIBase+url, nil, params, token)
		if err != nil {
			return err
		}

		var resData respData
		err = json.Unmarshal(resp, &resData)
		if err != nil {
			log.L(ctx).Errorf("unmarshal response error: %v", err)

			return err
		}
		var regItems RegItems
		err = json.Unmarshal(resp, &regItems)
		if err != nil {
			log.L(ctx).Errorf("unmarshal response error: %v", err)

			return err
		}

		regInfo = append(regInfo, regItems.Items...)

		if resData.PageToken == "" {
			break
		} else {
			pageToken = resData.PageToken
		}
	}

	regularDb, err := dcs.store.Wiki().GetWikiRegularByOrder(ctx)
	if err != nil {
		return err
	}

	dbRegMap := make(map[string]v1.WikiRegular)
	dbRegTagMap := make(map[string]int)
	for _, reg := range regularDb {
		dbRegMap[reg.Name] = reg
		dbRegTagMap[reg.Name] = 0
	}

	addReg := make([]RegItem, 0)
	updateReg := make([]RegItem, 0)
	delReg := make([]string, 0)
	for _, reg := range regInfo {
		wikiRegDb, ok := dbRegMap[reg.Fields.Name]
		if !ok {
			delReg = append(delReg, reg.RecordID)
		} else {
			if strconv.Itoa(wikiRegDb.Level) != reg.Fields.Level ||
				strconv.Itoa(wikiRegDb.GroupID) != reg.Fields.GroupID ||
				wikiRegDb.GroupName != reg.Fields.GroupName ||
				strconv.Itoa(wikiRegDb.Attribute) != reg.Fields.Attribute ||
				strconv.Itoa(wikiRegDb.Enable) != reg.Fields.Enable ||
				wikiRegDb.Remark != reg.Fields.Remark ||
				wikiRegDb.Content != reg.Fields.Content {
				updateReg = append(updateReg, RegItem{
					Fields: RegFields{
						Level:     strconv.Itoa(wikiRegDb.Level),
						Name:      wikiRegDb.Name,
						GroupID:   strconv.Itoa(wikiRegDb.GroupID),
						GroupName: wikiRegDb.GroupName,
						Attribute: strconv.Itoa(wikiRegDb.Attribute),
						Enable:    strconv.Itoa(wikiRegDb.Enable),
						Remark:    wikiRegDb.Remark,
						Content:   wikiRegDb.Content,
					},
					RecordID: reg.RecordID,
				})
			}
		}
		dbRegTagMap[reg.Fields.Name] = 1
	}

	for name, tag := range dbRegTagMap {
		if tag == 0 {
			wikiRegDb := dbRegMap[name]
			addReg = append(addReg, RegItem{
				Fields: RegFields{
					Level:     strconv.Itoa(wikiRegDb.Level),
					Name:      wikiRegDb.Name,
					GroupID:   strconv.Itoa(wikiRegDb.GroupID),
					GroupName: wikiRegDb.GroupName,
					Attribute: strconv.Itoa(wikiRegDb.Attribute),
					Enable:    strconv.Itoa(wikiRegDb.Enable),
					Remark:    wikiRegDb.Remark,
					Content:   wikiRegDb.Content,
				},
			})
		}
	}

	if len(delReg) != 0 {
		err = delBiRecord(ctx, delReg, 1)
		if err != nil {
			return err
		}
	}

	if len(addReg) != 0 {
		err = addBiRecord(ctx, addReg, 1, true)
		if err != nil {
			return err
		}
	}

	if len(updateReg) != 0 {
		err = addBiRecord(ctx, updateReg, 1, false)
	}
	if err != nil {
		return err
	}

	log.L(ctx).Infof("fresh bitable success")

	return nil
}
func delBiRecord(ctx context.Context, recordID []string, docType int) error {
	url := "/bitable/v1/apps/" + AppToken + "/tables/" + DocTableId + "/records/batch_delete"

	if docType == 1 {
		url = "/bitable/v1/apps/" + AppToken + "/tables/" + RegTableId + "/records/batch_delete"
	}

	body := map[string]interface{}{
		"records": recordID,
	}

	marshalBody, _ := json.Marshal(body)

	comSrv := newCommon()
	_, err := comSrv.GetDataByReqLarkAPI(ctx, "POST", LarkOpenAPIBase+url, bytes.NewReader(marshalBody), nil, token)
	if err != nil {
		return err
	}

	return nil
}

func addBiRecord(ctx context.Context, docInfo interface{}, docType int, add bool) error {

	comSrv := newCommon()

	url := "/bitable/v1/apps/" + AppToken + "/tables/" + DocTableId + "/records"
	if docType == 1 {
		url = "/bitable/v1/apps/" + AppToken + "/tables/" + RegTableId + "/records"
	}
	if add {
		url += "/batch_create"
	} else {
		url += "/batch_update"
	}

	body := map[string]interface{}{
		"records": docInfo,
	}

	marshalBody, _ := json.Marshal(body)
	_, err := comSrv.GetDataByReqLarkAPI(ctx, "POST", LarkOpenAPIBase+url, bytes.NewReader(marshalBody), nil, token)
	if err != nil {
		return err
	}

	return nil
}

func (dcs *docCheckService) NotifyOwner(ctx context.Context, wikiDocUnsafeMap map[string][]v1.WikiDoc) error {
	approvals, err := dcs.store.Wiki().GetWikiApprovalByStatus(ctx, 0)
	if err != nil {
		log.L(ctx).Errorf("GetWikiApprovals error: %v", err)

		return err
	}

	approvalMap := make(map[string]v1.WikiApproval)
	for _, approval := range approvals {
		approvalMap[approval.Owner] = approval
	}
	for owner, docs := range wikiDocUnsafeMap {
		if _, ok := approvalMap[owner]; ok { // already sent
			continue
		}

		// send lark alert
		apprCode, err := dcs.sendLarkAlert(ctx, owner, len(docs))
		if err != nil {
			log.L(ctx).Errorf("send lark alert error: %v", err)

			return err
		}

		// save wiki approval
		err = dcs.store.Wiki().SaveWikiApproval(ctx, v1.WikiApproval{
			CreatedAt:        time.Now(),
			Owner:            owner,
			ApprovalInstance: apprCode,
			Status:           0, // 0: pending, 1: approved, 2: rejected
		})
	}

	log.L(ctx).Infof("notify owner success")

	return nil
}

func (dcs *docCheckService) sendLarkAlert(ctx context.Context, owner string, docs int) (string, error) {
	log.L(ctx).Infof("send lark alert to owner: %s", owner)

	comSrv := newCommon()

	reqBody := LarkCreateApprovalBody{
		ApprovalCode: DocApprovalCode,
	}

	reqBody.UserId = primaryNotificationLarkUserID
	// reqBody.OpenId = primaryNotificationLarkOpenID

	assistMembers := uniqueCollaborators(owner, primaryNotificationLarkUserID)
	problemMsg := "系统扫描到名下存在" + strconv.Itoa(docs) + "个风险文档，请及时在下面链接上确认文档安全级别。\nReason选择“user confirm”，Level选择合适的级别。\n"
	problemMsg += "The system scanned and found that there are " + strconv.Itoa(docs) + " risky documents in your own. Please confirm the document security level on the link below in time.\n Reason selects \"user confirm\" and Level selects the appropriate level."
	docUrl := "https://pingcap.feishu.cn/base/KOxgbMsTxaqdmRswBkrctFwHnwg?table=tblqNJICnJh0wjGg&view=vewSqquIx1"
	safeDocUrl := "https://pingcap.feishu.cn/docx/XGb7dznYHoFyz4xiDBmcy7G8n6b"

	var resForm []FormItem
	resForm = append(resForm, FormItem{
		Id:    "widget17001199989930001",
		Type:  "date",
		Value: time.Now(), // 当前时间
	}, FormItem{
		Id:    "widget17001200176610001",
		Type:  "input",
		Value: problemMsg, // 问题说明
	}, FormItem{
		Id:    "widget17005506324920001",
		Type:  "input",
		Value: docUrl, // 文档链接
	}, FormItem{
		Id:   "widget17001201593500001",
		Type: "contact",
		OpenIDs: []string{
			owner,
		},
	}, FormItem{
		Id:    "widget17001202301330001",
		Type:  "input",
		Value: safeDocUrl,
	}, FormItem{
		Id:    "widget17001200668490001",
		Type:  "contact",
		Value: assistMembers,
	})

	jsonForm, _ := json.Marshal(resForm)
	reqBody.Form = string(jsonForm)

	marshalBody, _ := json.Marshal(reqBody)

	approvalR, err := comSrv.GetDataByReqLarkAPI(ctx, "POST", LarkOpenAPIBase+"/approval/v4/instances", bytes.NewReader(marshalBody), nil, token)
	if err != nil {
		log.L(ctx).Errorf("error when submit lark approval, err: %s", err.Error())

		return "", err
	}

	var approvalRes ApprovalLarkResData
	err = json.Unmarshal(approvalR, &approvalRes)
	if err != nil {
		log.L(ctx).Errorf("error when unmarshal lark approval response, err: %s", err.Error())

		return "", err
	}

	log.L(ctx).Infof("submit lark approval success, approval id: %s", approvalRes.InstanceCode)

	return approvalRes.InstanceCode, nil
}

type apprResp struct {
	Status string `json:"status"`
}

func (dcs *docCheckService) checkApprStatus(ctx context.Context) error {

	comSrv := newCommon()

	approvals, err := dcs.store.Wiki().GetWikiApprovalByStatus(ctx, 0)
	if err != nil {
		log.L(ctx).Errorf("GetWikiApprovals error: %v", err)

		return err
	}

	url := "/approval/v4/instances/"
	for _, approval := range approvals {
		resp, err := comSrv.GetDataByReqLarkAPI(ctx, "GET", LarkOpenAPIBase+url+approval.ApprovalInstance, nil, nil, token)
		if err != nil {
			log.L(ctx).Errorf("error when get lark approval, err: %s", err.Error())

			return err
		}

		var approvalRes apprResp
		err = json.Unmarshal(resp, &approvalRes)
		if err != nil {
			log.L(ctx).Errorf("error when unmarshal lark approval response, err: %s", err.Error())

			return err
		}

		if approvalRes.Status != "PENDING" {
			approval.Status = 1
			err = dcs.store.Wiki().SaveWikiApproval(ctx, approval)
			if err != nil {
				log.L(ctx).Errorf("error when save wiki approval, err: %s", err.Error())

				return err
			}
		}
	}

	log.L(ctx).Infof("check approval status success")

	return nil
}

func (dcs *docCheckService) SetWikiDocPermission(ctx context.Context) error {
	permissions, err := dcs.store.Wiki().GetWikiPermissions(ctx)
	if err != nil {
		log.L(ctx).Errorf("GetWikiPermissions error: %v", err)

		return err
	}

	permissionMap := make(map[int]v1.WikiPermission)
	for _, permission := range permissions {
		permissionMap[permission.Level] = permission
	}

	wikiDocs, err := dcs.store.Wiki().GetWikiDocsWithConfirm(ctx)
	if err != nil {
		log.L(ctx).Errorf("GetWikiDocsWithConfirm error: %v", err)

		return err
	}

	updateDocs := make([]v1.WikiDoc, 0)
	for _, wikiDoc := range wikiDocs {
		permission, ok := permissionMap[wikiDoc.Level]
		if !ok {
			continue
		}

		permissionPublic := getWikiDocPermission(ctx, wikiDoc.ObjToken, wikiDoc.Type)
		if permissionPublic == nil {
			continue
		}

		var er error
		if permissionPublic.ExternalAccessEntity != permission.ExternalAccessEntity ||
			permissionPublic.SecurityEntity != permission.SecurityEntity ||
			permissionPublic.CommentEntity != permission.CommentEntity ||
			permissionPublic.ShareEntity != permission.ShareEntity ||
			permissionPublic.ManageCollaboratorEntity != permission.ManageCollaboratorEntity ||
			permissionPublic.LinkShareEntity != permission.LinkShareEntity ||
			permissionPublic.CopyEntity != permission.CopyEntity {
			er = setWikiDocPermission(ctx, wikiDoc.ObjToken, wikiDoc.Type, PermissionPublic{
				ExternalAccessEntity:     permission.ExternalAccessEntity,
				SecurityEntity:           permission.SecurityEntity,
				CommentEntity:            permission.CommentEntity,
				ShareEntity:              permission.ShareEntity,
				ManageCollaboratorEntity: permission.ManageCollaboratorEntity,
				LinkShareEntity:          permission.LinkShareEntity,
				CopyEntity:               permission.CopyEntity,
			})
		}
		if er != nil {
			log.L(ctx).Errorf("setWikiDocPermission error: %v", err)
		} else if wikiDoc.Permission == 0 {
			wikiDoc.Permission = 1

			updateDocs = append(updateDocs, wikiDoc)
		}
	}

	err = dcs.store.Wiki().SaveDelWikiDocs(ctx, updateDocs, nil)
	if err != nil {
		log.L(ctx).Errorf("SaveDelWikiDocs error: %v", err)

		return err
	}

	log.L(ctx).Infof("set wiki doc permission success")

	return nil
}

type PermissionResp struct {
	PermissionPublic PermissionPublic `json:"permission_public"`
}

type PermissionPublic struct {
	ExternalAccessEntity     string `json:"external_access_entity"`
	SecurityEntity           string `json:"security_entity"`
	CommentEntity            string `json:"comment_entity"`
	ShareEntity              string `json:"share_entity"`
	ManageCollaboratorEntity string `json:"manage_collaborator_entity"`
	LinkShareEntity          string `json:"link_share_entity"`
	CopyEntity               string `json:"copy_entity"`
}

func getWikiDocPermission(ctx context.Context, docToken, docType string) *PermissionPublic {
	comSrv := newCommon()

	url := "/drive/v2/permissions/" + docToken + "/public"

	params := map[string]string{
		"type": docType,
	}
	resp, err := comSrv.GetDataByReqLarkAPI(ctx, "GET", LarkOpenAPIBase+url, nil, params, token)
	if err != nil {
		log.L(ctx).Errorf("error when get lark approval, err: %s", err.Error())

		return nil
	}

	var permissionResp PermissionResp
	err = json.Unmarshal(resp, &permissionResp)
	if err != nil {
		log.L(ctx).Errorf("error when unmarshal lark approval response, err: %s", err.Error())

		return nil
	}

	return &permissionResp.PermissionPublic
}

func setWikiDocPermission(ctx context.Context, docToken, docType string, permission PermissionPublic) error {
	comSrv := newCommon()

	url := "/drive/v2/permissions/" + docToken + "/public"

	params := map[string]string{
		"type": docType,
	}

	marshalBody, _ := json.Marshal(permission)

	_, err := comSrv.GetDataByReqLarkAPI(ctx, "PATCH", LarkOpenAPIBase+url, bytes.NewReader(marshalBody), params, token)
	if err != nil {
		log.L(ctx).Errorf("error when get lark approval, err: %s", err.Error())

		return err
	}

	log.L(ctx).Infof("setWikiDocPermission success, docToken: %s", docToken)

	return nil
}

type BiMap struct {
	keyToValue map[string]int
	valueToKey map[int]string
}

func NewBiMap() *BiMap {
	return &BiMap{
		keyToValue: make(map[string]int),
		valueToKey: make(map[int]string),
	}
}

func (b *BiMap) Put(key string, value int) {
	b.keyToValue[key] = value
	b.valueToKey[value] = key
}

func (b *BiMap) GetByKey(key string) int {
	value := b.keyToValue[key]
	return value
}

func (b *BiMap) GetByValue(value int) string {
	key := b.valueToKey[value]
	return key
}
