package v1

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

	"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/spf13/viper"
)

type larkCommonService struct{}

type LarkCommonSrv interface {
	GetAndCacheP7STToken() (string, error)
}

type LarkTATokenRes struct {
	Code   int64  `json:"code"`
	Msg    string `json:"msg"`
	TAT    string `json:"tenant_access_token"`
	Expire int    `json:"expire"`
}

type LarkCommonRes struct {
	Code int64           `json:"code"`
	Msg  string          `json:"msg"`
	Data json.RawMessage `json:"data"`
}

const TENANT_TOKEN_KEY = "p7s-tenant-token"
const LARK_OPEN_API_BASE = "https://open.feishu.cn/open-apis"

func newLarkCommon() *larkCommonService {
	return &larkCommonService{}
}

func (lc *larkCommonService) GetAndCacheP7STToken() (string, error) {
	p7sAppIdInLark := viper.GetString("lark.appId")
	p7sAppSecInLark := viper.GetString("lark.appSecret")
	if p7sAppIdInLark == "" || p7sAppSecInLark == "" {
		log.Error("There is no appId or appSecret for p7s-lark-app")
		return "", errors.WithCode(code.ErrNoLarkAppIdSecret, "")
	}

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

	val, err := rStore.GetKey(TENANT_TOKEN_KEY)
	if err != nil {
		if !errors.As(err, &storage.ErrKeyNotFound) {
			log.Error(errors.Wrap(err, "error when retrieve key in redis").Error())
		}
	}

	tToken := val
	if val == "" {
		log.Info("P7S tenant access token not found in redis. Try to get from api")
		tatBody, _ := json.Marshal(map[string]string{
			"app_id":     p7sAppIdInLark,
			"app_secret": p7sAppSecInLark,
		})
		tatReqBody := bytes.NewBuffer(tatBody)
		tatResp, err := http.Post(LARK_OPEN_API_BASE+"/auth/v3/tenant_access_token/internal", "application/json", tatReqBody)
		if err != nil {
			log.Error(err.Error())
			return "", errors.Wrap(err, "error when request lark api for tenant_access_toekn")
		}
		defer tatResp.Body.Close()
		tatRespBody, _ := ioutil.ReadAll(tatResp.Body)
		var tatRes LarkTATokenRes
		if err := json.Unmarshal(tatRespBody, &tatRes); err != nil {
			log.Errorf("Error when unmarshal lark res: %s", err.Error())
			return "", errors.Wrap(err, "error when unmarshal lark tenant_access_token res")
		}

		tToken = tatRes.TAT

		if err := rStore.SetKey(TENANT_TOKEN_KEY, tToken, time.Duration(tatRes.Expire)*time.Second-time.Minute*10); err != nil {
			log.Errorf("Error when store token into redis: %s", err.Error())
		}
	} else {
		log.Info("P7S tenant access token found in redis. Use it from redis.")
	}

	return tToken, nil
}
