package base

import (
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"math/rand"
	"os"
	"strconv"
	"time"

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

	"mongo.games.com/game/common"
	"mongo.games.com/game/model"
	"mongo.games.com/game/proto"
	loginproto "mongo.games.com/game/protocol/login"
	serverproto "mongo.games.com/game/protocol/server"
)

/*
 登录及机器人账号更新
*/

var ClientMgrSingleton = &ClientMgr{
	sessionPool:    make(map[string]*netlib.Session),
	Running:        true,
	CycleTimeEvent: [24][]HourEvent{},
}

type HourEvent struct {
	newAcc  string //wait to open
	oldAcc  string //wait to close
	session *netlib.Session
}
type InvalidAcc struct {
	Acc     string
	Session *netlib.Session
}
type ClientMgr struct {
	sessionPool    map[string]*netlib.Session
	Running        bool
	CycleTimeEvent [24][]HourEvent
}

// 发送登录消息
func (this *ClientMgr) startLogin(acc string, s *netlib.Session) {
	ts := time.Now().UnixNano()
	csLogin := &loginproto.CSLogin{
		Username:  proto.String(acc),
		TimeStamp: proto.Int64(ts),
		Platform:  proto.String(common.Platform_Rob),
		Channel:   proto.String(common.Channel_Rob),
	}
	params := &model.PlayerParams{
		Ip:           fmt.Sprintf("%v.%v.%v.%v", 1+rand.Int31n(255), 1+rand.Int31n(255), 1+rand.Int31n(255), 1+rand.Int31n(255)),
		City:         RandZone(),
		Platform:     1,
		Logininmodel: "app",
	}
	data, err := json.Marshal(params)
	if err == nil {
		csLogin.Params = proto.String(string(data[:]))
	}
	h := md5.New()
	io.WriteString(h, fmt.Sprintf("%v%v", acc, Config.AppId))
	pwd := hex.EncodeToString(h.Sum(nil))
	h.Reset()
	io.WriteString(h, fmt.Sprintf("%v%v%v", pwd, Config.AppId, ts))
	pwd = hex.EncodeToString(h.Sum(nil))
	csLogin.Password = pwd
	csLogin.LoginType = proto.Int32(0)
	csLogin.Sign = proto.String(common.MakeMd5String(csLogin.GetUsername(), csLogin.GetPassword(),
		strconv.Itoa(int(csLogin.GetTimeStamp())), csLogin.GetParams(), Config.AppId))
	s.Send(int(loginproto.LoginPacketID_PACKET_CS_LOGIN), csLogin)
	logger.Logger.Infof("账号 [%v] 开始登录", acc)
}

func (this *ClientMgr) RegisterSession(acc string, s *netlib.Session) {
	this.sessionPool[acc] = s
	StartSessionLoginTimer(s, timer.TimerActionWrapper(func(h timer.TimerHandle, ud interface{}) bool {
		if !s.IsConned() || !this.Running {
			StopSessionLoginTimer(s)
			return false
		}
		this.startLogin(acc, s)
		return true
	}), nil, time.Second*time.Duration(2+rand.Int31n(3)), -1)
}

func (this *ClientMgr) UnRegisterSession(acc string) {
	delete(this.sessionPool, acc)
}

func (this *ClientMgr) HourChange() {
	fileModify := false
	eventArr := this.CycleTimeEvent[time.Now().Hour()]
	for _, event := range eventArr {
		accChan[event.newAcc] = true //使用新的账号
		cfg := NewSessionConfig()
		netlib.Connect(cfg) //创建新的连接
		if session, ok := this.sessionPool[event.oldAcc]; ok && session != nil {
			//删除旧有账号数据
			pack := &serverproto.RWAccountInvalid{
				Acc: proto.String(event.oldAcc),
			}
			session.Send(int(loginproto.LoginPacketID_PACKET_CS_ACCOUNTINVALID), pack) // 删除机器人账号
			//删号标记,不要在断线重连了
			session.SetAttribute(SessionAttributeDelAccount, true)
			//关闭连接
			session.Close()
		}
		//更新本地账号数据信息
		for key, value := range accPool {
			if value.Acc == event.oldAcc {
				accPool[key] = &AccountData{
					Acc:    event.newAcc,
					Create: time.Now().UnixNano(),
					Time:   time.Now(),
				}
				fileModify = true
				break
			}
		}
	}
	this.CycleTimeEvent[time.Now().Hour()] = []HourEvent{}
	if fileModify == true {
		//持久化本次的账号数据
		buff, err := json.Marshal(accPool)
		if err != nil {
			logger.Logger.Error("Marshal account data error:", err)
		} else {
			err := os.WriteFile(accountFileName, buff, os.ModePerm)
			if err != nil {
				logger.Logger.Error("Write robot account file error:", err)
			}
		}
	}
}

func (this *ClientMgr) DayChange() {
	invalidCount := 0                                                           //过期账号数量
	updateLimit := len(accPool) * model.GameParamData.InvalidRobotAccRate / 100 //可更新的账号数量
	invalidAccs := []InvalidAcc{}
	if updateLimit > 0 {
		invalideTime := time.Now().AddDate(0, 0, -model.GameParamData.InvalidRobotDay).UnixNano()
		for _, value := range accPool {
			//检查过期账号
			if value.Create < invalideTime {
				//过期账号
				invalidAccs = append(invalidAccs, InvalidAcc{
					Acc:     value.Acc,
					Session: this.sessionPool[value.Acc],
				})
				invalidCount++
				j := rand.Intn(invalidCount)
				i := invalidCount - 1
				if i != j {
					invalidAccs[i], invalidAccs[j] = invalidAccs[j], invalidAccs[i]
				}
			}
		}
		if len(invalidAccs) >= updateLimit {
			invalidAccs = invalidAccs[:updateLimit]
		}
	}
	//本次需要生成的新账号
	cnt := len(invalidAccs)
	for i := 0; i < cnt; i++ {
		timePoint := i % 24
		eventArr := this.CycleTimeEvent[timePoint]
		eventArr = append(eventArr, HourEvent{
			newAcc:  bson.NewObjectId().Hex(),
			oldAcc:  invalidAccs[i].Acc,
			session: invalidAccs[i].Session,
		})
		this.CycleTimeEvent[timePoint] = eventArr
	}
}
