package api

import (
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"sync"
	"sync/atomic"
	"time"

	"mongo.games.com/goserver/core"
	"mongo.games.com/goserver/core/admin"
	"mongo.games.com/goserver/core/logger"
	"mongo.games.com/goserver/core/netlib"
	"mongo.games.com/goserver/core/transact"
	"mongo.games.com/goserver/core/utils"
	"mongo.games.com/goserver/srvlib"

	"mongo.games.com/game/common"
	"mongo.games.com/game/model"
)

// API
// http://127.0.0.1:9595/api/Report/QueryOnlineReportList?ts=20141024000000&sign=41cc8cee8dd93f7dc70b6426cfd1029d
const (
	WEBAPI_TRANSACTE_EVENT int = iota
	WEBAPI_TRANSACTE_RESPONSE
)

var WebApiStats = new(sync.Map)

type ApiStats struct {
	RunTimes        int64 //执行次数
	TotalRuningTime int64 //总执行时间
	MaxRuningTime   int64 //最长执行时间
	TimeoutTimes    int64 //执行超时次数
	UnreachTimes    int64 //不可达次数
}

func WorldSrvApi(rw http.ResponseWriter, req *http.Request) {
	defer utils.DumpStackIfPanic("api.WorldSrvApi")
	logger.Logger.Info("WorldSrvApi 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 {
		logger.Logger.Info("Body err.", err)
		webApiResponse(rw, nil /*map[string]interface{}{webapi.RESPONSE_STATE: webapi.STATE_ERR, webapi.RESPONSE_ERRMSG: "Post data is null!"}*/)
		return
	}
	m := req.URL.Query()
	timestamp := m.Get("nano")
	if timestamp == "" {
		logger.Logger.Info(req.RemoteAddr, " WorldSrvApi param error: nano not allow null")
		return
	}
	sign := m.Get("sign")
	if sign == "" {
		logger.Logger.Info(req.RemoteAddr, " WorldSrvApi param error: sign not allow null")
		return
	}
	startTime := time.Now().UnixNano()
	args := fmt.Sprintf("%v;%v;%v;%v", common.Config.AppId, req.URL.Path, string(data), timestamp)
	h := md5.New()
	io.WriteString(h, args)
	realSign := hex.EncodeToString(h.Sum(nil))
	if realSign != sign && !common.Config.IsDevMode {
		logger.Logger.Info(req.RemoteAddr, " srvCtrlMain sign error: expect ", realSign, " ; but get ", sign, " raw=", args)
		webApiResponse(rw, nil /*map[string]interface{}{webapi.RESPONSE_STATE: webapi.STATE_ERR, webapi.RESPONSE_ERRMSG: "Sign error!"}*/)
		return
	}

	var stats *ApiStats
	if v, exist := WebApiStats.Load(req.URL.Path); exist {
		stats = v.(*ApiStats)
	} else {
		stats = &ApiStats{}
		WebApiStats.Store(req.URL.Path, stats)
	}
	var rep []byte
	start := time.Now()
	res := make(chan []byte, 1)
	suc := core.CoreObject().SendCommand(&WebApiEvent{req: req, path: req.URL.Path, h: HandlerWrapper(func(event *WebApiEvent, data []byte) bool {
		logger.Logger.Trace("WorldSrvApi start transcate")
		tnp := &transact.TransNodeParam{
			Tt:     common.TransType_WebApi,
			Ot:     transact.TransOwnerType(common.GetSelfSrvType()),
			Oid:    common.GetSelfSrvId(),
			AreaID: common.GetSelfAreaId(),
		}
		tNode := transact.DTCModule.StartTrans(tnp, event, transact.DefaultTransactTimeout) //超时时间30秒
		if tNode != nil {
			tNode.TransEnv.SetField(WEBAPI_TRANSACTE_EVENT, event)
			tNode.Go(core.CoreObject())
		}
		return true
	}), body: data, rawQuery: req.URL.RawQuery, res: res}, false)
	if suc {
		select {
		case rep = <-res:
			if rep != nil {
				webApiResponse(rw, rep)
			}
		case <-time.After(ApiDefaultTimeout):
			//rep = make(map[string]interface{})
			//rep[webapi.RESPONSE_STATE] = webapi.STATE_ERR
			//rep[webapi.RESPONSE_ERRMSG] = "proccess timeout!"
			webApiResponse(rw, rep)
			if stats != nil {
				atomic.AddInt64(&stats.TimeoutTimes, 1)
			}
		}
	} else {
		webApiResponse(rw, nil)
		if stats != nil {
			atomic.AddInt64(&stats.UnreachTimes, 1)
		}
	}
	ps := int64(time.Now().Sub(start) / time.Millisecond)
	if stats != nil {
		atomic.AddInt64(&stats.RunTimes, 1)
		atomic.AddInt64(&stats.TotalRuningTime, ps)
		if atomic.LoadInt64(&stats.MaxRuningTime) < ps {
			atomic.StoreInt64(&stats.MaxRuningTime, ps)
		}
	}
	result, err := json.Marshal(rep)
	if err == nil {
		log := model.NewAPILog(req.URL.Path, req.URL.RawQuery, string(data[:]), req.RemoteAddr, string(result[:]), startTime, ps)
		LogChannelSington.WriteLog(log)
	}
	return
}

// --------------------------------------------------------------------------------------
func init() {
	transact.RegisteHandler(common.TransType_WebApi, &transact.TransHanderWrapper{
		OnExecuteWrapper: transact.OnExecuteWrapper(func(tNode *transact.TransNode, ud interface{}) transact.TransExeResult {
			logger.Logger.Trace("WorldSrvApi start TransType_WebApi OnExecuteWrapper ")
			tnp := &transact.TransNodeParam{
				Tt:     common.TransType_WebApi,
				Ot:     transact.TransOwnerType(srvlib.WorldServerType),
				Oid:    common.GetWorldSrvId(),
				AreaID: common.GetSelfAreaId(),
				Tct:    transact.TransactCommitPolicy_TwoPhase,
			}
			if event, ok := ud.(*WebApiEvent); ok {
				userData := &common.M2GWebApiRequest{Path: event.path, RawQuery: event.rawQuery, Body: event.body, ReqIp: event.req.RemoteAddr}
				tNode.StartChildTrans(tnp, userData, transact.DefaultTransactTimeout)

				pid := tNode.MyTnp.TId
				cid := tnp.TId
				logger.Logger.Tracef("WorldSrvApi start TransType_WebApi OnExecuteWrapper tid:%x childid:%x", pid, cid)
				return transact.TransExeResult_Success
			}
			return transact.TransExeResult_Failed
		}),
		OnCommitWrapper: transact.OnCommitWrapper(func(tNode *transact.TransNode) transact.TransExeResult {
			logger.Logger.Trace("WorldSrvApi start TransType_WebApi OnCommitWrapper")
			event := tNode.TransEnv.GetField(WEBAPI_TRANSACTE_EVENT).(*WebApiEvent)
			resp := tNode.TransEnv.GetField(WEBAPI_TRANSACTE_RESPONSE)
			if ud, ok := resp.([]byte); ok {
				event.Response(netlib.SkipHeaderGetRaw(ud))
				return transact.TransExeResult_Success
			}
			event.Response(nil /*map[string]interface{}{webapi.RESPONSE_STATE: webapi.STATE_ERR, webapi.RESPONSE_ERRMSG: "execute failed!"}*/)
			return transact.TransExeResult_Success
		}),
		OnRollBackWrapper: transact.OnRollBackWrapper(func(tNode *transact.TransNode) transact.TransExeResult {
			logger.Logger.Trace("WorldSrvApi start TransType_WebApi OnRollBackWrapper")
			event := tNode.TransEnv.GetField(WEBAPI_TRANSACTE_EVENT).(*WebApiEvent)
			resp := tNode.TransEnv.GetField(WEBAPI_TRANSACTE_RESPONSE)
			if ud, ok := resp.([]byte); ok {
				event.Response(netlib.SkipHeaderGetRaw(ud))
				return transact.TransExeResult_Success
			}
			event.Response(nil /*map[string]interface{}{webapi.RESPONSE_STATE: webapi.STATE_ERR, webapi.RESPONSE_ERRMSG: "execute failed!"}*/)
			return transact.TransExeResult_Success
		}),
		OnChildRespWrapper: transact.OnChildRespWrapper(func(tNode *transact.TransNode, hChild transact.TransNodeID, retCode int, ud interface{}) transact.TransExeResult {
			logger.Logger.Tracef("WorldSrvApi start TransType_WebApi OnChildRespWrapper ret:%v childid:%x", retCode, hChild)
			tNode.TransEnv.SetField(WEBAPI_TRANSACTE_RESPONSE, ud)
			return transact.TransExeResult(retCode)
		}),
	})

	// 查询在线玩家列表，筛选，排序
	admin.MyAdminApp.Route("/api/Report/QueryOnlineReportList", WorldSrvApi)
	// 在线统计
	admin.MyAdminApp.Route("/api/Report/OnlineReportTotal", WorldSrvApi)
	// 加减金币钻石
	admin.MyAdminApp.Route("/api/mongo.games.com/game/AddCoinByIdAndPT", WorldSrvApi)
	// 查询玩家信息
	admin.MyAdminApp.Route("/api/Player/PlayerData", WorldSrvApi)
	// 查询多个玩家信息
	admin.MyAdminApp.Route("/api/Player/MorePlayerData", WorldSrvApi)
	// 踢下线
	admin.MyAdminApp.Route("/api/Player/KickPlayer", WorldSrvApi)
	// 黑白名单调控
	admin.MyAdminApp.Route("/api/Player/WhiteBlackControl", WorldSrvApi)
	// 设置黑名单
	admin.MyAdminApp.Route("/api/Player/BlackBySnId", WorldSrvApi)
	// 修改玩家内存属性
	admin.MyAdminApp.Route("/api/Cache/UpdatePlayerElement", WorldSrvApi)

	// 水池相关
	admin.MyAdminApp.Route("/api/mongo.games.com/game/QueryGamePoolByGameId", WorldSrvApi)
	admin.MyAdminApp.Route("/api/mongo.games.com/game/QueryAllGamePool", WorldSrvApi)
	admin.MyAdminApp.Route("/api/mongo.games.com/game/RefreshGamePool", WorldSrvApi)
	admin.MyAdminApp.Route("/api/mongo.games.com/game/UpdateGamePool", WorldSrvApi)
	admin.MyAdminApp.Route("/api/mongo.games.com/game/ResetGamePool", WorldSrvApi)

	// 邮箱相关
	admin.MyAdminApp.Route("/api/mongo.games.com/game/CreateShortMessage", WorldSrvApi)
	admin.MyAdminApp.Route("/api/mongo.games.com/game/QueryShortMessageList", WorldSrvApi)
	admin.MyAdminApp.Route("/api/mongo.games.com/game/DeleteShortMessage", WorldSrvApi)
	admin.MyAdminApp.Route("/api/mongo.games.com/game/DeleteAllShortMessage", WorldSrvApi)

	// 获取房间列表
	admin.MyAdminApp.Route("/api/Cache/ListRoom", WorldSrvApi)
	// 获取房间信息
	admin.MyAdminApp.Route("/api/Cache/GetRoom", WorldSrvApi)
	// 销毁房间
	admin.MyAdminApp.Route("/api/Cache/DestroyRoom", WorldSrvApi)
	// 兑换物品
	admin.MyAdminApp.Route("/api/Customer/UpExchangeStatus", WorldSrvApi)
	// 3方平台
	admin.MyAdminApp.Route("/api/thd/UpdatePlayerCoin", WorldSrvApi)
	// 兑换卷
	admin.MyAdminApp.Route("/api/mongo.games.com/game/CreateJYB", WorldSrvApi)
	admin.MyAdminApp.Route("/api/mongo.games.com/game/UpdateJYB", WorldSrvApi)
	// 支付回调
	admin.MyAdminApp.Route("/api/pay/CallbackPayment", WorldSrvApi)
	// 资源变更（给所有玩家发送消息）
	admin.MyAdminApp.Route("/api/mongo.games.com/game/resource", WorldSrvApi)
	// 修改手机号
	admin.MyAdminApp.Route("/api/player/update_tel", WorldSrvApi)
	// 删除账号
	admin.MyAdminApp.Route("/api/player/delete", WorldSrvApi)
}

func Stats() map[string]ApiStats {
	stats := make(map[string]ApiStats)
	WebApiStats.Range(func(k, v interface{}) bool {
		if s, ok := v.(*ApiStats); ok {
			ss := *s //计数可能不精准
			stats[k.(string)] = ss
		}
		return true
	})
	return stats
}
