package main

import (
	"../mod"
	"net/http"
	"strconv"
	"time"

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

type RpcService struct{}
type CallbackRpc func(*http.Request, *rpc.RpcArgs, *rpc.RpcResult)

var (
	MCbRpc = map[string]CallbackRpc{
		"change_channel":       change_channel,       	// 更新渠道
		"change_game":          change_game,          	// 更新游戏
		"change_client_config": change_client_config, 	// 更新客户端配置
		"change_money":         change_money,         	// 更新余额
		"change_bank_money":    change_bank_money,    	// 更新银行
		"disable":              disable,              	// 禁止/解禁用户
		"online_list":          online_list,          	// 在线玩家
		"private_notice":       private_notice,       	// 私信
		"system_notice":        system_notice,        	// 跑马灯
		"delete_system_notice": delete_system_notice, 	// 删除跑马灯
		"recharge":             recharge,             	// 充值
		"withdraw":             withdraw,             	// 提现
		"kickoff":              kickoff,              	// 踢人
		"server_info":          server_info,          	// 服务器信息
		"server_offline":       server_offline,       	// 踢服务器
		"activity":             activity,             	// 活动
		"wallet_transfer":      wallet_transfer,      	// 钱包金钱变动
		"vip_up":               vip_up,               	// vip升级
		"get_promote":          get_promote,          	// 领取佣金
		"task_config":          task_config,          	// 游戏任务配置
		"unread_cs":			unread_cs,				// 未读客服消息
		"critical_notice":		critical_notice, 		// 紧急通知
		"reset_bank_pass":		reset_bank_pass,		// 后台重置密码
		"ntf_client":			ntf_client,			    // 通知客户端金币变动
		"red_packets":			red_packets,			// 发红包
		"hand_withdraw":		hand_withdraw,			// 手动下分
	}
)

func (self *RpcService) Request(req *http.Request, args *rpc.RpcArgs, result *rpc.RpcResult) error {
	log.Debug("rpc request", zap.String("cmd", args.Cmd))

	if _func_, ok := MCbRpc[args.Cmd]; ok {
		_func_(req, args, result)
	} else {
		log.Warn("rpc command not supported:", zap.String("cmd", args.Cmd))
	}
	return nil
}

func kickoff(req *http.Request, args *rpc.RpcArgs, result *rpc.RpcResult) {
	// 查看用户是否在线
	// 不在线返回成功
	// 在线，向逻辑层申请踢掉，逻辑取消成功后，通知客户端，然后断开连接
	js := simplejson.NewFrom(args.Params)
	guids := js.Get("guids").MustArray()
	reason := js.Get("reason").MustInt64()

	ec := pb.ErrCode_OK
	if reason <= 0 {
		reason = int64(pb.ReasonDisconn_KICK)
	}

	defer func() {
		*result = rpc.RpcResult{
			Cmd: args.Cmd,
			Result: utils.H{
				"ec": ec,
			},
		}
	}()

	for _, ifguid := range guids {
		guid := utils.If_to_uint64(ifguid, 0)
		ses, err := Server.GetSession(guid)
		if err != nil {
			ec = pb.ErrCode_TARGET_NOT_FOUND
			return
		}
		// TODO: 踢人操作要更加复杂，如果玩家在游戏中，需要尝试退出成功，并且把金币稳定下来。
		KillUserSession(guid, ses, pb.ReasonDisconn(reason))
		if reason == int64(pb.ReasonDisconn_KICK) {
			defKickoffPolicy.OnUserKickedoff(guid)
		}
		Ev.OnUserKickedoff(guid)
	}
}

func change_channel(req *http.Request, args *rpc.RpcArgs, result *rpc.RpcResult) {
	*result = rpc.RpcResult{
		Cmd: args.Cmd,
		Result: utils.H{
			"ec": 0,
		},
	}
}

func change_game(req *http.Request, args *rpc.RpcArgs, result *rpc.RpcResult) {
	*result = rpc.RpcResult{
		Cmd: args.Cmd,
		Result: utils.H{
			"ec": 0,
		},
	}
}

func change_money(req *http.Request, args *rpc.RpcArgs, result *rpc.RpcResult) {
	// 既然收到，就意味着 web 方没有操作
	js := simplejson.NewFrom(args.Params)
	guid := js.Get("guid").MustUint64()
	money := js.Get("money").MustInt64()

	newval, err := mod.AddUserBalance(guid, money)
	if err != nil {
		*result = rpc.RpcResult{
			Cmd: args.Cmd,
			Result: utils.H{
				"ec": int32(pb.ErrCode_UNKNOWN),
			},
		}
		return
	}

	ses, err := Server.GetSession(guid)
	if nil == err { //在线
		var reason int32 = int32(pb.ReasonBalanceChange_MGR_ADD)
		if money < 0 {
			reason = int32(pb.ReasonBalanceChange_MGR_DEDUC)
		}
		SendToClient(ses, guid, &pb.S_Ntf_UserBalance{
			Guid:    guid,
			Reason:  reason,
			Balance: newval,
			Delta:   money,
		})
	}

	Ev.OnMgrChangeBalance(guid, money, newval, ses)

	*result = rpc.RpcResult{
		Cmd: args.Cmd,
		Result: utils.H{
			"ec": 0,
		},
	}
}

func change_bank_money(req *http.Request, args *rpc.RpcArgs, result *rpc.RpcResult) {
	// 既然收到，就意味着 web 方没有操作
	js := simplejson.NewFrom(args.Params)
	guid := js.Get("guid").MustUint64()
	money := js.Get("money").MustInt64()

	newval, err := mod.AddUserBank(guid, money)
	if err != nil {
		*result = rpc.RpcResult{
			Cmd: args.Cmd,
			Result: utils.H{
				"ec": int32(pb.ErrCode_FAILED),
			},
		}
		return
	}

	ses, err := Server.GetSession(guid)
	if nil == err { //在线
		// 通知
		var reason int32 = int32(pb.ReasonBalanceChange_MGR_ADD)
		if money < 0 {
			reason = int32(pb.ReasonBalanceChange_MGR_DEDUC)
		}
		SendToClient(ses, guid, &pb.S_Ntf_UserBank{
			Reason: reason,
			Bank:   newval,
			Delta:  money,
		})
	}

	Ev.OnMgrChangeBank(guid, money, newval, ses)

	*result = rpc.RpcResult{
		Cmd: args.Cmd,
		Result: utils.H{
			"ec": 0,
		},
	}
}

func disable(req *http.Request, args *rpc.RpcArgs, result *rpc.RpcResult) {
	p := simplejson.NewFrom(args.Params)
	guid := p.Get("guid").MustUint64()
	status := p.Get("status").MustInt64()

	err := mod.UpdateUserinfoToRedis(guid, map[string]interface{}{
		"status": status,
	})
	if nil != err {
		*result = rpc.RpcResult{
			Cmd: args.Cmd,
			Result: utils.H{
				"ec": int32(pb.ErrCode_FAILED),
			},
		}
		return
	}

	ses, err := Server.GetSession(guid)
	if nil == err { //在线
		KillUserSession(guid, ses, pb.ReasonDisconn_DISABLED)
	}

	Ev.OnMgrDisableUser(guid, status, ses)

	*result = rpc.RpcResult{
		Cmd: args.Cmd,
		Result: utils.H{
			"ec": 0,
		},
	}
}

// No need to implement
func online_list(req *http.Request, args *rpc.RpcArgs, result *rpc.RpcResult) {
}

func private_notice(req *http.Request, args *rpc.RpcArgs, result *rpc.RpcResult) {
	p := simplejson.NewFrom(args.Params)
	guid := p.Get("guid").MustUint64()

	PostUserNewMessages(guid)

	*result = rpc.RpcResult{
		Cmd: args.Cmd,
		Result: utils.H{
			"ec": 0,
		},
	}
}

func PostUserNewMessages(guid uint64) {
	ses, err := Server.GetSession(guid)
	if nil != err {
		log.Warn("user offline", zap.Uint64("guid", guid))
		return
	}

	r := &pb.S_Ntf_NewMessage{
		Mails:   0,
		Notices: 0,
	}

	total, err := mod.GetPrivateMailNum(guid)
	if nil != err {
		log.Warn("GetPrivateMailNum", zap.Uint64("guid", guid))
	} else {
		r.Mails = int32(total)
	}
	r.Notices = GetNoticeCount()

	SendToClient(ses, guid, r)
}

func GetNoticeCount() int32 {
	ns, err := mod.GetNotices()
	if nil != err {
		log.Warn("GetNotices", zap.Error(err))
		return 0
	}
	var count int32 = 0
	now := time.Now().Unix()
	for _, n := range ns {
		j1, err := simplejson.NewJson([]byte(n))
		if nil != err {
			log.Warn("DataError", zap.String("data", n))
			continue
		}
		id := j1.Get("params").Get("id").MustInt64()
		endtime := j1.Get("params").Get("end_time").MustInt64()
		if endtime > now {
			count += 1
		} else {
			go func() {
				mod.RemoveNotices(id)
			}()
		}
	}
	return count
}

// 客户端配置
func change_client_config(req *http.Request, args *rpc.RpcArgs, result *rpc.RpcResult) {
	*result = rpc.RpcResult{
		Cmd: args.Cmd,
		Result: utils.H{
			"ec": 0,
		},
	}
}

// 跑马灯 + 公告
func system_notice(req *http.Request, args *rpc.RpcArgs, result *rpc.RpcResult) {
	p := simplejson.NewFrom(args.Params)
	typ := p.Get("type").MustInt64()

	if typ == 1 {
		__broadcast_new_marquee(p)
	} else if typ == 2 {
		__broadcast_new_notice(p)
	}

	*result = rpc.RpcResult{
		Cmd: args.Cmd,
		Result: utils.H{
			"ec": 0,
		},
	}
}

func __broadcast_new_marquee(p *simplejson.Json) {
	b := &pb.S_Ntf_Marquee{}

	item := &pb.Marquee{
		Id:      int32(p.Get("id").MustInt64()),
		Content: p.Get("content").MustString(),
		Times:	int32(p.Get("turn_count").MustInt64()),
		Start:   p.Get("start_time").MustInt64(),
		End:     p.Get("end_time").MustInt64(),
		Enabled: 1,
	}
	b.Mqs = append(b.Mqs, item)

	BroadcastToClient(b)
}

func __broadcast_new_notice(p *simplejson.Json) {
	r := &pb.S_Ntf_NewMessage{
		Mails:   -1,
		Notices: GetNoticeCount(),
	}
	BroadcastToClient(r)
}

// 删除跑马灯
func delete_system_notice(req *http.Request, args *rpc.RpcArgs, result *rpc.RpcResult) {
	p := simplejson.NewFrom(args.Params)
	b := &pb.S_Ntf_Marquee{}

	item := &pb.Marquee{
		Id:      int32(p.Get("id").MustInt64()),
		Enabled: 0,
	}
	b.Mqs = append(b.Mqs, item)

	BroadcastToClient(b)

	*result = rpc.RpcResult{
		Cmd: args.Cmd,
		Result: utils.H{
			"ec": 0,
		},
	}
}

// 充值加币
func recharge(req *http.Request, args *rpc.RpcArgs, result *rpc.RpcResult) {
	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() {
		*result = rpc.RpcResult{
			Cmd: args.Cmd,
			Result: utils.H{
				"ec": ec,
			},
		}
	}()

	// 传到这里假设用户在线,还没有操作
	if !nt.DynConf.ValidGuid(guid) {
		log.Warn("recharge invalid guid", zap.Int64("delta", money), zap.Uint64("guid", guid),
			zap.Int64("orderId", id))
		ec = pb.ErrCode_BAD_REQUEST
		return
	}
	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
	}

	// 操作加币
	newVal, err := mod.AddUserBalance(guid, money)
	if err != nil {
		log.Error("recharge", zap.Int64("delta", money), zap.Uint64("guid", guid),
			zap.Int64("orderId", id))
		ec = pb.ErrCode_UNKNOWN
		return
	}

	_, err = mod.AddUserRecharged(guid, money)
	if err != nil {
		log.Error("recharge", zap.Uint64("guid", guid), zap.Int64("delta", money))
	}

	// 用户是否在线
	ses, err := Server.GetSession(guid)
	if nil == err {
		// 在线通知
		m := &pb.S_Ntf_UserBalance{
			Guid:    guid,
			Reason:  int32(pb.ReasonBalanceChange_RECHARGE),
			Balance: newVal,
			Delta:   money,
		}
		SendToClient(ses, guid, m)
	}
	// 充值事件
	Ev.OnUserRecharge(guid, money, newVal, id)
}

// 提现扣币
func withdraw(req *http.Request, args *rpc.RpcArgs, result *rpc.RpcResult) {
	p := simplejson.NewFrom(args.Params)
	guid := p.Get("guid").MustUint64()
	money := p.Get("money").MustInt64()
	ec := pb.ErrCode_OK

	defer func() {
		*result = rpc.RpcResult{
			Cmd: args.Cmd,
			Result: utils.H{
				"ec": ec,
			},
		}
	}()

	// 传到这里假设用户在线,还没有操作
	if !nt.DynConf.ValidGuid(guid) {
		log.Warn("withdraw invalid guid", zap.Int64("delta", money), zap.Uint64("guid", guid))
		ec = pb.ErrCode_BAD_REQUEST
		return
	}
	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("withdraw 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("withdraw ParseInt", zap.Int64("delta", money), zap.Uint64("guid", guid))
		ec = pb.ErrCode_UNKNOWN
		return
	}
	if balance < money {
		log.Error("withdraw", zap.Int64("delta", money), zap.Int64("balance", balance), zap.Uint64("guid", guid))
		ec = pb.ErrCode_BALANCE_INSUFFICIENT
		return
	}

	newVal, err := mod.AddUserBalance(guid, -money)
	if nil != err {
		log.Error("withdraw AddUserBalance", zap.Int64("delta", -money), zap.Int64("balance", balance),
			zap.Uint64("guid", guid), zap.Error(err))
		ec = pb.ErrCode_UNKNOWN
		return
	}
	// 增加用户提现记录
	// update: comment, 因为扣币不代表提现成功
	//_, err = mod.AddUserWithDraw(guid, money)
	//if err != nil {
	//	log.Error("withdraw AddUserWithDraw", zap.Uint64("guid", guid), zap.Int64("delta", money))
	//}

	// 用户是否在线
	ses, err := Server.GetSession(guid)
	if nil == err {
		// 在线通知
		m := &pb.S_Ntf_UserBalance{
			Guid:    guid,
			Reason:  int32(pb.ReasonBalanceChange_WITHDRAW),
			Balance: newVal,
			Delta:   money,
		}
		SendToClient(ses, guid, m)
	}

	// 事件
	Ev.OnUserWithdrawSuc(guid, money, newVal)
}

// 服务器信息
func server_info(req *http.Request, args *rpc.RpcArgs, result *rpc.RpcResult) {
	*result = rpc.RpcResult{
		Cmd: args.Cmd,
		Result: utils.H{
			"ec": 0,
			"list": utils.H{
				"users":  Server.GetSessionNum(),
				"online": Online,
				"uptime": time.Since(nt.BootTime).Seconds(),
			},
		},
	}
}

// 使下线
func server_offline(req *http.Request, args *rpc.RpcArgs, result *rpc.RpcResult) {
	p := simplejson.NewFrom(args.Params)
	id := p.Get("id").MustUint64()
	ec := pb.ErrCode_OK

	defer func() {
		*result = rpc.RpcResult{
			Cmd: args.Cmd,
			Result: utils.H{
				"ec": ec,
			},
		}
	}()

	if id != nt.InstConf.Id {
		ec = pb.ErrCode_BAD_REQUEST
		log.Warn("bad id", zap.Uint64("id", id), zap.Uint64("conf", nt.InstConf.Id))
		return
	}

	Online = 0
	Login.Stop()
}

//@deprecated
func activity(req *http.Request, args *rpc.RpcArgs, result *rpc.RpcResult) {

	*result = rpc.RpcResult{
		Cmd: args.Cmd,
		Result: utils.H{
			"ec": pb.ErrCode_OK,
		},
	}
}

func wallet_transfer(req *http.Request, args *rpc.RpcArgs, result *rpc.RpcResult) {
	p := simplejson.NewFrom(args.Params)
	guid := p.Get("guid").MustUint64()
	ec := pb.ErrCode_OK

	defer func() {
		*result = rpc.RpcResult{
			Cmd: args.Cmd,
			Result: utils.H{
				"ec": ec,
			},
		}
	}()

	if !nt.DynConf.ValidGuid(guid) {
		log.Error("invalid guid", zap.Uint64("guid", guid))
		ec = pb.ErrCode_BAD_REQUEST
		return
	}
	r := &pb.S_Ntf_UserBalance{
		Guid:    guid,
		Delta:   p.Get("delta").MustInt64(),
		Balance: p.Get("newBalance").MustInt64(),
		Reason:  int32(pb.ReasonBalanceChange_BET),
	}

	ses, err := Server.GetSession(guid)
	if nil != err {
		log.Debug("user offline", zap.Uint64("guid", guid), zap.Error(err))
		ec = pb.ErrCode_TARGET_NOT_FOUND
		return
	}

	SendToClient(ses, guid, r)
}

func vip_up(req *http.Request, args *rpc.RpcArgs, result *rpc.RpcResult) {
	p := simplejson.NewFrom(args.Params)
	guid := p.Get("guid").MustUint64()
	level := p.Get("newVip").MustInt64()

	ses, err := Server.GetSession(guid)
	if err != nil {
		log.Warn("no session", zap.Uint64("guid", guid))
		return
	}

	SendToClient(ses, guid, &pb.S_NtfVipChange{Vip: int32(level)})
}

func get_promote(req *http.Request, args *rpc.RpcArgs, result *rpc.RpcResult) {
	p := simplejson.NewFrom(args.Params)
	guid := p.Get("guid").MustUint64()
	money := p.Get("money").MustInt64()
	ec := pb.ErrCode_OK

	defer func() {
		*result = rpc.RpcResult{
			Cmd: args.Cmd,
			Result: utils.H{
				"ec": ec,
			},
		}
	}()

	if !nt.DynConf.ValidGuid(guid) || money <= 0 {
		log.Error("bad args", zap.Uint64("guid", guid), zap.Int64("money", money))
		ec = pb.ErrCode_BAD_REQUEST
		return
	}

	ses, err := Server.GetSession(guid)
	if err != nil {
		log.Warn("user offline", zap.Uint64("guid", guid), zap.Error(err))
		return
	}
	// 不在线不发放，因为不在线应该在 manager 处理。
	newBalance, err := mod.AddUserBalance(guid, money)
	if err != nil {
		log.Error("AddUserBalance", zap.Uint64("guid", guid), zap.Int64("money", money), zap.Error(err))
		ec = pb.ErrCode_FAILED
		return
	}

	Ev.OnUserReward(guid, money, newBalance, int32(pb.ReasonBalanceChange_PROMOTE), ses)
}

func task_config(req *http.Request, args *rpc.RpcArgs, result *rpc.RpcResult) {
	*result = rpc.RpcResult{
		Cmd: args.Cmd,
		Result: utils.H{
			"ec": 0,
		},
	}
}

func unread_cs(req *http.Request, args *rpc.RpcArgs, result *rpc.RpcResult) {
	p := simplejson.NewFrom(args.Params)
	guid := p.Get("guid").MustUint64()
	//TODO: 利用 num 少查询一次 redis
	//num := p.Get("num").MustUint64()
	// 通知
	ses, err := Server.GetSession(guid)
	if err != nil {
		// user should online now.
		log.Warn("unread_cs", zap.Uint64("guid", guid), zap.Error(err))
		return
	}

	PostUserUnreadCS(ses, guid)

	*result = rpc.RpcResult{
		Cmd: args.Cmd,
		Result: utils.H{
			"ec": 0,
		},
	}
}

func ntf_client(req *http.Request, args *rpc.RpcArgs, result *rpc.RpcResult) {
	p := simplejson.NewFrom(args.Params)
	guid := p.Get("guid").MustUint64()
	balance := p.Get("balance").MustInt64()

	ses, err := Server.GetSession(guid)
	if err != nil {
		log.Warn("ntf_client", zap.Uint64("guid", guid), zap.Error(err))
		return
	}

	p_b := &pb.S_Ntf_UserBalance{}
	p_b.Balance = balance
	SendToClient(ses, ses.Id(), p_b)
	*result = rpc.RpcResult{
		Cmd: args.Cmd,
		Result: utils.H{
			"ec": 0,
		},
	}
}

func red_packets(req *http.Request, args *rpc.RpcArgs, result *rpc.RpcResult){
	b := &pb.S_Ntf_RedEnvelope{}
	b.Ec = 0
	p     := simplejson.NewFrom(args.Params)
	guids := p.Get("guids").MustStringArray()

	if len(guids) > 0 {
		sess := Server.GetAllSessions()
		for _, guid := range  guids{
			for id, ses := range sess  {
				if strconv.FormatUint(id,10) == guid {
					SendToClient(ses, id, b)
				}
			}
		}

	}else{
		BroadcastToClient(b)
	}
}

func hand_withdraw(req *http.Request, args *rpc.RpcArgs, result *rpc.RpcResult){
	p     := simplejson.NewFrom(args.Params)
	guid_str := p.Get("guid").MustString()
	guid_uint,err := strconv.ParseUint(guid_str,10,64)
	if err != nil{
		log.Debug("hand_withdraw ParseUint err",zap.Error(err))
		result.Result = -1 //系统错误 联系技术
	}
	ok, detal := gameapi.PHP_withdraw(guid_str)
	if ok {
		balan_ := &pb.S_Ntf_UserBalance{Balance:detal}
		if gameapi.OnSettlement("后台手动下分",guid_uint, balan_){
			result.Result = 0 //下分成功
		}else{
			result.Result = 1  //下分成功 金币没加 联系技术
		}
	}else{
		result.Result = 2      //下分失败 联系技术
	}
}