package JD

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"github.com/gogf/gf/util/gconv"
	"github.com/gomodule/redigo/redis"
	"github.com/guonaihong/gout"
	"github.com/syyongx/php2go"
	"golang_open_platform/pkg/gredis"
	"strconv"
	"time"
)

type JdRequest struct {
	GrantType interface{}`json:"grantType"`
	AppKey interface{} `json:"appKey"`
	UserName interface{} `json:"userName"`
	Password interface{} `json:"password"`
	RequestTime interface{} `json:"requestTime"`
	Sign interface{} `json:"sign"`
	Scope interface{} `json:"scope"`
}

type AccessTokenData struct {
	AccessToken string `json:"accessToken"`
	Time		int64 `json:"time"`
	ExpiresIn   int64 `json:"expires_in"`
	RefreshTokenExpires int64 `json:"RefreshTokenExpires`
}

type Token struct {
}

func (this *Token) getKoken() string {
	//return "E9E647D9DEEF17B1770C02153D8A4DCA"
	token:=this.getTokenCache()
	if(token!=""){
		return token
	}
	return this.getRealToken()
}

//获取真实token
func (this *Token)getRealToken() string {

	requestTime:=strconv.FormatInt(time.Now().UnixNano()/1e6, 10)
	jdRes:=JdRes{}
	jdRequest:=JdRequest{}
	jdRequest.GrantType="accessToken"
	jdRequest.AppKey=Appkey
	jdRequest.UserName=UserName
	jdRequest.Password=php2go.Strtoupper(php2go.Md5(php2go.Base64Encode(Password+requestTime)))
	jdRequest.RequestTime=requestTime
	jdRequest.Sign=this.getSignString(jdRequest)
	jdRequest.Scope=""
	err:=gout.POST(GetTokenUrl).Debug(true).SetForm(jdRequest).BindJSON(&jdRes).Do()
	if(err!=nil){
		panic(err.Error())
	}
	if(jdRes.Success==false){
		msg:=fmt.Sprintf("code:%v,msg:%v",jdRes.ResponseCode,jdRes.ResponseMessage)
		panic(msg)
	}
	if tokenDataMap,ok:= jdRes.ResponseData.(map[string]interface{});ok{
		this.setTokenCache(tokenDataMap)//存token
		return gconv.String(tokenDataMap["accessToken"])//返回token
	}else{
		panic("responseData 不符合格式 ")
	}
}


//设置缓存token
func (this *Token) setTokenCache(tokenMapData map[string]interface{})  {

	redisWriteConn := gredis.Conn("search_w")
	defer redisWriteConn.Close()
	tokenMapData["realExpires"]=gconv.Int64(tokenMapData["expiresIn"])+gconv.Int64(tokenMapData["time"])*1000//毫秒
	bytes,err:=json.Marshal(tokenMapData)
	if(err!=nil){
		RecordErrLog("设置缓存token,json.Marshal 报错,err:"+err.Error())
		panic("")
	}
	_,err=redisWriteConn.Do("Set",jd_client_token_redis_key,string(bytes))
	if(err!=nil){
		RecordErrLog("设置缓存token,redis SET 报错,err:"+err.Error())
		panic("")
	}
}

/**
		1.先从redis读出token的数据(map)
		2.后通过过期时间验证是否过期(或者是否离过期还有一个小时),如果是,返回空
	    3.如果不是,返回token
*/
func (this *Token) getTokenCache() string {

	redisReadConn := gredis.Conn("search_r")
	defer redisReadConn.Close()

	bytes,err:=redis.Bytes(redisReadConn.Do("GET",jd_client_token_redis_key))
	if(err!=nil && err!=redis.ErrNil){
		RecordErrLog("获取缓存token,redis GET 报错,err:"+err.Error())
		panic("")
	}
	if(err==redis.ErrNil||bytes==nil){
		return ""
	}
	tokenInfo:=make(map[string]interface{})
	err=json.Unmarshal(bytes,&tokenInfo)
	if(err!=nil){
		RecordErrLog(jdserver_err,"获取缓存token,json数据有误,err:"+err.Error()+"json:"+string(bytes))
		panic(nil)
	}
	timeNow:=time.Now().Unix()*1000
	realExpires:=gconv.Int64(tokenInfo["realExpires"])
	s:=int64(55*60*1000)
	if(realExpires-timeNow<=s){//距离过期小于等于55分钟就重新获取
		return this.getRealToken()
	}else{
		return gconv.String(tokenInfo["accessToken"])
	}
}

/**
	签名加密
	按照以下顺序将字符串拼接起来
	appKey + userName + password + requestTime + grantType+ secretKey
	将上述拼接的字符串使用 HMACSHA256 加密,
*/
func (this *Token) getSignString(jdRequest JdRequest)  string{
	str:=gconv.String(jdRequest.AppKey)+gconv.String(jdRequest.UserName)+gconv.String(jdRequest.Password)
	str=str+gconv.String(jdRequest.RequestTime)+gconv.String(jdRequest.GrantType)+gconv.String(secretKey)
	hmac:=hmac.New(sha256.New,gconv.Bytes(secretKey))
	hmac.Write([]byte(str))
	return hex.EncodeToString(hmac.Sum([]byte("")))
}