package main

import (
	"../cross"
	"../gameapi"
	"../log"
	"../mod"
	"../nnet"
	pb "../protos"
	"../utils"
	"encoding/json"
	"github.com/gogo/protobuf/proto"
	"go.uber.org/zap"
	"math"
	"math/rand"
	"strconv"
	"time"
)

func do_C_Checkin30Status(ses nnet.ISession, req *pb.Request) {
	r := &pb.S_Checkin30Status{
		Ec: int32(pb.ErrCode_OK),
	}

	status, ec := GetCheckin30Status(req.Guid)
	if ec != pb.ErrCode_OK {
		r.Ec = int32(ec)
		SendToClient(ses, req.Guid, r)
		return
	}

	r.Vip = status.Vip
	r.Daily = status.Daily
	r.Ext = status.Ext

	SendToClient(ses, req.Guid, r)
}

func do_C_GetChechin30(ses nnet.ISession, req *pb.Request) {
	p := &pb.C_GetChechin30{}
	r := &pb.S_GetCheckin30{}

	err := proto.Unmarshal(req.Req, p)
	if nil != err {
		log.Error("do_C_GetChechin30", zap.Error(err))
		r.Ec = int32(pb.ErrCode_BAD_REQUEST)
		SendToClient(ses, req.Guid, r)
		return
	}
	// user bind
	if !cross.IsBindUser(req.Guid) {
		log.Warn("do_C_GetChechin30 not bind phone", zap.Uint64("guid", req.Guid))
		r.Ec = int32(pb.ErrCode_UNBIND)
		SendToClient(ses, req.Guid, r)
		return
	}
	// user acitve
	if !UA_IsTodayActive(req.Guid) {
		log.Debug("do_C_GetCheckin30 not active", zap.Uint64("guid", req.Guid))
		r.Ec = int32(pb.ErrCode_INSUFFICIENT)
		SendToClient(ses, req.Guid, r)
		return
	}

	status, ec := GetCheckin30Status(req.Guid)
	if ec != pb.ErrCode_OK {
		r.Ec = int32(ec)
		SendToClient(ses, req.Guid, r)
		return
	}
	//
	if status.Daily.Got != int32(pb.RewardStatus_INREADY) {
		r.Ec = int32(pb.ErrCode_INSUFFICIENT)
		SendToClient(ses, req.Guid, r)
		return
	}
	// 标记
	status.Daily.Ts = time.Now().Unix()
	status.Daily.Got = int32(pb.RewardStatus_GOTTEN)
	status.Daily.Days += 1
	status.TotalTimes += 1
	status.TotalReward += status.Daily.Bonus

	// 保存状态
	err = status.Saveback(req.Guid)
	if nil != err {
		log.Error("do_C_GetChechin30", zap.Uint64("guid", req.Guid), zap.Error(err))
		r.Ec = int32(pb.ErrCode_FAILED)
		SendToClient(ses, req.Guid, r)
		return
	}
	// 领取
	newBalance, err := mod.AddUserBalance(req.Guid, status.Daily.Bonus)
	if err != nil {
		log.Error("do_C_GetChechin30", zap.Uint64("guid", req.Guid), zap.Error(err))
		r.Ec = int32(pb.ErrCode_FAILED)
		SendToClient(ses, req.Guid, r)
		return
	}

	r.Delta = status.Daily.Bonus
	SendToClient(ses, req.Guid, r)

	// 事件
	Ev.OnUserReward(req.Guid, status.Daily.Bonus, newBalance, int32(pb.ReasonBalanceChange_CHECKIN), ses)
}

func do_C_GetCheckin30Ext(ses nnet.ISession, req *pb.Request) {
	p := &pb.C_GetCheckin30Ext{}
	r := &pb.S_GetCheckin30Ext{}

	err := proto.Unmarshal(req.Req, p)
	if nil != err {
		log.Error("do_C_GetChechin30", zap.Error(err))
		r.Ec = int32(pb.ErrCode_BAD_REQUEST)
		SendToClient(ses, req.Guid, r)
		return
	}
	// 必须绑定
	if !cross.IsBindUser(req.Guid) {
		log.Warn("do_C_GetChechin30 not bind phone", zap.Uint64("guid", req.Guid))
		r.Ec = int32(pb.ErrCode_UNBIND)
		SendToClient(ses, req.Guid, r)
		return
	}

	status, ec := GetCheckin30Status(req.Guid)
	if ec != pb.ErrCode_OK {
		r.Ec = int32(ec)
		SendToClient(ses, req.Guid, r)
		return
	}

	if p.Index < 0 || p.Index >= int32(len(status.Ext)) {
		r.Ec = int32(pb.ErrCode_BAD_REQUEST)
		SendToClient(ses, req.Guid, r)
		return
	}

	if status.Ext[p.Index].Got != int32(pb.RewardStatus_INREADY) {
		r.Ec = int32(pb.ErrCode_INSUFFICIENT)
		SendToClient(ses, req.Guid, r)
		return
	}

	// 保存标记
	status.Ext[p.Index].Got = int32(pb.RewardStatus_GOTTEN)
	status.Ext[p.Index].Ts = time.Now().Unix()
	status.TotalReward += status.Ext[p.Index].Bonus

	err = status.Saveback(req.Guid)
	if nil != err {
		log.Error("do_C_GetCheckin30Ext", zap.Uint64("guid", req.Guid), zap.Error(err))
		r.Ec = int32(pb.ErrCode_FAILED)
		SendToClient(ses, req.Guid, r)
		return
	}

	// 领取
	newBalance, err := mod.AddUserBalance(req.Guid, status.Ext[p.Index].Bonus)
	if err != nil {
		log.Error("do_C_GetCheckin30Ext", zap.Uint64("guid", req.Guid), zap.Error(err))
		r.Ec = int32(pb.ErrCode_FAILED)
		SendToClient(ses, req.Guid, r)
		return
	}

	r.Delta = status.Ext[p.Index].Bonus
	SendToClient(ses, req.Guid, r)

	// 事件
	Ev.OnUserReward(req.Guid, r.Delta, newBalance, int32(pb.ReasonBalanceChange_CHECKIN_BONUS), ses)
}

func do_C_UpdatePasswordV(ses nnet.ISession, req *pb.Request) {
	p := &pb.C_UpdatePasswordV{}
	r := &pb.S_UpdatePassword{}

	err := proto.Unmarshal(req.Req, p)
	if nil != err {
		log.Error("do_C_UpdatePasswordV", zap.Error(err))
		r.Ec = int32(pb.ErrCode_BAD_REQUEST)
		SendToClient(ses, req.Guid, r)
		return
	}
	phone, err := mod.GetUserAttr(req.Guid, "phone")
	if err != nil {
		log.Error("do_C_UpdatePasswordV", zap.Error(err))
		r.Ec = int32(pb.ErrCode_FAILED)
		SendToClient(ses, req.Guid, r)
		return
	}
	if !utils.IsPhoneSimple(phone) {
		log.Error("do_C_UpdatePasswordV", zap.Error(err))
		r.Ec = int32(pb.ErrCode_TARGET_NOT_FOUND)
		SendToClient(ses, req.Guid, r)
		return
	}
	// 验证
	code, err := mod.GetVerifiCode(req.Guid, phone)
	if code != p.VerifyCode {
		log.Error("do_C_UpdatePasswordV verify code not match", zap.String("code", code),
			zap.String("p.VerifyCode", p.VerifyCode))
		r.Ec = int32(pb.ErrCode_TIMEOUT)
		SendToClient(ses, req.Guid, r)
		return
	}
	err = mod.UpdateUserPassword(req.Guid, p.Password)
	if nil != err {
		log.Error("do_C_UpdatePasswordV", zap.Error(err))
		r.Ec = int32(pb.ErrCode_UNKNOWN)
		SendToClient(ses, req.Guid, r)
		return
	}

	mod.ClearVerifiCode(req.Guid, phone)
	// login
	err = LoginPostUpdatePassword(req.Guid, ses, p.Password)
	if nil != err {
		log.Error("do_C_UpdatePasswordV", zap.Uint64("guid", req.Guid), zap.Error(err))
		r.Ec = int32(pb.ErrCode_FAILED)
		SendToClient(ses, req.Guid, r)
		return
	}
}

func do_C_TurnTableStatus(ses nnet.ISession, req *pb.Request) {
	p := &pb.C_TurnTableStatus{}
	r := &pb.S_TurnTableStatus{}

	err := proto.Unmarshal(req.Req, p)
	if nil != err {
		log.Error("do_C_TurnTableStatus", zap.Error(err))
		r.Ec = int32(pb.ErrCode_BAD_REQUEST)
		SendToClient(ses, req.Guid, r)
		return
	}

	r = cross.TurntableStatus(req.Guid)

	SendToClient(ses, req.Guid, r)
}

// 必须为绑定用户
func do_C_TurnTablePlay(ses nnet.ISession, req *pb.Request) {
	p := &pb.C_TurnTablePlay{}
	r := &pb.S_TurnTablePlay{}

	err := proto.Unmarshal(req.Req, p)
	if nil != err {
		log.Error("do_C_TurnTablePlay", zap.Error(err))
		r.Ec = int32(pb.ErrCode_BAD_REQUEST)
		SendToClient(ses, req.Guid, r)
		return
	}
	// bind user
	if !cross.IsBindUser(req.Guid) {
		log.Warn("do_C_TurnTablePlay not bind phone", zap.Uint64("guid", req.Guid))
		r.Ec = int32(pb.ErrCode_UNBIND)
		SendToClient(ses, req.Guid, r)
		return
	}

	r = cross.TurntablePlay(req.Guid)

	SendToClient(ses, req.Guid, r)
}

func do_C_TurnTableGet(ses nnet.ISession, req *pb.Request) {
	p := &pb.C_TurnTableGet{}
	r := &pb.S_TurnTableGet{}

	err := proto.Unmarshal(req.Req, p)
	if nil != err {
		log.Error("do_C_UpdatePasswordV", zap.Error(err))
		r.Ec = int32(pb.ErrCode_BAD_REQUEST)
		SendToClient(ses, req.Guid, r)
		return
	}
	delta, newBalance, ec := turntable_award_user(req.Guid)
	if ec != pb.ErrCode_OK {
		r.Ec = int32(ec)
		SendToClient(ses, req.Guid, r)
	}
	r.Reward = delta
	SendToClient(ses, req.Guid, r)

	log.Info("do_C_TurnTableGet", zap.Uint64("guid", req.Guid), zap.Int64("delta", r.Reward), zap.Int64("balance", newBalance))

	Ev.OnUserReward(req.Guid, delta, newBalance, int32(pb.ReasonBalanceChange_TURNTABLE), ses)
}

// @发放所有累计的转盘奖励
// @return: 奖励金额，奖励后总金额，错误码
func turntable_award_user(guid uint64) (int64, int64, pb.ErrCode) {
	blocked, ec := cross.TurntablePeekAll(guid)
	if ec != pb.ErrCode_OK {
		log.Warn("turntable_award_user", zap.Uint64("guid", guid))
		return 0, 0, pb.ErrCode_FAILED
	}
	total := utils.Sum(0, blocked...)

	newBalance, err := mod.AddUserBalance(guid, total)
	if nil != err {
		log.Error("turntable_award_user", zap.Uint64("guid", guid), zap.Error(err))
		return 0, 0, pb.ErrCode_FAILED
	}
	return total, newBalance, pb.ErrCode_OK
}

func do_C_RedEnvelope(ses nnet.ISession, req *pb.Request){
	r := &pb.S_RedEnvelope{}

	acc_str,err := mod.GetUserAttr(ses.Id(),"account")
	if err != nil{
		log.Debug("do_C_RedEnvelope GetUserAttr err",zap.Uint64("guid",ses.Id()),zap.Error(err))
		r.Ec = -1
		return
	}
	if !gameapi.IsPhoneNumber(acc_str){
		log.Debug("the user not bind phonenumber",zap.Uint64("guid",ses.Id()),zap.String("account",acc_str))
		r.Ec = 2
		return
	}

	type redEnvelope struct {
		Num int `json:"num"`
		Money int64 `json:"money"`
		RedType int `json:"redtype"`
		Exp int `json:"exp"`
		Guids []string `json:"guids"`
	}

	str, err := mod.GetRedEnvelope()
	if err != nil{
		log.Debug("GetRedEnvelope err",zap.Error(err))
		r.Ec = -1
		SendToClient(ses, req.Guid, r)
		return
	}
	if len(str) == 0 {
		log.Debug("红包活动已结束",zap.Uint64("guid",ses.Id()))
		r.Ec = 1
		SendToClient(ses, req.Guid, r)
		return
	}

	var rl redEnvelope
	if err := json.Unmarshal([]byte(str), &rl);err != nil{
		log.Debug("do_C_RedEnvelope Unmarshal err",zap.Error(err))
		r.Ec = -1
		SendToClient(ses, req.Guid, r)
		return
	}

	//!!!!!!发送指定红包
	if len(rl.Guids) > 0 {
		sess := Server.GetAllSessions()
		for _, guid := range  rl.Guids{
			for id, ses := range sess  {
				if strconv.FormatUint(id,10) == guid {
					newBalance, err := mod.AddUserBalance(id, rl.Money)
					if nil != err {
						log.Error("抢红包加钱 err", zap.Error(err), zap.Uint64("guid", id))
						r.Ec = -1
						SendToClient(ses, id, r)
						break
					}
					Ev.OnRedEnvelope(id, rl.Money, newBalance - rl.Money, int32(pb.ReasonBalanceChange_REDPACKETS), ses)
					return
				}
			}
		}
		return
	}


	//!!!!!!群发红包
	//拼手气
	//红包金额=（当前红包剩余金额/当前红包剩余个数/1.5 ~ 当前红包剩余金额/当前红包剩余个数*1.5）中随机，取2位小数
	// 红包金额=当前红包剩余金额
	//均分:
	//抢到红包的人点开红包金额=红包总金额/红包个数

	var reward int64 = 0
	var rate   = 1.5
	if rl.Num != 1 {
		if rl.RedType == 1{ //拼手气
			min  := int64(math.Floor((float64(rl.Money) / float64(rl.Num)) / rate))
			max  := int64(math.Floor((float64(rl.Money) / float64(rl.Num)) * rate))
			rand.Seed(time.Now().Unix())
			reward = rand.Int63n(max - min) + min
		}else{//平分
			reward = rl.Money / int64(rl.Num)
		}
	}else{
		reward =  rl.Money
	}
	r.Reward = reward

	//log.Debug("抢红包金额",zap.Uint64("guid",ses.Id()), zap.Int64("reward",reward), zap.Int("type", rl.RedType))

	newBalance, err := mod.AddUserBalance(req.Guid, r.Reward)
	if nil != err {
		log.Error("抢红包加钱 err", zap.Error(err), zap.Uint64("guid", req.Guid))
		r.Ec = -1
		SendToClient(ses, req.Guid, r)
		return
	}

	//更新红包记录
	rl.Money -= r.Reward
	rl.Num -= 1
	json_,err := json.Marshal(rl)
	if err != nil{
		log.Debug("do_C_RedEnvelope err",zap.Error(err))
	}
	log.Debug("过期时间",zap.Int("exp",rl.Exp))
	exp_t,_ := time.ParseDuration(strconv.Itoa(rl.Exp)) //多少秒后过期

	if err := mod.UpdateRedEnvelope(json_, exp_t);err != nil{
		log.Debug("do_C_RedEnvelope UpdateRedEnvelope err", zap.Error(err))
	}

	log.Debug("抢红包金额",zap.Uint64("guid",ses.Id()), zap.Int64("reward", reward), zap.Int("type", rl.RedType),
		zap.Int64("left money", rl.Money),zap.Int("left num", rl.Num))
	if rl.Num == 0 {
		if err := mod.RemRedEnvelop();err != nil{
			log.Debug("红包已发放完成 删除任务err",zap.Error(err))
		}
		log.Debug("红包已发放完成 success")
	}

	Ev.OnRedEnvelope(req.Guid, r.Reward, newBalance - r.Reward, int32(pb.ReasonBalanceChange_REDPACKETS), ses)
}