package player

import (
	"context"
	"fmt"
	jsoniter "github.com/json-iterator/go"
	cconst "github.com/mhaya/const"
	cstring "github.com/mhaya/extend/string"
	cfacade "github.com/mhaya/facade"
	"github.com/mhaya/game/game_cluster/internal/constant"
	"github.com/mhaya/game/game_cluster/internal/event"
	"github.com/mhaya/game/game_cluster/internal/mdb"
	"github.com/mhaya/game/game_cluster/internal/mdb/models"
	"github.com/mhaya/game/game_cluster/nodes/game/module/online"
	clog "github.com/mhaya/logger"
	"github.com/mhaya/net/parser/pomelo"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"log"
	"time"
)

type (
	// ActorPlayers 玩家总管理actor
	ActorPlayers struct {
		pomelo.ActorBase
		Name          string
		childExitTime time.Duration
	}
)

func (p *ActorPlayers) AliasID() string {
	return "player" + p.Name
}

func (p *ActorPlayers) OnInit() {
	p.childExitTime = time.Minute * 10
	// 注册角色登陆事件
	p.Event().Register(event.PlayerLoginKey, p.onLoginEvent)
	p.Event().Register(event.PlayerLogoutKey, p.onLogoutEvent)
	p.Event().Register(event.PlayerCreateKey, p.onPlayerCreateEvent)
	p.Remote().Register("checkChild", p.checkChild)

	p.Timer().AddOnce(20*time.Second, p.loadServer)
	//p.Timer().Add(2*time.Minute, p.SetDb)
}

func (p *ActorPlayers) loadServer() {
	//初始节点
	models.SetServerRecord(p.App().NodeId(), 0)
	mdb.RDB.Del(context.Background(), fmt.Sprintf("%v:%v", constant.CNamePlayer, p.App().NodeId()))
}

func (p *ActorPlayers) SetDb() {
	go p.OpDb()
}

func (p *ActorPlayers) OpDb() {
	res := mdb.RDB.HGetAll(context.Background(), fmt.Sprintf("%v:%v", constant.CNamePlayer, p.App().NodeId())).Val()
	newPlayer := mdb.RDB.HGetAll(context.Background(), constant.NewPlayers).Val()
	bulkUpdOps := []mongo.WriteModel{}
	bulkInsOps := []mongo.WriteModel{}
	np := []string{}
	for k, v := range res {
		var player CachePlayer
		if len(v) > 0 {
			err := jsoniter.Unmarshal([]byte(v), &player)
			if err != nil {
				clog.Debugf("[ActorPlayers] unmarshal player data error: %v", err)
				return
			}
			if player.Dirty {
				if _, ok := newPlayer[k]; ok {
					np = append(np, k)
					bulkInsOps = append(bulkInsOps, mongo.NewInsertOneModel().SetDocument(player.Player))
				} else {
					filter := bson.M{"openId": player.Player.OpenId}
					update := bson.M{"$set": player.Player}
					bulkUpdOps = append(bulkUpdOps, mongo.NewUpdateOneModel().SetFilter(filter).SetUpdate(update))
				}
			}
		}
	}
	if len(bulkInsOps) > 0 {
		_, err := mdb.MDB.Collection(constant.CNamePlayer).BulkWrite(context.Background(), bulkInsOps)
		if err != nil {
			log.Fatalf("Bulk write failed: %v", err)
		}
		for _, op := range np {
			mdb.RDB.HDel(context.Background(), constant.NewPlayers, op)
		}

	}
	if len(bulkUpdOps) > 0 {
		_, err := mdb.MDB.Collection(constant.CNamePlayer).BulkWrite(context.Background(), bulkUpdOps)
		if err != nil {
			clog.Fatalf("Bulk write failed: %v", err)
		}
	}
}

func (p *ActorPlayers) OnFindChild(msg *cfacade.Message) (cfacade.IActor, bool) {
	// 动态创建 player child actor
	childID := msg.TargetPath().ChildID
	childActor, err := p.Child().Create(childID, &actorPlayer{
		isOnline: false,
	})

	if err != nil {
		return nil, false
	}

	return childActor, true
}

// cron
func (p *ActorPlayers) everySecondTimer() {
	p.Call(p.PathString(), "checkChild", nil)
}

func (p *ActorPlayers) checkChild() {
	// 扫描所有玩家actor
	p.Child().Each(func(iActor cfacade.IActor) {
		child, ok := iActor.(*actorPlayer)
		if !ok {
			return
		}

		if child.isOnline {
			return
		}

		// 玩家下线，并且超过childExitTime时间没有收发消息，则退出actor
		deadline := time.Now().Add(-p.childExitTime).Unix()
		if child.LastAt() < deadline {
			child.Exit() //actor退出
		}
	})
}

// onLoginEvent 玩家登陆事件处理
func (p *ActorPlayers) onLoginEvent(e cfacade.IEventData) {
	evt, ok := e.(*event.PlayerLogin)
	if ok == false {
		return
	}

	node, ok := cstring.SplitIndex(evt.TargetPath, cconst.DOT, 0)
	if ok && len(node) > 0 {
		models.SetServerRecord(node, online.Count())
	}

	clog.Infof("[PlayerLoginEvent] [playerId = %v,openid = %v,TargetPath=%v,onlineCount = %v]",
		evt.PlayerId,
		evt.OpenID,
		evt.TargetPath,
		online.Count(),
	)
}

// onLoginEvent 玩家登出事件处理
func (p *ActorPlayers) onLogoutEvent(e cfacade.IEventData) {
	evt, ok := e.(*event.PlayerLogout)
	if !ok {
		return
	}

	node, ok := cstring.SplitIndex(evt.TargetPath, cconst.DOT, 0)
	if ok && len(node) > 0 {
		models.SetServerRecord(node, online.Count())
	}

	clog.Infof("[PlayerLogoutEvent] [playerId = %v,TargetPath=%v, onlineCount = %v]",
		evt.PlayerId,
		evt.TargetPath,
		online.Count(),
	)
}

// onPlayerCreateEvent 玩家创建事件
func (p *ActorPlayers) onPlayerCreateEvent(e cfacade.IEventData) {
	evt, ok := e.(*event.PlayerCreate)
	if !ok {
		return
	}

	clog.Infof("[PlayerCreateEvent] [%+v]", evt)
}

func (p *ActorPlayers) OnStop() {
	clog.Infof("onlineCount = %d", online.Count())
}
