package game

import (
	"fmt"
	"reflect"
	"server/MergeConst"
	"server/conf"
	"server/game/internal"
	GoUtil "server/game_util"
	"strings"
	"time"

	"server/msg"

	"server/pkg/github.com/name5566/leaf/gate"
	"server/pkg/github.com/name5566/leaf/log"

	"google.golang.org/protobuf/proto"

	"server/db"
)

var (
	Module  = new(internal.Module)
	ChanRPC = internal.ChanRPC
)

func handler(m interface{}, h interface{}) {
	Module.Skeleton.RegisterChanRPC(reflect.TypeOf(m), h)
}

func RegisterHandlerRPC(param []interface{}) {
	handler(&msg.ClientReq{}, HandleClientReq)
	handler(&msg.AdminReq{}, HandleAdminReq)
}

func init() {
	register()
	db.InitDB()
	db.InitRedis()
	// db.TestGetAllKey()
	GoUtil.RegisterEvent("GameModuleOnInit", RegisterHandlerRPC, nil)
	G_getGameLogic()
}

func HandleAdminReq(args []interface{}) {
	m := args[0].(*msg.AdminReq)
	// 消息的发送者
	a := args[1].(gate.Agent)
	buf := m.GetInfo()
	// log.Debug("admin 消息Func : %s", m.GetFunc())
	AdminProcess(m.GetFunc(), []interface{}{a, buf})
}

func HandleClientReq(args []interface{}) {
	start := time.Now()
	if G_GameLogicPtr.SeverInfo.Status == SERVER_STATUS_CLOSE || G_GameLogicPtr.SeverInfo.Status == SERVER_STATUS_MAINTAIN {
		return // 服务器关闭或者维护中，不处理任何消息
	}
	m := args[0].(*msg.ClientReq)
	// 消息的发送者
	a := args[1].(gate.Agent)
	buf := m.GetInfo()
	// log.Debug("消息Func : %s", m.GetFunc())
	switch m.GetFunc() {
	case "ClientTick":
	case "ReqChangePassword":
		detail := &msg.ReqChangePassword{}
		proto.Unmarshal(buf, detail)
		accountInfo := db.GetAccountInfoFromDb(detail.UserName)
		if accountInfo == nil {
			ResChangePassword := &msg.ResChangePassword{}
			ResChangePassword.ResultCode = MergeConst.Protocol_Error_Account_NoExsit
			data, _ := proto.Marshal(ResChangePassword)
			G_GameLogicPtr.PackResInfo(a, "ResChangePassword", data)
			return
		}
		if accountInfo.UserPassword != detail.OldPwd && detail.OldPwd != "-1" {
			ResChangePassword := &msg.ResChangePassword{}
			ResChangePassword.ResultCode = MergeConst.Protocol_Error_Account_OR_PWD_ERROR
			data, _ := proto.Marshal(ResChangePassword)
			G_GameLogicPtr.PackResInfo(a, "ResChangePassword", data)
			return
		}
		if strings.Count(detail.NewPwd, "")-1 < 6 {
			ResChangePassword := &msg.ResChangePassword{}
			ResChangePassword.ResultCode = MergeConst.Protocol_Error_Account_OR_PWD_Short
			data, _ := proto.Marshal(ResChangePassword)
			G_GameLogicPtr.PackResInfo(a, "ResChangePassword", data)
			return
		}
		accountInfo.UserPassword = detail.NewPwd
		err := db.UpdateAccountInfoToDb(accountInfo)
		if err != nil {
			ResChangePassword := &msg.ResChangePassword{}
			ResChangePassword.ResultCode = MergeConst.Protocol_Error_Account_Fail
			data, _ := proto.Marshal(ResChangePassword)
			G_GameLogicPtr.PackResInfo(a, "ResChangePassword", data)
			return
		}
		ResChangePassword := &msg.ResChangePassword{}
		ResChangePassword.ResultCode = 0
		data, _ := proto.Marshal(ResChangePassword)
		G_GameLogicPtr.PackResInfo(a, "ResChangePassword", data)
	case "ReqAdminInfo": // 后台接口
		AdminProcess(m.GetFunc(), []interface{}{a, buf})
	case "ReqLoginCode":
		Detail := &msg.ReqLoginCode{}
		proto.Unmarshal(buf, Detail)
		Code, err := GeneratedCode(Detail.TelPhone)
		ResLoginCode := &msg.ResLoginCode{}
		if err != nil {
			ResLoginCode.ResultCode = MergeConst.Protocol_Error_Account_Code_Error
			ResLoginCode.Msg = err.Error()
		}
		ResLoginCode.Code = Code
		data, _ := proto.Marshal(ResLoginCode)
		G_GameLogicPtr.PackResInfo(a, "ResLoginCode", data)
	case "ReqServerVersion":
		G_GameLogicPtr.SendServerVersion(a)
	case "ReqRegisterAccount":
		detail := &msg.ReqRegisterAccount{}
		log.Debug("player %s start login", detail.UserName)
		proto.Unmarshal(buf, detail)
		gl := G_getGameLogic()
		defer func() {
			if r := recover(); r != nil {
				GoUtil.SendFeishuFatal(0, m.GetFunc(), fmt.Sprintf("username: %s, fatal: %s", detail.UserName, r))
				log.Error("uid : %d, func : %s, fatal : %s", 0, m.GetFunc(), r)
			}
		}()
		if gl.IsExsitAccount(detail.UserName) {
			ResRegisterAccount := &msg.ResRegisterAccount{}
			ResRegisterAccount.ResultCode = MergeConst.Protocol_Error_Account_Exist
			data, _ := proto.Marshal(ResRegisterAccount)
			gl.PackResInfo(a, "ResRegisterAccount", data)
			break
		}
		gl.Db_AccountInfo.UserName = detail.UserName
		gl.Db_AccountInfo.UserPassword = detail.UserPwd
		if !gl.NewAccountInsertDataToDB() {
			ResRegisterAccount := &msg.ResRegisterAccount{}
			ResRegisterAccount.ResultCode = MergeConst.Protocol_Error_Account_Fail
			data, _ := proto.Marshal(ResRegisterAccount)
			gl.PackResInfo(a, "ResRegisterAccount", data)
			break
		}
		ResRegisterAccount := &msg.ResRegisterAccount{}
		ResRegisterAccount.ResultCode = 0
		data, _ := proto.Marshal(ResRegisterAccount)
		gl.PackResInfo(a, "ResRegisterAccount", data)
	case "ReqLogin": // 登录请求
		detail := &msg.ReqLogin{}
		proto.Unmarshal(buf, detail)
		accountInfo := db.GetAccountInfoFromDb(detail.UserName)
		log.Debug("player %s start login", detail.UserName)
		defer func() {
			if r := recover(); r != nil {
				GoUtil.SendFeishuFatal(0, m.GetFunc(), fmt.Sprintf("username: %s, fatal: %s", detail.UserName, r))
				log.Error("uid : %d, func : %s, fatal : %s", 0, m.GetFunc(), r)
			}
		}()
		ResLogin, accountInfo := VerifyUser(accountInfo, detail)
		if ResLogin.ResultCode != 0 {
			G_GameLogicPtr.PackLoginResInfo(a, ResLogin)
			return
		}
		newPlayer := false
		if ResLogin.DwUin > 0 {
			PlayerInfo := G_GameLogicPtr.GetPlayer(ResLogin.DwUin)
			err := G_GameLogicPtr.ReplaceExistPlayerAndAgent(a, PlayerInfo)
			if err != nil {
				PlayerInfo = G_GameLogicPtr.CreateNewPlayer(a, detail.UserName)
				newPlayer = true
			}
			if PlayerInfo.PlayMod.getBaseMod().IdCardName == "" && conf.Server.IdVerify {
				ResLogin.ResultCode = MergeConst.Protocol_Error_Id_Not_Verify
				G_GameLogicPtr.PackLoginResInfo(a, ResLogin)
				return
			}
		}
		accountInfo.DeviceId = detail.Device
		db.UpdateAccountInfoDeviceToDb(accountInfo)
		p, _ := internal.Agents.Load(a)
		if p != nil {
			p.(*Player).lock.Lock()         //加锁
			defer p.(*Player).lock.Unlock() //解锁
			log.Debug("player %s login success", detail.UserName)
			p.(*Player).PlayMod.getBaseMod().DiviceId = detail.Device //加锁
			p.(*Player).PushClientRes(ResLogin)
			p.(*Player).LoginBackData()
			p.(*Player).TeLog("Login_log", nil)
			p.(*Player).ProcessTrigger()
		}
		log.Debug("uid : %d, init user process : %s, execTime : %v , isNew: %v", p.(*Player).M_DwUin, m.GetFunc(), time.Since(start), newPlayer)
	case "ReqServerTime": // 获取服务器时间
		detail := &msg.ReqServerTime{}
		proto.Unmarshal(buf, detail)
		res := &msg.ResServerTime{}
		res.ServerTime = (int32)(time.Now().Unix())
		data, _ := proto.Marshal(res)
		G_getGameLogic().PackResInfo(a, "ResServerTime", data)
	case "ReqOfflineReconnect": // 断线重连
		ReqOfflineReconnectFunc(a, buf)
	default:
		p, ok := internal.Agents.Load(a)
		if ok && p != G_GameLogicPtr.NotInitPlayer {
			p.(*Player).lock.Lock()        //加锁
			backup := p.(*Player).BackUp() // 备份当前的 Player 值
			defer func() {
				if r := recover(); r != nil {
					if m.GetFunc() != "ReqGmCommand" { // 如果不是 GM 命令，才记录日志和发送飞书告警
						log.Error("uid : %d, func : %s, fatal : %s", p.(*Player).M_DwUin, m.GetFunc(), r)
						GoUtil.SendFeishuFatal(int(p.(*Player).M_DwUin), m.GetFunc(), fmt.Sprintf("fatal : %s", r))
					}
					p.(*Player).Recover(backup) //还原Player的数据
				}
				p.(*Player).lock.Unlock() //解锁
			}()
			p.(*Player).args = make(map[string]interface{})
			p.(*Player).args["func"] = m
			p.(*Player).args["agent"] = a
			err := RunNetProcessByKey(m.GetFunc(), []interface{}{a, buf})
			if err != nil {
				log.Error("uid : %d, func : %s, err : %s", p.(*Player).M_DwUin, m.GetFunc(), err)
				p.(*Player).TeLog("func_exec_error", map[string]interface{}{
					"method_name": m.GetFunc(),
					"error_info":  err.Error(),
				})
				p.(*Player).Recover(backup) //还原Player的数据
				return
			}
			p.(*Player).ProcessTrigger()
			p.(*Player).TeLog("func_exec_time", map[string]interface{}{
				"method_name": m.GetFunc(),
				"exec_time":   fmt.Sprintf("%v", time.Since(start)),
			})
		}
	}
	p, b := internal.Agents.Load(a)
	if b {
		p.(*Player).SendClientRes()
	}
	//log.Debug("uid : %d, func : %s, execTime : %s ", p.(*Player).M_DwUin, m.GetFunc(), time.Since(start))
}
