package v1

import (
	"encoding/json"
	"io/ioutil"
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/marmotedu/errors"
	"github.com/pingcap-inc/pinsights/internal/pkg/code"
	"github.com/pingcap-inc/pinsights/internal/pkg/store"
	"github.com/pingcap-inc/pinsights/pkg/log"
	"github.com/pingcap-inc/pinsights/pkg/storage"
	"github.com/pingcap-inc/pinsights/pkg/util/jsonc"
)

type WhiteListSrv interface {
	RefreshByEvent(c *gin.Context, evt *EventSchemaV2) error
	Refresh() error
}

type whiteListSrv struct{}

var _ WhiteListSrv = (*whiteListSrv)(nil)

func newWhiteListSrv(srv *service) *whiteListSrv {
	return &whiteListSrv{}
}

const ASKP7S_CHAT_ID = "oc_40a7bbff357fafd708220cbd20930732"

type ChatMemUID struct {
	UserId string `json:"userId"`
}

type ChatMemUser struct {
	UserId ChatMemUID `json:"userId"`
}

type MemEventContent struct {
	ChatId string        `json:"chatId"`
	Users  []ChatMemUser `json:"users"`
}

type MemItem struct {
	MemberIdType string `json:"memberIdType"`
	MemberId     string `json:"memberId"`
	Name         string `json:"name"`
}

type MembersData struct {
	Items     []MemItem `json:"items"`
	PageToken string    `json:"pageToken"`
	HasMore   bool      `json:"hasMore"`
}

const WHITE_LIST_MID_KEY = "white-list-larkid-"

func (w *whiteListSrv) RefreshByEvent(c *gin.Context, evt *EventSchemaV2) error {
	log.Info("Refresh whitelist by event.")

	var eContent MemEventContent
	if err := json.Unmarshal(jsonc.ConvertJSON2Camel(evt.Event), &eContent); err != nil {
		log.Error(err.Error())
		return errors.Wrap(err, "Error when unmarshal event content")
	}

	// if not ASK P7S group, do nothing
	if eContent.ChatId != ASKP7S_CHAT_ID || len(eContent.Users) == 0 {
		return nil
	}

	isRemove := true
	if evt.Header.EventType == "im.chat.member.user.added_v1" {
		isRemove = false
	}

	rStore := &storage.RedisCluster{
		KeyPrefix: store.P7SKeyPrefix,
	}

	for _, u := range eContent.Users {
		if u.UserId.UserId == "" {
			continue
		}
		if isRemove {
			log.Info("Remove user " + u.UserId.UserId + " from whitelist-larkid")
			rStore.DeleteKey(WHITE_LIST_MID_KEY + u.UserId.UserId)
		} else {
			log.Info("Add user " + u.UserId.UserId + " into whitelist-larkid")
			rStore.SetKey(WHITE_LIST_MID_KEY+u.UserId.UserId, "y", 0)
		}
	}

	return nil
}

func (w *whiteListSrv) Refresh() error {
	log.Info("Try to refresh whitelist from ask p7s group")

	larkCommonService := newLarkCommon()
	tat, err := larkCommonService.GetAndCacheP7STToken()
	if err != nil {
		log.Error(err.Error())
		return errors.Wrap(err, "Error when get lark userInfo bacause of the error in GetAndCacheP7STToken")
	}

	mItems, err := getChatMembers(tat, "")
	if err != nil {
		log.Error(err.Error())
		return errors.Wrap(err, "Error when get lark chat member bacause of the error in getChatMembers")
	}

	log.Info("Success to get ask p7s group members.")

	rStore := &storage.RedisCluster{
		KeyPrefix: store.P7SKeyPrefix,
	}

	if ok := rStore.DeleteScanMatch(store.P7SKeyPrefix + WHITE_LIST_MID_KEY + "*"); !ok {
		return errors.New("Can not delete old keys")
	}

	for _, it := range *mItems {
		rStore.SetKey(WHITE_LIST_MID_KEY+it.MemberId, "y", 0)
	}

	return nil
}

func getChatMembers(tat string, pageToken string) (*[]MemItem, error) {
	var pageTokenQ string
	if pageToken != "" {
		pageTokenQ = "&page_token=" + pageToken
	}

	req, err := http.NewRequest("GET", LARK_OPEN_API_BASE+"/im/v1/chats/"+ASKP7S_CHAT_ID+"/members?member_id_type=user_id&page_size=100"+pageTokenQ, nil)
	if err != nil {
		log.Error(err.Error())
		return nil, errors.Wrap(err, "Error when contruct request for chat member")
	}
	req.Header.Set("Authorization", "Bearer "+tat)
	resp, err := (&http.Client{}).Do(req)
	if err != nil {
		log.Error(err.Error())
		return nil, errors.Wrap(err, "Error when request lark user api")
	}
	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)
	var res LarkCommonRes
	if err := json.Unmarshal(jsonc.ConvertJSON2Camel(body), &res); err != nil {
		log.Error(err.Error())
		return nil, errors.Wrap(err, "Error when unmarshal lark get chat member res")
	}
	if res.Code != 0 {
		log.Errorf("Lark API error code: %d, msg: %s", res.Code, res.Msg)
		return nil, errors.WithCode(code.ErrLarkApiBizErrorCode, "Lark Error Code: %d", res.Code)
	}

	var d MembersData
	if err := json.Unmarshal(jsonc.ConvertJSON2Camel(res.Data), &d); err != nil {
		log.Error(err.Error())
		return nil, errors.Wrap(err, "Error when unmarshal lark get chat member res")
	}

	var result []MemItem
	result = append(result, d.Items...)
	if d.HasMore && d.PageToken != "" {
		mItems, err := getChatMembers(tat, d.PageToken)
		if err != nil {
			log.Error(err.Error())
			return nil, errors.Wrap(err, "Error when getChatMembers for next page")
		}
		result = append(result, *mItems...)
	}

	return &result, nil
}
