package api

import (
	"io"
	"mongo.games.com/game/common"
	"mongo.games.com/game/model"
	"mongo.games.com/game/proto"
	msg_proto "mongo.games.com/game/protocol/message"
	"mongo.games.com/game/protocol/server"
	"mongo.games.com/game/protocol/webapi"
	"mongo.games.com/goserver/core/admin"
	"mongo.games.com/goserver/core/logger"
	"mongo.games.com/goserver/core/timer"
	"mongo.games.com/goserver/core/utils"
	"mongo.games.com/goserver/srvlib"
	"mongo.games.com/goserver/srvlib/protocol"
	srvlibprotocol "mongo.games.com/goserver/srvlib/protocol"
	"net/http"
	"os/exec"
	"strconv"
	"time"
)

//-------------------------------切换状态-------------------------------------------------------

func ServerStateSwitch(rw http.ResponseWriter, data []byte) {
	pack := &webapi.SAServerStateSwitch{}
	var gs webapi.ASServerStateSwitch
	err := proto.Unmarshal(data, &gs)
	if err != nil {
		pack.Tag = webapi.TagCode_FAILED
		pack.Msg = "proto.Unmarshal is err:" + err.Error()
		by, _ := proto.Marshal(pack)
		webApiResponse(rw, by)
		return
	}

	if gs.SrvId == 0 || gs.SrvType == 0 {
		pack.Tag = webapi.TagCode_FAILED
		pack.Msg = "SrvCtrlStateSwitch failed SrvId or SrvType is zero."
		by, _ := proto.Marshal(pack)
		webApiResponse(rw, by)
		return
	}
	s := srvlib.ServerSessionMgrSington.GetSession(common.GetSelfAreaId(), int(gs.SrvType), int(gs.SrvId))
	logger.Logger.Trace("srvCtrl_StateSwitch enter")
	if s != nil {
		ctrlPacket := &server.ServerCtrl{
			CtrlCode: proto.Int32(common.SrvCtrlStateSwitchCode),
		}
		proto.SetDefaults(ctrlPacket)
		s.Send(int(server.SSPacketID_PACKET_MS_SRVCTRL), ctrlPacket, true)
		pack.Tag = webapi.TagCode_SUCCESS
		pack.Msg = "Server is changed."
		rep, _ := proto.Marshal(pack)
		webApiResponse(rw, rep)
		return
	}
	pack.Tag = webapi.TagCode_FAILED
	pack.Msg = "[stateswitch] no find " + strconv.Itoa(int(gs.SrvId)) + "-" + strconv.Itoa(int(gs.SrvType)) + " server"
	rep, _ := proto.Marshal(pack)
	webApiResponse(rw, rep)
}
func SrvCtrlClose(rw http.ResponseWriter, data []byte) {
	logger.Logger.Trace("srvCtrl_Close enter")
	msg := &webapi.ASSrvCtrlClose{}
	pack := &webapi.SASrvCtrlClose{
		Tag: webapi.TagCode_SUCCESS,
	}
	err := proto.Unmarshal(data, msg)
	if err != nil {
		pack.Msg = "Unmarshal CtrlClose" + err.Error()
		rep, _ := proto.Marshal(pack)
		webApiResponse(rw, rep)
		return
	}
	ctrlPacket := &server.ServerCtrl{
		CtrlCode: proto.Int32(common.SrvCtrlCloseCode),
	}
	proto.SetDefaults(ctrlPacket)
	var closeType []int
	srvType := int(msg.SrvType)
	if msg.SrvType == 0 {
		closeType = append(closeType, srvlib.GameServerType, srvlib.GateServerType, srvlib.WorldServerType)
	} else if srvType == srvlib.GameServerType || srvType == srvlib.GateServerType || srvType == srvlib.WorldServerType {
		closeType = append(closeType, int(msg.SrvType))
	}
	for _, serverType := range closeType {
		srvlib.ServerSessionMgrSington.Broadcast(int(server.SSPacketID_PACKET_MS_SRVCTRL), ctrlPacket, common.GetSelfAreaId(), serverType)
	}
	pack.Msg = "success"
	rep, _ := proto.Marshal(pack)
	webApiResponse(rw, rep)
}
func srvCtrlExecShell(shell string) bool {
	cmd := exec.Command(shell)
	if cmd == nil {
		logger.Logger.Info("srvCtrlExecShell exec.Command(", shell, ") failed")
		return false
	}
	logger.Logger.Info("srvCtrlExecShell exec.Command(", shell, ")")

	stdout, err := cmd.StdoutPipe()
	if err != nil {
		logger.Logger.Info("srvCtrlExecShell.cmd.StdoutPipe() error:", err)
		return false
	}

	stderr, err := cmd.StderrPipe()
	if err != nil {
		logger.Logger.Info("srvCtrlExecShell.cmd.StderrPipe() error:", err)
		return false
	}

	err = cmd.Start()
	if err != nil {
		logger.Logger.Info("srvCtrlExecShell.cmd.Start()", err)
		return false
	}

	ber, err := io.ReadAll(stderr)
	if err != nil {
		logger.Logger.Info("srvCtrlExecShell.io.ReadAll(stderr)", err)
		return false
	}
	logger.Logger.Info("stderr=", string(ber[:]))

	bsr, err := io.ReadAll(stdout)
	if err != nil {
		logger.Logger.Info("srvCtrlExecShell.io.ReadAll(stdout)", err)
		return false
	}
	logger.Logger.Info("stdout=", string(bsr[:]))

	err = cmd.Wait()
	if err != nil {
		logger.Logger.Info("srvCtrlExecShell.cmd.Wait()", err)
		return false
	}
	return true
}

func SrvCtrlStartScript(rw http.ResponseWriter, data []byte) {
	logger.Logger.Trace("srvCtrl_Start enter")
	msg := &webapi.ASSrvCtrlStartScript{}
	pack := &webapi.SASrvCtrlStartScript{
		Tag: webapi.TagCode_SUCCESS,
	}
	err := proto.Unmarshal(data, msg)
	if err != nil {
		pack.Tag = webapi.TagCode_FAILED
		pack.Msg = "Unmarshal ASSrvCtrlStartScript is err:" + err.Error()
		r, _ := proto.Marshal(pack)
		webApiResponse(rw, r)
		return
	}
	if srvCtrlExecShell(Config.StartScript) {
		pack.Msg = "Start success"
	} else {
		pack.Tag = webapi.TagCode_FAILED
		pack.Msg = "Start failed"
	}
	rep, _ := proto.Marshal(pack)
	webApiResponse(rw, rep)
}
func SrvApi(rw http.ResponseWriter, req *http.Request) {
	defer utils.DumpStackIfPanic("api.SrvCtrlApi")
	logger.Logger.Info("srvCtrl_StateSwitch receive:", req.URL.Path, req.URL.RawQuery)

	if common.RequestCheck(req, model.GameParamData.WhiteHttpAddr) == false {
		logger.Logger.Info("RemoteAddr [%v] require api.", req.RemoteAddr)
		return
	}
	data, err := io.ReadAll(req.Body)
	if err != nil {
		webApiResponse(rw, nil)
		return
	}
	switch req.URL.Path {
	case "/api/Ctrl/ServerStateSwitch":
		ServerStateSwitch(rw, data)
	case "/api/Ctrl/SrvCtrlClose":
		SrvCtrlClose(rw, data)
	case "/api/Ctrl/SrvCtrlStartScript":
		SrvCtrlStartScript(rw, data)
	case "/api/Ctrl/SrvCtrlNotice":
		SrvCtrlNotice(rw, data)
	}
}

var noticeMap = make(map[timer.TimerHandle]struct{})

func SrvCtrlNotice(rw http.ResponseWriter, data []byte) {
	logger.Logger.Trace("SrvCtrlNotice")
	pack := &webapi.SASrvCtrlNotice{}
	msg := &webapi.ASSrvCtrlNotice{}
	err := proto.Unmarshal(data, msg)
	if err != nil {
		pack.Tag = webapi.TagCode_FAILED
		pack.Msg = "Unmarshal ASSrvCtrlNotice is err:" + err.Error()
		r, _ := proto.Marshal(pack)
		webApiResponse(rw, r)
		return
	}
	if msg.OpNotice == 1 {
		logger.Logger.Trace("srvCtrl_StopNotice enter")
		for h, _ := range noticeMap {
			timer.StopTimer(h)
		}
		noticeMap = make(map[timer.TimerHandle]struct{})
		pack.Tag = webapi.TagCode_SUCCESS
		pack.Msg = "stop all notice success"
		r, _ := proto.Marshal(pack)
		webApiResponse(rw, r)
		return
	}
	logger.Logger.Trace("srvCtrl_Notice enter")
	if msg.Notice == "" {
		pack.Tag = webapi.TagCode_FAILED
		pack.Msg = "Notice is nil"
		r, _ := proto.Marshal(pack)
		webApiResponse(rw, r)
		return
	}
	noticePacket := &server.ServerNotice{
		Text: proto.String(msg.GetNotice()),
	}
	proto.SetDefaults(noticePacket)
	sc := &protocol.BCSessionUnion{
		Bccs: &protocol.BCClientSession{},
	}
	broadcast, err := BroadcastMaker.CreateBroadcastPacket(sc, int(msg_proto.MSGPacketID_PACKET_SC_NOTICE), noticePacket)
	if err != nil || broadcast == nil {
		pack.Tag = webapi.TagCode_FAILED
		pack.Msg = "send notice failed(inner error)"
		r, _ := proto.Marshal(pack)
		webApiResponse(rw, r)
		return
	}
	funcNotice := func() {
		srvlib.ServerSessionMgrSington.Broadcast(int(srvlibprotocol.SrvlibPacketID_PACKET_SS_BROADCAST), broadcast, common.GetSelfAreaId(), srvlib.GateServerType)
	}
	funcNotice()
	h, b := timer.StartTimer(timer.TimerActionWrapper(func(h timer.TimerHandle, ud interface{}) bool {
		funcNotice()
		return true
	}), nil, time.Second*time.Duration(msg.GetInterval()), int(msg.GetTimes()))
	if b {
		noticeMap[h] = struct{}{}
	}
	pack.Tag = webapi.TagCode_SUCCESS
	pack.Msg = "send notice success"
	r, _ := proto.Marshal(pack)
	webApiResponse(rw, r)
}

// --------------------------------------------------------------------------------------
func init() {
	//切换状态
	admin.MyAdminApp.Route("/api/Ctrl/ServerStateSwitch", SrvApi)
	//获取服务器列表
	admin.MyAdminApp.Route("/api/Ctrl/ListServerStates", WorldSrvApi)
	//关服
	admin.MyAdminApp.Route("/api/Ctrl/SrvCtrlClose", SrvApi)
	//执行脚本
	admin.MyAdminApp.Route("/api/Ctrl/SrvCtrlStartScript", SrvApi)
	//发送 停止 公告
	admin.MyAdminApp.Route("/api/Ctrl/SrvCtrlNotice", SrvApi)
	//重置Etcd
	admin.MyAdminApp.Route("/api/Ctrl/ResetEtcdData", WorldSrvApi)
}
