package main

import (
	"../log"
	"../mod"
	"../nnet"
	pb "../protos"
	"../utils"
	"github.com/coheneye/go-simplejson"
	"github.com/gogo/protobuf/proto"
	"go.uber.org/zap"
)

// 修改账号资料
func do_C_UserSettings(ses nnet.ISession, req *pb.Request) {
	r := &pb.S_UserSettings{Ec: int32(pb.ErrCode_OK)} // response
	kv := make(map[string]interface{})                //key-value
	p := &pb.C_UserSettings{}                         //param

	err := proto.Unmarshal(req.Req, p)
	if err != nil {
		r.Ec = int32(pb.ErrCode_BAD_REQUEST)
		SendToClient(ses, req.Guid, r)
		return
	}
	// 昵称不能为空
	if len(p.Nickname) <= 0 {
		r.Ec = int32(pb.ErrCode_LENGTH)
		SendToClient(ses, req.Guid, r)
		return
	}

	// save
	kv["nickname"] = p.Nickname
	kv["icon"] = p.Icon
	kv["qq"] = p.Qq
	kv["wechat"] = p.Wechat
	kv["email"] = p.Email
	kv["birthday"] = p.Birthday
	err = mod.UpdateUserinfoToRedis(req.Guid, kv)
	if nil != err {
		r.Ec = int32(pb.ErrCode_UNKNOWN)
		SendToClient(ses, req.Guid, r)
		return
	}
	//
	r.Wechat = p.Wechat
	r.Qq = p.Qq
	r.Icon = p.Icon
	r.Nickname = p.Nickname
	r.Email = p.Email
	r.Birthday = p.Birthday
	SendToClient(ses, req.Guid, r)

	Ev.OnUserUpdateSettings(req.Guid, p)
}

// 修改密码
func do_C_UpdatePassword(ses nnet.ISession, req *pb.Request) {
	r := &pb.S_UpdatePassword{Ec: int32(pb.ErrCode_OK)}
	p := &pb.C_UpdatePassword{}
	err := proto.Unmarshal(req.Req, p)
	if nil != err {
		r.Ec = int32(pb.ErrCode_BAD_REQUEST)
		SendToClient(ses, req.Guid, r)
		return
	}

	pwd := mod.GetUserPassword(req.Guid)
	if pwd != p.OldPassword {
		r.Ec = int32(pb.ErrCode_AUTH)
		SendToClient(ses, req.Guid, r)
		return
	}
	err = mod.UpdateUserPassword(req.Guid, p.Password)
	if nil != err {
		r.Ec = int32(pb.ErrCode_UNKNOWN)
		SendToClient(ses, req.Guid, r)
		return
	}

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

// 修改银行金额
func do_C_UpdateBankBalance(ses nnet.ISession, req *pb.Request) {
	r := &pb.S_UpdateBankBalance{Ec: int32(pb.ErrCode_OK)}
	p := &pb.C_UpdateBankBalance{}

	err := proto.Unmarshal(req.Req, p)
	if nil != err || p.Delta == 0 {
		log.Error("do_C_UpdateBankBalance", zap.Uint64("guid", req.Guid),
			zap.Error(err), zap.Int64("delta", p.Delta))
		r.Ec = int32(pb.ErrCode_BAD_REQUEST)
		SendToClient(ses, req.Guid, r)
		return
	}

	vals, err := mod.GetUserAttrs(req.Guid, "balance", "bank", "bankPass")
	if nil != err {
		log.Error("do_C_UpdateBankBalance", zap.Uint64("guid", req.Guid),
			zap.Error(err))
		r.Ec = int32(pb.ErrCode_UNKNOWN)
		SendToClient(ses, req.Guid, r)
		return
	}

	user_balance := utils.If_to_int64(vals[0], 0)
	user_bank := utils.If_to_int64(vals[1], 0)
	bank_pass := utils.If_to_string(vals[2], "")
	var newBalance int64 = 0
	var newBank int64 = 0

	if p.BankPass != bank_pass {
		log.Warn("do_C_UpdateBankBalance password error", zap.Uint64("guid", req.Guid),
			zap.String("bank_pass", bank_pass), zap.String("p.BankPass", p.BankPass))
		r.Ec = int32(pb.ErrCode_AUTH)
		SendToClient(ses, req.Guid, r)
		return
	}

	if p.Delta > 0 { // 存钱
		if p.Delta > user_balance {
			log.Warn("do_C_UpdateBankBalance", zap.Uint64("guid", req.Guid),
				zap.Int64("p.Delta", p.Delta), zap.Int64("balance", user_balance))
			r.Ec = int32(pb.ErrCode_BALANCE_INSUFFICIENT)
			SendToClient(ses, req.Guid, r)
			return
		}
	} else { // 取钱
		if -p.Delta > user_bank {
			log.Warn("do_C_UpdateBankBalance", zap.Uint64("guid", req.Guid),
				zap.Int64("p.Delta", p.Delta), zap.Int64("user_bank", user_bank))
			r.Ec = int32(pb.ErrCode_BANK_INSUFFICIENT)
			SendToClient(ses, req.Guid, r)
			return
		}
	}
	newBalance = user_balance - p.Delta
	newBank = user_bank + p.Delta

	err = mod.UpdateUserinfoToRedis(req.Guid, map[string]interface{}{
		"balance": newBalance,
		"bank":    newBank,
	})
	if nil != err {
		log.Error("do_C_UpdateBankBalance", zap.Uint64("guid", req.Guid),
			zap.Error(err))
		r.Ec = int32(pb.ErrCode_UNKNOWN)
		SendToClient(ses, req.Guid, r)
		return
	}

	r.Balance = newBalance
	r.Bank = newBank
	r.Delta = p.Delta
	r.Ec = int32(pb.ErrCode_OK)
	SendToClient(ses, req.Guid, r)

	log.Info("bank", zap.Int64("balance", user_balance), zap.Int64("bank", user_bank),
		zap.Int64("delta", p.Delta), zap.Int64("new_balance", newBalance), zap.Int64("new_bank", newBank))

	Ev.OnUserUpdateBank(req.Guid, p.Delta, r.Balance, r.Bank, ses)
}

// 修改银行密码
func do_C_UpdateBankPassword(ses nnet.ISession, req *pb.Request) {
	r := &pb.S_UpdateBankPassword{Ec: int32(pb.ErrCode_OK)}
	p := &pb.C_UpdateBankPassword{}

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

	arr, err := mod.GetUserAttrs(req.Guid, "bankPass", "password")
	if nil != err {
		log.Error("do_C_UpdateBankPassword", zap.Uint64("guid", req.Guid), zap.Error(err))
		r.Ec = int32(pb.ErrCode_FAILED)
		SendToClient(ses, req.Guid, r)
		return
	}

	if len(p.NewPassword) != 0 && len(p.NewPassword) != 4 {
		log.Warn("do_C_UpdateBankPassword", zap.Uint64("guid", req.Guid), zap.String("NewPassword", p.NewPassword))
		r.Ec = int32(pb.ErrCode_LENGTH)
		SendToClient(ses, req.Guid, r)
		return
	}

	nowBankPass := utils.If_to_string(arr[0], "")
	nowPassword := utils.If_to_string(arr[1], "")

	if len(nowBankPass) > 0 {
		if p.LoginPassword != nowPassword {
			log.Warn("do_C_UpdateBankPassword login password is not matched.", zap.Uint64("guid", req.Guid),
				zap.String("p.Password", p.LoginPassword), zap.String("password", nowPassword))
			r.Ec = int32(pb.ErrCode_AUTH)
			SendToClient(ses, req.Guid, r)
			return
		}
	}

	// 设置
	err = mod.UpdateUserAttr(req.Guid, "bankPass", p.NewPassword)
	if err != nil {
		log.Error("do_C_UpdateBankPassword", zap.Uint64("guid", req.Guid), zap.Error(err))
		r.Ec = int32(pb.ErrCode_FAILED)
		SendToClient(ses, req.Guid, r)
		return
	}

	r.BankPass = p.NewPassword

	SendToClient(ses, req.Guid, r)

	Ev.OnUserUpdateBankPassword(req.Guid, p)
}

// 请求余额
func do_C_UserBalance(ses nnet.ISession, req *pb.Request) {
	r := &pb.S_Ntf_UserBalance{}

	rs, err := mod.GetUserAttrs(req.Guid, "balance", "bank")
	if nil != err {
		r.Ec = int32(pb.ErrCode_UNKNOWN)
		SendToClient(ses, req.Guid, r)
		return
	}

	r.Guid = req.Guid
	r.Balance = utils.If_to_int64(rs[0], 0)

	SendToClient(ses, req.Guid, r)
}

//
func do_C_BindAlipay(ses nnet.ISession, req *pb.Request) {
	r := &pb.S_BindAlipay{}
	p := &pb.C_BindAlipay{}

	err := proto.Unmarshal(req.Req, p)
	if nil != err {
		r.Ec = int32(pb.ErrCode_UNKNOWN)
		SendToClient(ses, req.Guid, r)
		return
	}
	if len(p.Alipay) <= 0 || len(p.Name) <= 0 {
		log.Warn("do_C_BindAlipay empty input", zap.String("alipay", p.Alipay), zap.String("name", p.Name))
		r.Ec = int32(pb.ErrCode_BAD_REQUEST)
		SendToClient(ses, req.Guid, r)
		return
	}

	//// 獲取總提現和支付寶賬號
	arr, err := mod.GetUserAttrs(req.Guid, "alipay", "withdrawed")
	if nil != err {
		log.Error("do_C_BindAlipay", zap.Error(err), zap.String("alipay", p.Alipay), zap.String("name", p.Name))
		r.Ec = int32(pb.ErrCode_UNKNOWN)
		SendToClient(ses, req.Guid, r)
		return
	}
	cur_alipay := utils.If_to_string(arr[0], "")
	cur_withdrawed := utils.If_to_int64(arr[1], 0)

	// 已經綁定並且提過現則不予重新綁定
	if cur_withdrawed > 0 && len(cur_alipay) > 0 {
		log.Warn("do_C_BindAlipay withdrawed>0", zap.Uint64("guid", req.Guid), zap.String("alipay", p.Alipay), zap.String("name", p.Name))
		r.Ec = int32(pb.ErrCode_DUP_OPER)
		SendToClient(ses, req.Guid, r)
		return
	}

	// if bind before
	if mod.IfAlipayInUse(p.Alipay) {
		log.Warn("do_C_BindAlipay alipay in use", zap.String("alipay", p.Alipay), zap.String("name", p.Name))
		r.Ec = int32(pb.ErrCode_OCCUPIED)
		SendToClient(ses, req.Guid, r)
		return
	}
	// update
	//TODO: transaction
	err = mod.UpdateUserinfoToRedis(req.Guid, utils.H{
		"alipay": p.Alipay,
		"name":   p.Name,
	})
	if nil != err {
		log.Error("do_C_BindAlipay", zap.Error(err), zap.String("alipay", p.Alipay), zap.String("name", p.Name))
		r.Ec = int32(pb.ErrCode_FAILED)
		SendToClient(ses, req.Guid, r)
		return
	}
	// record
	err = mod.UpdateAlipayGuid(r.Alipay, req.Guid)
	if err != nil {
		log.Error("do_C_BindAlipay", zap.Error(err), zap.String("alipay", p.Alipay), zap.String("name", p.Name))
		r.Ec = int32(pb.ErrCode_FAILED)
		SendToClient(ses, req.Guid, r)
		return
	}

	r.Alipay = p.Alipay
	r.Name = p.Name
	SendToClient(ses, req.Guid, r)

	Ev.OnUserBindAlipay(req.Guid, p)
}

// 私人郵件，這個 redis 只保存 param 部分，弄煩了
func do_C_PrivateMail(ses nnet.ISession, req *pb.Request) {
	p := &pb.C_PrivateMail{} // params
	r := &pb.S_PrivateMail{} // response

	err := proto.Unmarshal(req.Req, p)
	if err != nil {
		r.Ec = int32(pb.ErrCode_UNKNOWN)
		SendToClient(ses, req.Guid, r)
		return
	}

	js_mails, err := mod.GetPrivateMails(req.Guid)
	if err != nil {
		r.Ec = int32(pb.ErrCode_UNKNOWN)
		SendToClient(ses, req.Guid, r)
		return
	}
	for _, js_mail := range js_mails {
		js, err := simplejson.NewJson([]byte(js_mail))
		if nil != err {
			continue
		}
		m := &pb.Mail{
			Id:      int32(js.Get("id").MustInt64()),
			Title:   js.Get("title").MustString(),
			Content: js.Get("content").MustString(),
			Time:    js.Get("created_at").MustInt64(),
		}
		r.Mails = append(r.Mails, m)
	}

	SendToClient(ses, req.Guid, r)

	Ev.OnUserPrivateMail(req.Guid, r)
}

func do_C_RemPrivateMail(ses nnet.ISession, req *pb.Request) {
	p := &pb.C_RemPrivateMail{}
	r := &pb.S_RemPrivateMail{}

	err := proto.Unmarshal(req.Req, p)
	if err != nil {
		r.Ec = int32(pb.ErrCode_UNKNOWN)
		SendToClient(ses, req.Guid, r)
		return
	}

	if len(p.Ids) <= 0 {
		r.Ec = int32(pb.ErrCode_BAD_REQUEST)
		SendToClient(ses, req.Guid, r)
		return
	}

	// TODO: 優化
	for _, id := range p.Ids {
		_ = mod.RemovePrivateMailById(req.Guid, int64(id))
	}

	SendToClient(ses, req.Guid, r)

	Ev.OnUserRemovePrivateMail(req.Guid, p)
}

// 登录银行
func do_C_AuthBank(ses nnet.ISession, req *pb.Request) {

}
