package player

import (
	"context"
	"fmt"
	clog "github.com/mhaya/logger"

	mhayaCrypto "github.com/mhaya/extend/crypto"
	cstring "github.com/mhaya/extend/string"
	mhayaTime "github.com/mhaya/extend/time"
	cutils "github.com/mhaya/extend/utils"
	"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/mdb"
	"github.com/mhaya/game/game_cluster/internal/mdb/models"
	"github.com/mhaya/game/game_cluster/internal/param"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo/options"
	"log"
	"time"
)

func (p *actorPlayer) cashOut(req *param.CashOutReq) int32 {
	if !p.isOnline {
		clog.Infof("[cashOut] PlayerNotLogin err node=%v", p.App().NodeId())
		return code.PlayerNotLogin
	}

	if p.Player.TonWall == "" {
		return code.TonWallNotExistsError
	}

	/*	errCode := p.VerifyPwd(req.PassWord)
		if errCode > 0 {
			return errCode
		}*/

	num := int(cutils.Float64ByRatioToInt64(req.Amount, constant.MoneyRatio))
	var id int
	switch req.Tp {
	case 1:
		id = models.ItemU
	case 2:
		id = models.ItemTon
	}
	if p.Player.Item[id].Num >= num {
		old := p.Player.Item[id].Num
		p.Player.Item[id].Sub(num)
		rs, err := mdb.MDB.Collection(constant.CNameCashOutRecord).InsertOne(context.Background(), &models.CashOutRecord{
			UserName:    p.Player.UserName,
			NickName:    p.Player.NickName,
			OpenId:      p.Player.OpenId,
			Status:      0,
			Type:        req.Tp,
			AfterAmount: old,
			Amount:      num,
			Address:     p.Player.TonWall,
			CreateAt:    mhayaTime.Now().Unix(),
		})
		if err != nil || rs.InsertedID == "" {
			return code.Error
		}
		p.Player.IsCashOut = 1
		p.dirty = true
		p.cashOutStat(req.Tp, num)

		p.Player.SetPlayerRewardLog(models.SourceCashOut, 0, []data.ItemReward{}, []data.ItemReward{{4, id, num}}, rs.InsertedID)

	} else {
		return code.NotEnoughItemsError
	}

	return code.OK
}

func (p *actorPlayer) setPwd(req *param.SetPwdReq) int32 {
	if !p.isOnline {
		clog.Infof("[setPwd] PlayerNotLogin err node=%v", p.App().NodeId())
		return code.PlayerNotLogin
	}
	if len(p.Player.Password) > 0 {
		return code.PassWordIsExist
	}

	p.Player.Email = req.Mail
	p.Player.Salt = cstring.GetRandString(6)
	p.Player.Password = mhayaCrypto.MD5(req.PassWord + p.Player.Salt)
	p.dirty = true

	return code.OK
}

func (p *actorPlayer) VerifyPwd(pwd string) int32 {
	if p.isOnline {
		if p.Player.Password == mhayaCrypto.MD5(pwd+p.Player.Salt) {
			return code.OK
		}
		p.dirty = true
		p.Player.PwdErrTimes += 1
		if p.Player.PwdErrTimes > 3 {
			p.Player.PwdErrRefreshTime = mhayaTime.Now().Add(24 * time.Hour).Unix()
			return code.PassWordTooManyErrors
		}
	}
	return code.PassWordIsError
}

func (p *actorPlayer) cashOutList(req *param.PageReq) (interface{}, int32) {
	if !p.isOnline {
		clog.Infof("[cashOutList] PlayerNotLogin err node=%v", p.App().NodeId())
		return nil, code.PlayerNotLogin
	}

	findFilter := bson.M{"userName": p.Player.UserName}

	findOptions := options.Find()
	findOptions.SetSkip(int64((req.Page - 1) * 10))
	findOptions.SetLimit(int64(10))
	findOptions.SetSort(bson.D{{"createAt", -1}})

	ctx := context.Background()
	collection := mdb.MDB.Collection(constant.CNameCashOutRecord)
	cursor, err := collection.Find(ctx, findFilter, findOptions)
	if err != nil {
		return nil, code.Error
	}
	defer func() {
		if closeErr := cursor.Close(ctx); closeErr != nil {
			log.Printf("Error closing cursor: %v", closeErr)
		}
	}()

	// 获取总数total
	count, err := collection.CountDocuments(ctx, findFilter)
	if err != nil {
		return nil, code.Error
	}

	// 解析结果
	cashOutRecord := make([]*models.ToUserWithdrawal, 0)
	for cursor.Next(ctx) {
		var admin models.UserWithdrawal
		err = cursor.Decode(&admin)
		if err != nil {
			continue
		}
		cashOutRecord = append(cashOutRecord, admin.To())
	}

	resp := Resp{List: cashOutRecord, Total: count}

	return resp, code.OK
}

type Resp struct {
	List  []*models.ToUserWithdrawal `json:"list"`
	Total int64                      `json:"total"`
}

func (p *actorPlayer) updCashOutStatus(tp int) {
	if !p.isOnline {

		return
	}
	if tp == 1 {
		if p.Player.IsCashOut == 0 {
			return
		}
	}

	findFilter := bson.M{"userName": p.Player.UserName, "state": 0}

	ctx := context.Background()
	cursor, err := mdb.MDB.Collection(constant.CNameCashOutRecord).Find(ctx, findFilter)
	if err != nil {
		return
	}

	defer func() {
		if closeErr := cursor.Close(ctx); closeErr != nil {
			clog.Warnf("Error closing cursor: %v", closeErr)
		}
	}()
	var num int
	for cursor.Next(ctx) {
		var cash models.UserWithdrawal
		err = cursor.Decode(&cash)
		if err != nil {
			continue
		}
		num++
		if cash.Status == 2 || (cash.Status == 1 && cash.Withdrawal == 1) {
			objID := primitive.ObjectID{}
			objID, _ = primitive.ObjectIDFromHex(cash.Id)
			updateCondition := bson.M{"_id": objID}
			_, err = mdb.MDB.Collection(constant.CNameCashOutRecord).UpdateOne(ctx, updateCondition, bson.M{"$set": bson.M{"state": 1, "updateAt": mhayaTime.Now().Unix()}})
			if err != nil {
				continue
			}
			switch cash.Type {
			case 1:
				if cash.Status == 1 {
					p.Player.Item[models.ItemU].RollbackAdd(cash.Amount, 1)
				} else {
					p.Player.Item[models.ItemU].RollbackAdd(cash.Amount, 0)
				}
			case 2:
				if cash.Status == 1 {
					p.Player.Item[models.ItemTon].RollbackAdd(cash.Amount, 1)
				} else {
					p.Player.Item[models.ItemTon].RollbackAdd(cash.Amount, 0)
				}
			}
			num--
		}
	}
	if num == 0 {
		p.Player.IsCashOut = 0
	}
	p.dirty = true
}

func (p *actorPlayer) updCashOutStatusByOld(tp int) {
	if !p.isOnline {
		return
	}
	/*	if tp == 1 {
		if p.Player.IsCashOut == 0 {
			return
		}
	}*/

	findFilter := bson.M{"userName": p.Player.UserName}

	ctx := context.Background()
	cursor, err := mdb.MDB.Collection(constant.CNameCashOutRecord).Find(ctx, findFilter)
	if err != nil {
		return
	}

	defer func() {
		if closeErr := cursor.Close(ctx); closeErr != nil {
			clog.Warnf("Error closing cursor: %v", closeErr)
		}
	}()
	var num int
	for cursor.Next(ctx) {
		var cash models.UserWithdrawal
		err = cursor.Decode(&cash)
		if err != nil {
			continue
		}
		if cash.Status == 0 {
			continue
		}
		if cash.State == 1 {
			continue
		}
		num++
		if cash.Status == 2 || (cash.Status == 1 && cash.Withdrawal == 1) {
			objID := primitive.ObjectID{}
			objID, _ = primitive.ObjectIDFromHex(cash.Id)
			updateCondition := bson.M{"_id": objID}
			_, err = mdb.MDB.Collection(constant.CNameCashOutRecord).UpdateOne(ctx, updateCondition, bson.M{"$set": bson.M{"state": 1, "updateAt": mhayaTime.Now().Unix()}})
			if err != nil {
				continue
			}
			switch cash.Type {
			case 1:
				if cash.Status == 1 {
					p.Player.Item[models.ItemU].RollbackAdd(cash.Amount, 1)
				} else {
					p.Player.Item[models.ItemU].RollbackAdd(cash.Amount, 0)
				}
			case 2:
				if cash.Status == 1 {
					p.Player.Item[models.ItemTon].RollbackAdd(cash.Amount, 1)
				} else {
					p.Player.Item[models.ItemTon].RollbackAdd(cash.Amount, 0)
				}
			}
			num--
		}
	}
	if num == 0 {
		p.Player.IsCashOut = 0
	}
	p.dirty = true
}

// 基于启动项目环境的单元测试
func (p *actorPlayer) unitTest(req *param.UnitTestData) int32 {
	//var wg sync.WaitGroup
	//// 邀请排行榜机器人
	//accounts := mhayaUnit.BuildAccount(100)
	//for _, account := range accounts {
	//	wg.Add(1)
	//	mhayaUnit.InviteRankRobot(account, &wg)
	//}
	//wg.Wait()

	//ret := data.RankConfig.GetAll()
	//ctx := context.Background()
	//i := float64(2*1e12-time.Now().UnixMilli()) / 1e13
	//
	//for _, v := range ret {
	//	key := fmt.Sprintf("%s", constant.RankKey)
	//	key = fmt.Sprintf("%s:%v:%v", key, v.ID, mhayaTime.Now().Add(-24*time.Hour).Format(mhayaTime.DateFormat))
	//	mdb.RDB.ZIncrBy(ctx, key, float64(2000)+i, req.Param1)
	//}

	mdb.RDB.Set(context.Background(), fmt.Sprintf("Invite:1:%v", req.Param1), req.Param2, time.Hour*24*7).Err()
	return code.OK
}
