package main

import (
	"encoding/json"
	"net/http"
	"strconv"
	"time"

	"../log"
	"../mod"
	"../nt"
	pb "../protos"
	"../rpc"
	"../utils"
	"github.com/coheneye/go-simplejson"
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
)

func do_delete_system_notice(c *gin.Context, args *rpc.RpcArgs, body string) {
	p := simplejson.NewFrom(args.Params)
	id := p.Get("id").MustInt64()
	typ := p.Get("type").MustInt64()
	ec := pb.ErrCode_OK

	defer func() {
		c.JSON(http.StatusOK, gin.H{
			"cmd": args.Cmd,
			"result": gin.H{
				"ec": ec,
			},
		})
	}()

	var err error = nil
	if typ == 1 {
		err = mod.RemoveMarquee(id)
	} else if typ == 2 {
		err = mod.RemoveNotices(id)
	} else {
		ec = pb.ErrCode_BAD_REQUEST
		return
	}

	if nil != err {
		ec = pb.ErrCode_UNKNOWN
		return
	}
}

func do_recharge(c *gin.Context, args *rpc.RpcArgs, body string) {
	p := simplejson.NewFrom(args.Params)
	guid := p.Get("guid").MustUint64()
	money := p.Get("money").MustInt64()
	id := p.Get("id").MustInt64()
	ec := pb.ErrCode_OK

	defer func() {
		c.JSON(http.StatusOK, gin.H{
			"cmd": args.Cmd,
			"result": gin.H{
				"ec": ec,
			},
		})
	}()

	if money < 0 {
		log.Warn("recharge negative money", zap.Int64("delta", money), zap.Uint64("guid", guid),
			zap.Int64("orderId", id))
		ec = pb.ErrCode_BAD_REQUEST
		return
	}
	var newVal int64 = 0
	addr, err := rpc.GetUserAgentRpcUrl(guid)
	if nil != err { // offline
		newVal, err = mod.AddUserBalance(guid, money)
		if nil != err {
			log.Error("AddUserBalance", zap.Int64("delta", money), zap.Uint64("guid", guid),
				zap.Int64("orderId", id))
			ec = pb.ErrCode_UNKNOWN
			return
		} else {
			// 成功
			Ev.OnUserRecharge(args.Cmd, guid, money, id, newVal)
			log.Info("do_recharge", zap.Int64("delta", money), zap.Uint64("guid", guid),
				zap.Int64("orderId", id), zap.Int64("newVal", newVal))
		}
	} else { // online
		var res rpc.RpcResult
		err = rpc.RpcRequest(addr, args, &res)
		if nil != err {
			log.Error("RpcRequest", zap.Int64("delta", money), zap.Uint64("guid", guid),
				zap.Int64("orderId", id))
			ec = pb.ErrCode_TARGET_NOT_FOUND
		}
		obj, ok := res.Result.(map[string]interface{})
		if !ok {
			log.Error("interface", zap.Int64("delta", money), zap.Uint64("guid", guid),
				zap.Int64("orderId", id))
		}
		ec = pb.ErrCode(utils.If_to_int32(obj["ec"], 0))
	}
}

func do_withdraw(c *gin.Context, args *rpc.RpcArgs, body string) {
	p := simplejson.NewFrom(args.Params)
	guid := p.Get("guid").MustUint64()
	money := p.Get("money").MustInt64()
	ec := pb.ErrCode_OK

	defer func() {
		c.JSON(http.StatusOK, gin.H{
			"cmd": args.Cmd,
			"result": gin.H{
				"ec": ec,
			},
		})
	}()

	if money < 0 {
		log.Warn("withdraw negative money", zap.Int64("delta", money), zap.Uint64("guid", guid))
		ec = pb.ErrCode_BAD_REQUEST
		return
	}
	sbalance, err := mod.GetUserAttr(guid, "balance")
	if err != nil {
		log.Error("GetUserAttr", zap.Int64("delta", money), zap.Uint64("guid", guid))
		ec = pb.ErrCode_UNKNOWN
		return
	}
	balance, err := strconv.ParseInt(sbalance, 10, 64)
	if nil != err {
		log.Error("ParseInt", zap.Int64("delta", money), zap.Uint64("guid", guid))
		ec = pb.ErrCode_UNKNOWN
		return
	}
	if balance < money {
		ec = pb.ErrCode_BALANCE_INSUFFICIENT
		return
	}

	var newVal int64 = 0
	addr, err := rpc.GetUserAgentRpcUrl(guid)
	if nil != err { // offline
		newVal, err = mod.AddUserBalance(guid, -money)
		if nil != err {
			log.Error("AddUserBalance", zap.Int64("delta", -money), zap.Uint64("guid", guid))
			ec = pb.ErrCode_UNKNOWN
			return
		} else {
			log.Info("do_withdraw", zap.Int64("delta", money), zap.Uint64("guid", guid),
				zap.Int64("newVal", newVal))
			// 成功
			Ev.OnUserWithdraw(args.Cmd, guid, money, newVal)
		}
	} else { // online
		var res rpc.RpcResult
		err = rpc.RpcRequest(addr, args, &res)
		if nil != err {
			log.Error("RpcRequest", zap.Int64("delta", money), zap.Uint64("guid", guid))
			ec = pb.ErrCode_TARGET_NOT_FOUND
		}
		val, ok := res.Result.(map[string]interface{})
		if ok {
			ec = pb.ErrCode(utils.If_to_int32(val["ec"], 0))
		} else {
			log.Error("interface", zap.Uint64("guid", guid), zap.Int64("delta", money))
		}
	}
}

// 踢人
func do_kickoff(c *gin.Context, args *rpc.RpcArgs, body string) {
	p := simplejson.NewFrom(args.Params)
	pguids := p.Get("guids")
	ec := pb.ErrCode_PENDING
	var err_guids []uint64

	defer func() {
		c.JSON(http.StatusOK, gin.H{
			"cmd": args.Cmd,
			"result": gin.H{
				"ec":    ec,
				"guids": err_guids,
			},
		})
	}()

	sguids, err := pguids.Array()
	if nil != err {
		log.Error("invaild guids", zap.Error(err))
		ec = pb.ErrCode_BAD_REQUEST
		return
	}

	var guids []uint64
	parts := make(map[string][]uint64) // 服务器地址与所在服务器用户
	for _, guid := range sguids {
		nguid := utils.If_to_uint64(guid, 0)
		guids = append(guids, nguid) // 对错都记录
		if !nt.DynConf.ValidGuid(nguid) {
			log.Warn("invalid guid", zap.String("cmd", args.Cmd), zap.Uint64("guid", nguid))
			// not return error here
			err_guids = append(err_guids, nguid) // guid 非法

			continue
		}
		//
		addr, err := rpc.GetUserAgentRpcUrl(nguid)
		if err != nil {
			err_guids = append(err_guids, nguid) // 不在线
		} else {
			parts[addr] = append(parts[addr], nguid)
		}
	} // for

	for addr, guidarr := range parts {
		params := &rpc.RpcArgs{
			Cmd: args.Cmd,
			Params: utils.H{
				"guids": guidarr,
			},
		}
		var res rpc.RpcResult
		err = rpc.RpcRequest(addr, params, &res)
		if nil != err {
			log.Error("RpcRequest", zap.Error(err))
			ec = pb.ErrCode_TARGET_NOT_FOUND
		}
	}

	Ev.OnMgrKickOff(guids...)
}

// 获取各个服务器状态
func do_server_info(c *gin.Context, args *rpc.RpcArgs, body string) {
	ec := pb.ErrCode_OK

	var err error
	var res rpc.RpcResult
	list := make(map[uint64]map[string]int64)
	for _, conf := range nt.DynConf.Lobbies {
		addr := "http://" + conf.RpcAddr + "/rpc"

		err = rpc.RpcRequest(addr, args, &res)
		if nil != err {
			log.Error("RpcRequest", zap.String("rpc_addr", addr), zap.String("cmd", args.Cmd),
				zap.Error(err), zap.Uint64("id", conf.Id))
			continue
		}
		//TODO: 汇集结果
		res, ok := res.Result.(map[string]interface{})
		if !ok {
			log.Error("Interface", zap.String("rpc_addr", addr), zap.String("cmd", args.Cmd),
				zap.Error(err), zap.Uint64("id", conf.Id))
			continue
		}
		item, ok := res["list"].(map[string]interface{})
		if !ok {
			log.Error("Interface-2", zap.String("rpc_addr", addr), zap.String("cmd", args.Cmd),
				zap.Error(err), zap.Uint64("id", conf.Id))
			continue
		}
		js := simplejson.NewFrom(item)
		list[conf.Id] = map[string]int64{
			"users":  js.Get("users").MustInt64(),
			"online": js.Get("online").MustInt64(),
			"uptime": js.Get("uptime").MustInt64(),
		}
	}

	c.JSON(http.StatusOK, gin.H{
		"cmd": args.Cmd,
		"result": gin.H{
			"ec":   ec,
			"list": list,
		},
	})
}

// 下线服务器
func do_server_offline(c *gin.Context, args *rpc.RpcArgs, body string) {
	p := simplejson.NewFrom(args.Params)
	id := p.Get("id").MustUint64()
	ec := pb.ErrCode_OK

	defer func() {
		c.JSON(http.StatusOK, gin.H{
			"cmd": args.Cmd,
			"result": gin.H{
				"ec": ec,
			},
		})
	}()

	addr, err := rpc.GetLobbyById(id)
	if err != nil {
		ec = pb.ErrCode_TARGET_NOT_FOUND
		log.Error("GetLobbyById", zap.String("cmd", args.Cmd), zap.Uint64("id", id), zap.Error(err))
		return
	}

	var res rpc.RpcResult
	err = rpc.RpcRequest(addr, args, &res)
	if nil != err {
		ec = pb.ErrCode_NO_REACTION
		log.Error("RpcRequest", zap.String("cmd", args.Cmd), zap.Uint64("id", id))
		return
	}

	Ev.OnMgrOfflineLogic(id)
}

func do_activity(c *gin.Context, args *rpc.RpcArgs, body string) {
	var ec pb.ErrCode = pb.ErrCode_PENDING

	defer func() {
		c.JSON(http.StatusOK, gin.H{
			"cmd": args.Cmd,
			"result": gin.H{
				"ec": ec,
			},
		})
	}()

	bparams, err := json.Marshal(args.Params)
	if nil != err {
		ec = pb.ErrCode_BAD_REQUEST
		return
	}
	err = mod.UpdateActivity(bparams)
	if nil != err {
		ec = pb.ErrCode_FAILED
		return
	}
	//var res rpc.RpcResult

	// 通知到所有服务器
	//TODO 优化，做工作池管理起来
	//go func() {
	//	_ = rpc.BroadCastToLobbies(args, &res)
	//}()

}

func do_chip_info(c *gin.Context, args *rpc.RpcArgs, body string) {
	p := simplejson.NewFrom(args.Params)
	md, ok := p.Interface().(map[string]interface{})
	if !ok {
		log.Error("interface error", zap.String("body", body))
		c.JSON(http.StatusOK, gin.H{
			"cmd": args.Cmd,
			"result": gin.H{
				"ec": pb.ErrCode_BAD_REQUEST,
			},
		})
		return
	}

	go func() {
		for sguid, ibets := range md {
			guid, err := strconv.ParseUint(sguid, 10, 64)
			if err != nil {
				log.Warn("ParseInt", zap.String("sguid", sguid))
				continue
			}
			bets := utils.If_to_int64(ibets, 0)
			if bets <= 0 {
				log.Warn("bets <= 0", zap.Uint64("guid", guid), zap.Int64("bets", bets))
				continue
			}
			__do_user_chip(guid, bets)
		}
	}()

	c.JSON(http.StatusOK, gin.H{
		"cmd": args.Cmd,
		"result": gin.H{
			"ec": pb.ErrCode_PENDING,
		},
	})
}

func __do_user_chip(guid uint64, bets int64) {
	svip, err := mod.GetUserAttr(guid, "vip")
	if nil != err {
		log.Warn("GetUserAttr", zap.Uint64("guid", guid), zap.Error(err))
		return
	}
	cur_level, err := strconv.ParseInt(svip, 10, 32)
	if nil != err {
		log.Error("ParseUint", zap.String("svip", svip), zap.Error(err))
		return
	}
	newlevel, _, err := nt.GetVIPByBets(bets)
	if err != nil {
		log.Error("GetVIPByBets", zap.Uint64("guid", guid), zap.Int64("bets", bets), zap.Error(err))
		return
	}

	err = mod.UpdateUserinfoToRedis(guid, utils.H{
		"vip":  newlevel,
		"bets": bets,
	})
	if nil != err {
		log.Error("UpdateUserinfoToRedis", zap.Uint64("guid", guid), zap.Error(err))
		return
	}
	if newlevel > int32(cur_level) {
		log.Info("user levelup", zap.Uint64("guid", guid), zap.Int64("bets", bets),
			zap.Int64("lvl", cur_level), zap.Int32("newLevel", newlevel))
		// 升级
		Ev.OnUserLevelup(guid, int32(cur_level), newlevel, bets)
	}
}

func do_get_promote(c *gin.Context, args *rpc.RpcArgs, body string) {
	p := simplejson.NewFrom(args.Params)
	guid := p.Get("guid").MustUint64()
	money := p.Get("money").MustInt64()
	ec := pb.ErrCode_OK

	defer func() {
		c.JSON(http.StatusOK, gin.H{
			"cmd": args.Cmd,
			"result": gin.H{
				"ec": ec,
			},
		})
	}()

	if !nt.DynConf.ValidGuid(guid) {
		ec = pb.ErrCode_BAD_REQUEST
		log.Error("bad guid", zap.Uint64("guid", guid))
		return
	}

	if money <= 0 {
		ec = pb.ErrCode_BAD_REQUEST
		log.Error("bad money", zap.Int64("money", money))
		return
	}

	agent, err := rpc.GetUserAgentRpcUrl(guid)
	if err != nil {
		newBalance, err := mod.AddUserBalance(guid, money)
		if err != nil {
			ec = pb.ErrCode_FAILED
			log.Error("AddUserBalance", zap.Uint64("guid", guid), zap.Int64("money", money), zap.Error(err))
			return
		}
		//TODO ev
		Ev.OnUserReward(guid, money, newBalance)
	} else {
		res := rpc.RpcResult{}
		err = rpc.RpcRequest(agent, args, &res)
		if err != nil {
			ec = pb.ErrCode_FAILED
			log.Error("RpcRequest", zap.Error(err), zap.Uint64("guid", guid), zap.Int64("money", money))
			return
		}
	}
}

func do_get_taskConf(c *gin.Context, args *rpc.RpcArgs, body string) {
	if nil != nt.Zk.SetNodeData(nt.ZkP.GameTask, []byte(body)) {
		c.JSON(http.StatusOK, gin.H{
			"cmd": args.Cmd,
			"result": gin.H{
				"ec": pb.ErrCode_FAILED,
			},
		})
		return
	}

	do_change_taskconf(c, args)
}

func do_change_taskconf(c *gin.Context, args *rpc.RpcArgs) {
	var res rpc.RpcResult

	//TODO 优化，做工作池管理起来
	go func() {
		_ = rpc.BroadCastToLobbies(args, &res)
	}()

	c.JSON(http.StatusOK, gin.H{
		"cmd": args.Cmd,
		"result": gin.H{
			"ec": pb.ErrCode_PENDING,
		},
	})

	//TODO 是否要发Nats回执
}

func do_not_read_message(c *gin.Context, args *rpc.RpcArgs, body string) {
	p := simplejson.NewFrom(args.Params)

	// parameters check
	fields, ok := p.Interface().(map[string]interface{})
	if !ok {
		log.Warn("do_not_read_message", zap.String("body", body))
		c.JSON(http.StatusOK, gin.H{
			"cmd": args.Cmd,
			"result": gin.H{
				"ec": pb.ErrCode_BAD_REQUEST,
			},
		})
	}
	// update to redis
	err := mod.UpdateUnreadCS(fields)
	if err != nil {
		log.Error("do_not_read_message", zap.Error(err))
	}

	// 实时推送
	go func() {
		for sguid, inum := range fields {
			guid, err := strconv.ParseUint(sguid, 10, 64)
			if err != nil {
				log.Error("do_not_read_message", zap.String("guid", sguid), zap.Error(err))
				continue
			}
			addr, err := rpc.GetUserAgentRpcUrl(guid)
			if err != nil {
				// user offline
				continue
			}
			ar := &rpc.RpcArgs{
				Cmd: "unread_cs",
				Params: utils.H{
					"guid": guid,
					"num":  inum,
				},
			}
			r := &rpc.RpcResult{}
			err = rpc.RpcRequest(addr, ar, r)
			if err != nil {
				log.Error("do_not_read_message", zap.Error(err), zap.Uint64("guid", guid))
			}
		}
	}()

	// return
	c.JSON(http.StatusOK, gin.H{
		"cmd": args.Cmd,
		"result": gin.H{
			"ec": pb.ErrCode_OK,
		},
	})
}

func do_critical_notice(c *gin.Context, args *rpc.RpcArgs, body string) {
	var res rpc.RpcResult

	//TODO 优化，做工作池管理起来
	go func() {
		_ = rpc.BroadCastToLobbies(args, &res)
	}()

	c.JSON(http.StatusOK, gin.H{
		"cmd": args.Cmd,
		"result": gin.H{
			"ec": pb.ErrCode_PENDING,
		},
	})
}

func do_reset_bank_pass(c *gin.Context, args *rpc.RpcArgs, body string) {
	p := simplejson.NewFrom(args.Params)

	parr, err := p.Array()
	if err != nil {
		log.Error("do_reset_bank_pass bad request", zap.Error(err))
		c.JSON(http.StatusBadRequest, gin.H{"cmd": args.Cmd, "result": gin.H{
			"ec": pb.ErrCode_BAD_REQUEST,
		}})
		return
	}

	ec := pb.ErrCode_OK
	for _, iguid := range parr {
		guid := utils.If_to_uint64(iguid, 0)
		// modify here
		if !nt.DynConf.ValidGuid(guid) {
			log.Error("do_reset_bank_pass bad guid", zap.Uint64("guid", guid))
			continue
		}
		if !mod.Exists(mod.KUserInfo(guid)) {
			log.Error("do_reset_bank_pass. user not exists")
			ec = pb.ErrCode_TARGET_NOT_FOUND
			break
		}

		err = mod.UpdateUserAttr(guid, "bankPass", "")
		if err != nil {
			log.Error("do_reset_bank_pass failed", zap.Error(err))
			ec = pb.ErrCode_FAILED
			break
		}
		log.Info("do_reset_bank_pass succeed", zap.Uint64("guid", guid))

		addr, err := rpc.GetUserAgentRpcUrl(guid)
		if err != nil {
			log.Warn("do_reset_bank_pass user offline", zap.Uint64("guid", guid))
			continue
		}

		a := &rpc.RpcArgs{
			Cmd: args.Cmd,
			Params: utils.H{
				"guid": guid,
			},
		}
		r := &rpc.RpcResult{}
		err = rpc.RpcRequest(addr, a, r)
		if err != nil {
			log.Warn("do_reset_bank_pass rpc failed", zap.Error(err))
		}
	}

	c.JSON(http.StatusBadRequest, gin.H{"cmd": args.Cmd, "result": gin.H{
		"ec": ec,
	}})
}

func do_red_packets(c *gin.Context, args *rpc.RpcArgs, body string){

	var res rpc.RpcResult
	ec := pb.ErrCode_OK
	type redEnvelope struct {
		Num int `json:"num"`
		Money int `json:"money"`
		RedType int `json:"redtype"`
		Exp int `json:"exp"`
		Guids []string `json:"guids"`
	}

	p := simplejson.NewFrom(args.Params)
	num   		:= p.Get("num").MustInt()
	money 		:= p.Get("money").MustInt()
	red_type    := p.Get("type").MustInt()
	exp			:= p.Get("exp").MustInt()
	guids 		:= p.Get("guids").MustStringArray()

	var el redEnvelope
	el.Num = num
	el.Money = money
	el.RedType = red_type
	el.Exp = exp
	el.Guids = guids

	log.Debug("红包参数",zap.Any("data",el))
	json_,err := json.Marshal(el)
	if err != nil{
		log.Debug("do_red_packets Marshal err",zap.Error(err))
		ec = pb.ErrCode_FAILED
		return
	}

	exp_t,_ := time.ParseDuration(strconv.Itoa(exp)) //多少秒后过期
	if err := mod.UpdateRedEnvelope(json_,exp_t);err != nil{
		log.Debug("UpdateRedEnvelope err",zap.Error(err))
		ec = pb.ErrCode_FAILED
		return
	}

	//TODO 优化，做工作池管理起来
	go func() {
		_ = rpc.BroadCastToLobbies(args, &res)
	}()

	c.JSON(http.StatusOK, gin.H{
		"cmd": args.Cmd,
		"result": gin.H{
			"ec": ec,
		},
	})
}

func do_hand_withdraw(c *gin.Context, args *rpc.RpcArgs, body string){
	var res rpc.RpcResult
	_ = rpc.BroadCastToOneLobby(args, &res)
	c.JSON(http.StatusOK, gin.H{
		"cmd": args.Cmd,
		"result": gin.H{
			"ec": res.Result.(int),
		},
	})
}

func do_withdraw_success(c *gin.Context, args *rpc.RpcArgs, body string) {
	p := simplejson.NewFrom(args.Params)

	parr, err := p.Array()
	if err != nil {
		log.Error("do_withdraw_success bad request", zap.Error(err))
		c.JSON(http.StatusBadRequest, gin.H{"cmd": args.Cmd, "result": gin.H{
			"ec": pb.ErrCode_BAD_REQUEST,
		}})
		return
	}

	ec := pb.ErrCode_OK
	for _, iguid := range parr {
		guid := utils.If_to_uint64(iguid, 0)
		// modify here
		if !nt.DynConf.ValidGuid(guid) {
			log.Error("do_withdraw_success bad guid", zap.Uint64("guid", guid))
			continue
		}
		if !mod.Exists(mod.KUserInfo(guid)) {
			log.Warn("do_withdraw_success. user not exists", zap.Uint64("guid", guid))
			ec = pb.ErrCode_TARGET_NOT_FOUND
			break
		}
		_, err := mod.AddUserWithDraw(guid, 100)
		if err != nil {
			log.Error("do_withdraw_success failed", zap.Error(err))
			ec = pb.ErrCode_FAILED
			break
		}
	}

	c.JSON(http.StatusBadRequest, gin.H{"cmd": args.Cmd, "result": gin.H{
		"ec": ec,
	}})
}

func do_reset_alipay(c *gin.Context, args *rpc.RpcArgs, body string) {
	p := simplejson.NewFrom(args.Params)

	parr, err := p.Array()
	if err != nil {
		log.Error("do_reset_alipay bad request", zap.Error(err))
		c.JSON(http.StatusBadRequest, gin.H{"cmd": args.Cmd, "result": gin.H{
			"ec": pb.ErrCode_BAD_REQUEST,
		}})
		return
	}

	ec := pb.ErrCode_OK
	for _, iguid := range parr {
		guid := utils.If_to_uint64(iguid, 0)
		// modify here
		if !nt.DynConf.ValidGuid(guid) {
			log.Error("do_reset_alipay bad guid", zap.Uint64("guid", guid))
			continue
		}
		if !mod.Exists(mod.KUserInfo(guid)) {
			log.Warn("do_reset_alipay. user not exists", zap.Uint64("guid", guid))
			ec = pb.ErrCode_TARGET_NOT_FOUND
			break
		}
		// get alipay
		alipay, err := mod.GetUserAttr(guid, "alipay")
		if err != nil {
			log.Error("do_reset_alipay failed", zap.Error(err), zap.Uint64("guid", guid))
			ec = pb.ErrCode_FAILED
			break
		}
		// remove bind
		err = mod.RemAlipayBind(alipay)
		if err != nil {
			log.Error("do_reset_alipay failed", zap.Error(err), zap.Uint64("guid", guid))
			ec = pb.ErrCode_FAILED
			break
		}
		// remove alipay
		err = mod.UpdateUserAttr(guid, "alipay", "")
		if err != nil {
			log.Error("do_reset_alipay failed", zap.Error(err), zap.Uint64("guid", guid))
			ec = pb.ErrCode_FAILED
			break
		}
	}

	c.JSON(http.StatusBadRequest, gin.H{"cmd": args.Cmd, "result": gin.H{
		"ec": ec,
	}})
}