package svc

import (
	"errors"
	"net/rpc"
	"time"

	"github.com/globalsign/mgo"
	"github.com/globalsign/mgo/bson"
	"mongo.games.com/goserver/core/logger"

	"mongo.games.com/game/common"
	"mongo.games.com/game/dbproxy/mongo"
	"mongo.games.com/game/model"
	rankproto "mongo.games.com/game/protocol/rank"
)

var (
	InviteScoreDBName   = "user"
	InviteScoreCollName = "user_invitescore"
	InviteScoreColError = errors.New("InviteScore collection open failed")
)

func InviteScoreCollection(plt string) *mongo.Collection {
	s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, InviteScoreDBName)
	if s != nil {
		c, first := s.DB().C(InviteScoreCollName)
		if first {
			c.EnsureIndex(mgo.Index{Key: []string{"snid"}, Background: true, Sparse: true})
			c.EnsureIndex(mgo.Index{Key: []string{"invitesnid"}, Background: true, Sparse: true})
			c.EnsureIndex(mgo.Index{Key: []string{"tp"}, Background: true, Sparse: true})
			c.EnsureIndex(mgo.Index{Key: []string{"ts"}, Background: true, Sparse: true})
		}
		return c
	}

	return nil
}

type BindScoreSvc struct {
}

func (b *BindScoreSvc) GetInviteScore(req *model.InviteScoreReq, ret *model.InviteScoreRet) error {
	c := InviteScoreCollection(req.Platform)
	if c == nil {
		return InviteScoreColError
	}

	type M struct {
		Score int64
	}

	var tc []M
	err := c.Pipe([]bson.M{
		{"$match": bson.M{
			"invitesnid": req.SnId,
		}},
		{"$group": bson.M{
			"_id":   nil,
			"score": bson.M{"$sum": "$score"},
		}},
	}).AllowDiskUse().All(&tc)
	if err != nil {
		logger.Logger.Error("GetInviteScore AllowDiskUse is error", err)
		return err
	}

	if len(tc) > 0 {
		ret.Score = tc[0].Score
		c := PlayerDataCollection(req.Platform)
		if c == nil {
			return PlayerColError
		}
		err = c.Update(bson.M{"snid": req.SnId}, bson.M{"$set": bson.M{"invitescore": tc[0].Score}})
		if err != nil {
			logger.Logger.Error("GetInviteScore update invitescore is error", err)
			return err
		}
	}

	tc = tc[:0]
	err = c.Pipe([]bson.M{
		{"$match": bson.M{
			"invitesnid": req.SnId,
			"score":      bson.M{"$gt": 0},
		}},
		{"$group": bson.M{
			"_id":   nil,
			"score": bson.M{"$sum": "$score"},
		}},
	}).AllowDiskUse().All(&tc)
	if err != nil {
		logger.Logger.Error("GetInviteScore z AllowDiskUse is error", err)
		return err
	}

	if len(tc) > 0 {
		ret.ZScore = tc[0].Score
	}

	return nil
}

func (b *BindScoreSvc) SaveInviteScore(req *model.InviteScore, ret *bool) error {
	logger.Logger.Tracef("SaveInviteScore req:%+v", *req)
	if req.InviteSnId == 0 {
		return nil
	}
	req.Id = bson.NewObjectId()
	u := PlayerDataCollection(req.Platform)
	if u == nil {
		return PlayerColError
	}

	type M struct {
		InviteScore int64
	}
	r := new(M)
	err := u.Find(bson.M{"snid": req.InviteSnId}).Select(bson.M{"invitescore": 1}).One(r)
	if err != nil && !errors.Is(err, mgo.ErrNotFound) {
		logger.Logger.Errorf("GetInviteScore Find error:%v", err)
		return err
	}

	if req.Score < 0 {
		if -req.Score > r.InviteScore {
			req.Score = -r.InviteScore
		}
	}

	c := InviteScoreCollection(req.Platform)
	if c == nil {
		return InviteScoreColError
	}

	err = c.Insert(req)
	if err != nil {
		logger.Logger.Errorf("SaveInviteScore Insert error:%v", err)
		return err
	}

	err = u.Update(bson.M{"snid": req.InviteSnId}, bson.M{"$inc": bson.M{"invitescore": req.Score}})
	if err != nil {
		logger.Logger.Errorf("inc InviteScore error:%v", err)
		return err
	}

	*ret = true
	return nil
}

func (b *BindScoreSvc) GetInviteRankList(req *model.FindPlayerRankInviteListArgs, ret *model.FindPlayerRankInviteListReply) error {
	c := InviteScoreCollection(req.Platform)
	if c == nil {
		return InviteScoreColError
	}

	NowTime := time.Now().Local()
	endTime := NowTime.UnixNano()
	startTime := NowTime.AddDate(-100, 0, 0).UnixNano()

	year, month, day := NowTime.Date()
	today := time.Date(year, month, day, 0, 0, 0, 0, time.Local)
	if req.RankType == int32(rankproto.RankInvite_InviteType_Week) {
		// 本周起始日期（周日）
		startTime = today.AddDate(0, 0, -int(today.Weekday())).UnixNano()
	} else if req.RankType == int32(rankproto.RankInvite_InviteType_Month) {
		// 本月起始日期
		startTime = time.Date(year, month, 1, 0, 0, 0, 0, time.Local).UnixNano()
		//startTime = NowTime.AddDate(0, 0, -NowTime.Day()+1).UnixNano()
	}

	type M struct {
		InviteSnId int32 // 邀请人id
		Score      int64 // 积分
	}

	var tc []M
	err := c.Pipe([]bson.M{
		{"$match": bson.M{
			"score": bson.M{"$gt": 0},
			"ts":    bson.M{"$gte": startTime, "$lte": endTime},
		}},
		{"$group": bson.M{
			"_id": bson.M{
				"invitesnid": "$invitesnid",
			},
			"invitesnid": bson.M{"$first": "$invitesnid"},
			"score":      bson.M{"$sum": "$score"},
		}},
		{
			"$sort": bson.M{"ts": -1},
		},
		{
			"$sort": bson.M{"score": -1},
		},
		{
			"$limit": model.GameParamData.RankInviteMaxNum,
		},
	}).AllowDiskUse().All(&tc)
	if err != nil {
		logger.Logger.Error("GetInviteRankList z AllowDiskUse is error", err)
		return err
	}

	var conds []int32
	if len(tc) > 0 {

		type PInfo struct {
			SnId  int32
			Name  string // 昵称
			Roles *model.RolePetInfo
		}

		var retPlayerList []PInfo

		cplayerdata := PlayerDataCollection(req.Platform)
		if cplayerdata == nil {
			return err
		}

		for i := 0; i < len(tc); i++ {
			conds = append(conds, tc[i].InviteSnId)
		}

		selecter := bson.M{"snid": bson.M{"$in": conds}}
		//err = cplayerdata.Find(selecter).Select(bson.M{"snid": 1, "name": 1, "roles": 1}).All(&retPlayerList)
		err = cplayerdata.Find(selecter).Select(bson.M{"snid": 1, "name": 1, "roles": 1}).All(&retPlayerList)
		if err != nil {
			logger.Logger.Error("svc.FindInvitePlayerList is error", err)
			return nil
		}

		for _, inviteInfo := range tc {
			for _, playerData := range retPlayerList {
				if inviteInfo.InviteSnId == playerData.SnId {
					var RankInvite model.PlayerRankInvite
					RankInvite.Name = playerData.Name
					RankInvite.Score = inviteInfo.Score
					RankInvite.SnId = inviteInfo.InviteSnId

					// 头像模型ID
					roleId := common.DefaultRoleId
					if playerData.Roles != nil {
						roleId = int(playerData.Roles.ModId)
					}
					RankInvite.ModId = int32(roleId)

					RankInvite.InviteNum, _ = GetInviteNum(req.Platform, inviteInfo.InviteSnId)
					ret.List = append(ret.List, &RankInvite)

					break
				}
			}
		}
		ret.RankType = req.RankType
	}

	return nil
}

func (b *BindScoreSvc) GetInviteList(req *model.InviteLisArgs, ret *model.InviteListRet) error {
	c := InviteScoreCollection(req.Platform)
	if c == nil {
		return InviteScoreColError
	}

	type M struct {
		SnId  int32
		Score int64
	}

	var tc []M
	err := c.Pipe([]bson.M{
		{"$match": bson.M{
			"invitesnid": req.SnId,
		}},
		{"$group": bson.M{
			"_id": bson.M{
				"snid": "$snid",
			},
			"snid":  bson.M{"$first": "$snid"},
			"score": bson.M{"$sum": "$score"},
		}},
		{
			"$sort": bson.M{
				"score": -1,
			},
		},
	}).AllowDiskUse().All(&tc)
	if err != nil {
		logger.Logger.Error("GetInviteList AllowDiskUse is error", err)
		return err
	}

	u := PlayerDataCollection(req.Platform)
	if u == nil {
		return PlayerColError
	}

	for _, v := range tc {
		d := &model.PlayerBaseInfo2{}
		err = u.Find(bson.M{"snid": v.SnId}).Select(bson.M{"name": 1, "createtime": 1, "roles": 1}).One(d)
		if err != nil {
			logger.Logger.Warnf("GetInviteList Find player is error:%v", err)
		}
		roleId := common.DefaultRoleId
		if d.Roles != nil && d.Roles.ModId != 0 {
			roleId = int(d.Roles.ModId)
		}
		ret.List = append(ret.List, &model.InviteInfo{
			Name:     d.Name,
			SnId:     v.SnId,
			CreateTs: d.CreateTime.Unix(),
			Score:    v.Score,
			ModId:    int32(roleId),
		})
	}

	return nil
}

func init() {
	rpc.Register(new(BindScoreSvc))
}
