package game

import (
	"encoding/json"
	"fmt"
	"runtime"
	"server/MergeConst"
	"server/conf"
	"server/db"
	Msg "server/game/mod/msg"
	GoUtil "server/game_util"
	"server/gamedata"
	"server/msg"
	"server/pkg/github.com/name5566/leaf/gate"
	"server/pkg/github.com/name5566/leaf/log"
	"time"

	"github.com/shirou/gopsutil/cpu"
	"github.com/shirou/gopsutil/mem"
	"google.golang.org/protobuf/proto"
)

var AdminFuncMap = map[string]func([]interface{}) error{
	"ReqAdminInfo":        AdminPlayerInfo,
	"ReqServerInfo":       ReqServerInfo,
	"ReqReloadServerMail": ReqReloadServerMail,
	"ReqReload":           ReqReload,
	"ReqAdminGm":          ReqAdminGm,
	"ReqAdminBan":         ReqAdminBan,
	"ReqAdminShipping":    ReqAdminShipping,
}

func AdminProcess(Func string, args []interface{}) {
	defer func() {
		if r := recover(); r != nil {
			GoUtil.SendFeishuFatal(0, Func, fmt.Sprintf("fatal : %s", r))
		}
	}()
	if f, ok := AdminFuncMap[Func]; ok {
		err := f(args)
		if err != nil {
			log.Debug("AdminProcess error: %v", err)
		}
		return
	}
	log.Debug("AdminProcess error: %v", "Func not found")
}

func VerifyUser(accountInfo *db.Db_Account, detail *msg.ReqLogin) (ResLogin *msg.ResLogin, accountInfoOut *db.Db_Account) {
	if detail.Type == msg.LOGIN_TYPE_CODE_LOGIN {
		err := VerifyCode(detail.UserName, detail.Code)
		if err != nil {
			ResLogin = &msg.ResLogin{
				ResultCode: MergeConst.Protocol_Error_Account_Code_Error,
				DwUin:      0,
			}
			return
		}
	}
	if detail.Type == msg.LOGIN_TYPE_SDK_LOGIN {
		if accountInfo == nil {
			accountInfo = db.GetAccountInfoFromDb(detail.Device)
		}
		if accountInfo != nil {
			db.UpdateAccountInfoName(accountInfo, detail.UserName)
			accountInfo.UserName = detail.UserName
			db.UpdatePlayerBaseInfoName(detail.Device, detail.UserName)
		}
	}
	if accountInfo == nil {
		ResLogin = &msg.ResLogin{
			ResultCode: MergeConst.Protocol_Error_Account_NoExsit,
			DwUin:      0,
		}
		return
	}
	if detail.Type == msg.LOGIN_TYPE_ACCOUNT_LOGIN && accountInfo.UserPassword != detail.UserPwd {
		ResLogin = &msg.ResLogin{
			ResultCode: MergeConst.Protocol_Error_Account_OR_PWD_ERROR,
			DwUin:      0,
		}
		return
	}
	if detail.Type == msg.LOGIN_TYPE_DEVICE_LOGIN {
		if detail.Device != accountInfo.DeviceId {
			ResLogin = &msg.ResLogin{
				ResultCode: MergeConst.Protocol_Error_Account_Device_Error,
				DwUin:      0,
			}
			return
		}
	}

	playerbaseinfo := db.GetPlayerBaseInfoFromDbByName(detail.UserName)
	if playerbaseinfo == nil {
		ResLogin = &msg.ResLogin{
			ResultCode: MergeConst.Protocol_Error_Account_NoExsit,
			DwUin:      0,
		}
		return
	}

	if playerbaseinfo.Ban > GoUtil.Now() || playerbaseinfo.Ban == -1 {
		ResLogin = &msg.ResLogin{
			ResultCode: MergeConst.Protocol_Error_Account_Ban,
			DwUin:      0,
		}
		return
	}
	accountInfoOut = accountInfo
	ResLogin = &msg.ResLogin{
		ResultCode: 0,
		DwUin:      playerbaseinfo.DwUin,
		FaceBookId: playerbaseinfo.FaceBookId,
		UserName:   playerbaseinfo.UserName,
	}
	return
}
func ReqAdminShipping(args []interface{}) error {
	a, buf := ParseAdminArgs(args)
	req := &msg.ReqAdminShipping{}
	proto.Unmarshal(buf, req)
	res := make(map[string]interface{})
	res["Code"] = 0
	OrderInfo, err := db.GetPlayerChargeData(req.OrderSn)
	if err != nil {
		res["Code"] = 1
		res["Msg"] = "order not found"
		AdminPlayerBack(a, res)
	}
	if OrderInfo.PayStatus == MergeConst.ORDER_STATUS_SHIP {
		res["Msg"] = "order already shipped"
		AdminPlayerBack(a, res)
	}
	Player := G_GameLogicPtr.GetPlayer(int64(OrderInfo.Uid))
	if Player != nil {
		go Player.TriggerShippingOrderOrigin(&msg.ReqShippingOrder{
			OrderSn: req.OrderSn,
		})
		res["Msg"] = "player online,triggered sync"
		AdminPlayerBack(a, res)
	} else {
		OrderInfo.PayStatus = MergeConst.ORDER_STATUS_PAY
		OrderInfo.PayChannelOrderId = req.ChannelOrderSn
		db.UpdatePlayerChargeData(OrderInfo)
	}
	return nil
}

func AdminPlayerInfo(args []interface{}) error {
	a, buf := ParseAdminArgs(args)
	req := &msg.ReqAdminInfo{}
	proto.Unmarshal(buf, req)
	player := G_GameLogicPtr.GetPlayer(req.Uid)
	online := true
	if player == nil {
		player = new(Player)
		player.M_DwUin = req.Uid
		player.InitPlayerOnly()
		player.ZeroUpdate(nil)
		online = false
	}
	res := make(map[string]interface{})
	res["Name"] = player.PlayMod.getBaseMod().NickName
	res["Uid"] = player.M_DwUin
	res["AreaId"] = player.PlayMod.getDecorateMod().GetAreaId()
	res["Face"] = player.PlayMod.getFaceMod().SetId
	res["Charge"] = player.PlayMod.getChargeMod().Charge
	res["MaxCharge"] = player.PlayMod.getChargeMod().MaxCharge
	res["Level"] = player.GetPlayerBaseMod().GetLevel()
	res["Diamond"] = player.GetPlayerBaseMod().GetDiamond()
	res["Star"] = player.GetPlayerBaseMod().GetStar()
	res["Energy"] = player.GetPlayerBaseMod().GetEnergy()
	res["Diamond"] = player.GetPlayerBaseMod().GetDiamond()
	res["Mac"] = player.GetPlayerBaseMod().GetName()
	res["Login"] = player.PlayMod.getBaseMod().LoginTime
	res["Cumulative"] = player.PlayMod.getBaseMod().Cumulative
	res["RegisterTime"] = player.GetPlayerBaseMod().GetRegisterTime()
	res["TodayCumulative"] = player.PlayMod.getBaseMod().TodayCumulative
	res["Ban"] = db.GetPlayerBan(player.PlayMod.getBaseMod().Account)
	if online {
		res["Cumulative"] = int64(player.PlayMod.getBaseMod().Cumulative) + GoUtil.Now() - int64(player.PlayMod.getBaseMod().LoginTime)
		res["TodayCumulative"] = int64(player.PlayMod.getBaseMod().TodayCumulative) + GoUtil.Now() - int64(player.PlayMod.getBaseMod().LoginTime)
	}
	res["Bonus"] = player.PlayMod.getLimitedTimeEventMod().Progress
	res["Code"] = player.PlayMod.getBaseMod().AddCode
	res["ChessMap"] = player.PlayMod.getChessMod().ChessMap
	res["ActLog"] = player.PlayMod.getFriendMod().ActivityLog
	friendList := player.PlayMod.getFriendMod().NewFriendList
	type friendInfo struct {
		Uid        int64
		NickName   string
		Avatar     int
		Level      int
		LogoutTime int64
		LoginTime  int64
	}
	resFriendList := make([]friendInfo, 0, len(friendList))
	for v := range friendList {
		ps := G_GameLogicPtr.GetSimplePlayerByUid(v)
		if ps == nil {
			continue
		}
		resFriendList = append(resFriendList, friendInfo{
			Uid:        int64(v),
			NickName:   ps.Name,
			Avatar:     ps.Face,
			Level:      ps.Level,
			LogoutTime: ps.Loginout,
			LoginTime:  ps.Login,
		})
	}
	res["FriendList"] = resFriendList
	OrderMap := make(map[int]interface{})
	Index := 0
	for k, v := range player.PlayMod.getOrderMod().OrderList {
		OrderMap[Index] = map[string]interface{}{
			"Id":      k,
			"Type":    v.Type,
			"Time":    v.Timestamp,
			"ChessId": GoUtil.IntSliceToString(v.MergeId),
			"Diff":    v.Diff,
		}
		Index++
	}
	res["Order"] = OrderMap
	AdminPlayerBack(a, res)
	return nil
}

func AdminPlayerBack(a gate.Agent, res map[string]interface{}) {
	JsonBuff, _ := json.Marshal(res)
	response := &msg.AdminRes{}
	response.Func = "admin"
	response.Info = string(JsonBuff)
	a.WriteMsg(response)
}

func ReqServerInfo(args []interface{}) error {
	a, _ := ParseAdminArgs(args)
	res := make(map[string]interface{})
	res["PlayerNum"] = G_GameLogicPtr.GetPlayerNum()
	res["StartTime"] = G_GameLogicPtr.GetStartTime()
	var m runtime.MemStats
	runtime.ReadMemStats(&m)
	res["TotalAlloc"] = fmt.Sprintf("%dM", m.TotalAlloc/(1024*1024))
	// 2. 获取系统内存使用信息
	vmStat, _ := mem.VirtualMemory()
	// 3. 获取 CPU 使用率（一秒内采样）
	cpuPercent, err := cpu.Percent(time.Second, false)
	if err == nil && len(cpuPercent) > 0 {
		res["CPU"] = cpuPercent[0]
	}
	res["Alloc"] = fmt.Sprintf("%dM", m.Alloc/(1024*1024))
	res["Sys"] = m.Sys / (1024 * 1024)
	res["NumGC"] = m.NumGC
	res["NumGoroutine"] = runtime.NumGoroutine()
	if vmStat != nil {
		res["FreeMem"] = vmStat.Available / (1024 * 1024)
		res["UsageMem"] = vmStat.Used / (1024 * 1024)
	} else {
		res["UsageMem"] = 0
		res["FreeMem"] = 0
	}
	res["Version"] = conf.Server.Version
	AdminPlayerBack(a, res)
	return nil
}

func ReqReloadServerMail(args []interface{}) error {
	G_GameLogicPtr.MailMgrCall(&Msg.Msg{Type: Msg.HANDLE_TYPE_MAIL_RELOAD})
	G_GameLogicPtr.NotifyAll(&Msg.Msg{Type: Msg.HANDLE_TYPE_MAIL})
	log.Debug("ReqReloadServerMail success")
	return nil
}

func ReqReload(args []interface{}) error {
	a, _ := ParseAdminArgs(args)
	gamedata.Reload()
	res := make(map[string]interface{})
	res["Code"] = 0
	AdminPlayerBack(a, res)
	return nil
}

func ReqAdminGm(args []interface{}) error {
	a, buf := ParseAdminArgs(args)
	req := &msg.ReqAdminGm{}
	proto.Unmarshal(buf, req)
	res := make(map[string]interface{})
	res["Code"] = 0
	res["Msg"] = "ok"
	player := G_GameLogicPtr.GetPlayer(req.Uid)
	if player == nil {
		res["Code"] = 1
		res["Msg"] = "player not found"
		AdminPlayerBack(a, res)
		return nil
	}
	player.lock.Lock()
	defer player.lock.Unlock()
	err := ReqGmCommand_(player, req.Command)
	if err != nil {
		res["Code"] = 1
		res["Msg"] = err.Error()
		AdminPlayerBack(a, res)
		return err
	}
	AdminPlayerBack(a, res)
	return nil
}

func ReqAdminBan(args []interface{}) error {
	a, buf := ParseAdminArgs(args)
	req := &msg.ReqAdminBan{}
	proto.Unmarshal(buf, req)
	res := make(map[string]interface{})
	res["Code"] = 0
	res["Msg"] = "ok"
	db.UpdatePlayerBan(req.Uid, int64(req.Time))
	AdminPlayerBack(a, res)
	return nil
}
