package sdk

import (
	"context"
	"fmt"
	"github.com/go-redis/redis/v8"
	jsoniter "github.com/json-iterator/go"
	mhayaGin "github.com/mhaya/components/gin"
	cerror "github.com/mhaya/error"
	cstring "github.com/mhaya/extend/string"
	mhayaTime "github.com/mhaya/extend/time"
	cfacade "github.com/mhaya/facade"
	"github.com/mhaya/game/game_cluster/internal/code"
	"github.com/mhaya/game/game_cluster/internal/constant"
	"github.com/mhaya/game/game_cluster/internal/data"
	"github.com/mhaya/game/game_cluster/internal/guid"
	"github.com/mhaya/game/game_cluster/internal/mdb"
	"github.com/mhaya/game/game_cluster/internal/mdb/models"
	"github.com/mhaya/game/game_cluster/internal/param"
	sessionKey "github.com/mhaya/game/game_cluster/internal/session_key"
	initdata "github.com/mhaya/game/game_cluster/internal/third/tg_sign"
	"github.com/mhaya/game/game_cluster/internal/token"
	clog "github.com/mhaya/logger"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"sort"
	"time"
)

type quickSdk struct {
	app cfacade.IApplication
}

func (quickSdk) SdkId() int {
	return QuickSDK
}

func (p quickSdk) Login(config *data.SdkConfigRow, params Params, callback Callback) {
	openid, found := params.GetString("openid")
	if found == false || cstring.IsBlank(openid) {
		err := cerror.Error("openid is nil")
		callback(code.LoginError, nil, err)
		return
	}

	ip, found := params.GetString("ip")
	if found == false || cstring.IsBlank(ip) {
		err := cerror.Error("ip is nil")
		callback(code.LoginError, nil, err)
		return
	}

	plt, found := params.GetString("platform")
	if found == false || cstring.IsBlank(plt) {
		err := cerror.Error("platform is nil")
		callback(code.LoginError, nil, err)
		return
	}

	channel, found := params.GetString("channel")
	if found == false || cstring.IsBlank(channel) {
		err := cerror.Error("channel is nil")
		callback(code.LoginError, nil, err)
		return
	}

	sign, _ := params.GetString("sign")

	req := &param.LoginReq{
		OpenID:   openid,
		IP:       ip,
		Platform: plt,
		Channel:  channel,
		Sign:     sign,
	}
	account, err := AccountRegisterOrLogin(req)
	if err > 0 {
		callback(code.LoginError, nil)
		return
	}
	nodeId, ok := SetNode(p.app)
	if code.IsFail(ok) {
		clog.Warnf("[RegisterAccount] openID = %s,nodeID=%v, errCode = %v", openid, nodeId, err)
		callback(code.LoginError, nil)
		return
	}

	targetPath := cfacade.NewChildPath(nodeId, "player"+nodeId, account.UserName)

	callback(code.OK, map[string]interface{}{
		sessionKey.PlayerID:   account.UserName, //返回 quick的uid做为 open id
		sessionKey.OpenID:     openid,
		sessionKey.TargetPath: targetPath,
		"account":             account,
	})
}

func (p quickSdk) Reconnect(token *token.Token) (int32, *token.Token) {
	list := p.app.Discovery().ListByType("game")
	targetPath, err := cfacade.ToActorPath(token.TargetPath)
	if err != nil {
		clog.Warnf("[Reconnect] Target path error. targetPath = %v, error = %v", token.TargetPath, err)
		return code.AccountAuthFail, nil
	}
	isNew := false
	for _, v := range list {
		if v.GetNodeId() == targetPath.NodeID {
			isNew = true
			break
		}
	}
	if !isNew {
		nodeId, ok := SetNode(p.app)
		if code.IsFail(ok) {
			clog.Warnf("[RegisterAccount] openID = %s,nodeID=%v, errCode = %v", token.OpenID, nodeId, err)
			return code.AccountAuthFail, nil
		}
		path := cfacade.NewChildPath(nodeId, "player"+nodeId, token.PlayerID)
		token.TargetPath = path
	}
	return code.OK, token
}

func (p quickSdk) PayCallback(config *data.SdkConfigRow, c *mhayaGin.Context) {
	// TODO 这里实现quick sdk 支付回调的逻辑
	c.RenderHTML("FAIL")
}

func AccountRegisterOrLogin(req *param.LoginReq) (*models.Account, int32) {
	var account models.Account
	ctx := context.Background()
	acc, err := mdb.RDB.Get(ctx, fmt.Sprintf("%v:%v", constant.CNameAccount, req.OpenID)).Bytes()
	if err != nil && err != redis.Nil {
		clog.Debugf("[Account] AccountRegisterOrLogin error: %v", err)
	}
	findFilter := bson.M{"openId": req.OpenID}
	if len(acc) > 0 {
		err = jsoniter.Unmarshal(acc, &account)
		if err != nil {
			clog.Debugf("[Account] unmarshal AccountRegisterOrLogin data error: %v", err)
			return nil, code.LoginError
		}
	} else {
		err = mdb.MDB.Collection(constant.CNameAccount).FindOne(context.Background(), findFilter).Decode(&account)
		if err != nil && err != mongo.ErrNoDocuments {
			clog.Errorf("Failed to AccountRegisterOrLogin select err request: %v  err = %v", req, err.Error())
			return nil, code.LoginError
		}
	}
	// todo 测试代码
	curLvID, _ := mdb.RDB.Get(context.Background(), constant.RocketLvKey).Int()
	if curLvID < 100 {
		curLvID = 100
		mdb.RDB.Set(context.Background(), constant.RocketLvKey, curLvID, 0)
	}

	user := initdata.GetUserInfo(req.Sign)

	//设置IP 规则
	num, ok := models.SetPlayerBlacklistIpRecord(req.IP, req.OpenID)
	if ok {
		clog.Errorf("Failed to SetPlayerBlacklistIpRecord err request: %v,num:%v", req, num)
		return nil, code.LoginError
	}

	if account.OpenId != "" {
		if account.Platform == "on" {
			//统计新注册
			models.SetAppointDailyRecordNewUserRegisterHash(req.Platform, req.Channel, account.UserName, account.JoinIp, mhayaTime.CreateFromTimestamp(account.JoinTime).Unix(), models.DailyRecordNewRegistered)
		}
		if (len(user.Username) > 0 && account.Account != user.Username) || user.IsPremium != account.IsPremium || req.IP != account.LoginIp || account.Platform != req.Platform {
			account.Account = user.Username
			account.IsPremium = user.IsPremium
			account.LoginIp = req.IP
			account.Platform = req.Platform
			//mdb.MDB.Collection(constant.CNameAccount).UpdateOne(context.Background(), findFilter, bson.M{"$set": bson.M{"platform": req.Platform, "account": user.Username, "isPremium": user.IsPremium, "loginIp": req.IP}})
			jsonData, _ := jsoniter.Marshal(&account)
			err = mdb.RDB.Set(context.Background(), fmt.Sprintf("%v:%v", constant.CNameAccount, account.OpenId), jsonData, time.Hour*3*24).Err()

			err = mdb.RDB.HSet(ctx, constant.NewAccounts, account.OpenId, 0).Err()
			if err != nil {
				clog.Errorf("Failed to AccountRegisterOrLogin request: %v  err = %v", account, err.Error())
				return nil, code.LoginError
			}
		}
		return &account, code.OK
	}

	devAccountTable := &models.Account{
		UserName:  guid.Next(),
		OpenId:    req.OpenID,
		Platform:  req.Platform,
		Channel:   req.Channel,
		Account:   user.Username,
		IsPremium: user.IsPremium,
		IsNew:     true,
		LoginIp:   req.IP,
		JoinIp:    req.IP,
		JoinTime:  mhayaTime.Now().Unix(),
	}
	player := models.NewPlayer(token.New(req.Pid, req.OpenID, devAccountTable.UserName, "", "", ""))
	jsonData, err := jsoniter.Marshal(&player)
	if err != nil {
		clog.Errorf("Failed to AccountRegisterOrLogin request: %v  err = %v", devAccountTable, err.Error())
		return nil, code.LoginError
	}
	err = mdb.RDB.Set(ctx, fmt.Sprintf("%v:%v", constant.CNamePlayer, req.OpenID), jsonData, time.Hour*3*24).Err()
	if err != nil {
		clog.Errorf("Failed to AccountRegisterOrLogin request: %v  err = %v", devAccountTable, err.Error())
		return nil, code.LoginError
	}

	jsonData, err = jsoniter.Marshal(&devAccountTable)
	if err != nil {
		clog.Errorf("Failed to AccountRegisterOrLogin  request: %v  err = %v", devAccountTable, err.Error())
		return nil, code.LoginError
	}

	err = mdb.RDB.Set(context.Background(), fmt.Sprintf("%v:%v", constant.CNameAccount, devAccountTable.OpenId), jsonData, time.Hour*3*24).Err()
	if err != nil {
		clog.Errorf("Failed to AccountRegisterOrLogin request: %v  err = %v", devAccountTable, err.Error())
		return nil, code.LoginError
	}

	err = mdb.RDB.HSet(ctx, constant.NewAccounts, devAccountTable.OpenId, 1).Err()
	if err != nil {
		clog.Errorf("Failed to AccountRegisterOrLogin request: %v  err = %v", devAccountTable, err.Error())
		return nil, code.LoginError
	}

	/*	_, err = mdb.MDB.Collection(constant.CNameAccount).InsertOne(context.Background(), devAccountTable)
		if err != nil {
			clog.Errorf("Failed to AccountRegisterOrLogin request: %v  err = %v", devAccountTable, err.Error())
			return nil, code.LoginError
		}*/

	//统计新注册
	models.SetDailyRecordNewUserRegisterHash(req.Platform, req.Channel, devAccountTable.UserName, req.IP, models.DailyRecordNewRegistered)

	// 新用户触发币安升级逻辑
	// 当前级别
	lvID, _ := mdb.RDB.Get(context.Background(), constant.RocketLvKey).Int()
	userNum := mdb.RDB.IncrBy(context.Background(), constant.TotalUserKey, 1).Val()
	/*	// 当前用户数
		userNum, _ := mdb.MDB.Collection(constant.CNamePlayer).CountDocuments(context.Background(), bson.M{})*/

	roConfig := data.RocketLvConfig.GetAll()

	// 当前经验
	var curExperience int64
	// 当前升级所需经验
	var curLvNeed int64

	keys := []int{}
	for _, row := range roConfig {
		keys = append(keys, row.ID)
	}
	sort.Ints(keys)

	for _, id := range keys {
		lvRow := roConfig[id]
		if id == lvID {
			curLvNeed = int64(lvRow.LvNeed)
			break
		}
		curExperience += int64(lvRow.LvNeed)
	}

	//curLvID, _ := mdb.RDB.Get(context.Background(), constant.RocketLvKey).Int()
	//if curLvID < 100 {
	//	curLvID = 100
	//}

	if userNum-curExperience >= curLvNeed {
		// 触发升级

		// 并发控制
		if lvID == curLvID {
			mdb.RDB.Set(context.Background(), constant.RocketLvKey, curLvID+1, 0)
		}
	}

	return devAccountTable, code.OK
}
