package ingame

import (
	"encoding/json"
	"f5"
	"main/constant"
	"main/model"
	"main/service"
	"q5"
	"strings"

	"github.com/gin-gonic/gin"
)

type InGameApi struct {
}

func (iga *InGameApi) PreOrder(c *gin.Context) {
	reqJson := struct {
		AccountId string `json:"account_id" binding:"required"`
		SessionId string `json:"session_id" binding:"required"`
		GoodsId   int32  `json:"goods_id" binding:"required"`
	}{}
	if err := c.ShouldBindJSON(&reqJson); err != nil {
		f5.RspErr(c, 401, "params error")
		return
	}

	data, ret := iga.checkSessionData(c, reqJson.AccountId)
	if !ret {
		return
	}

	nowTime := int32(f5.GetApp().GetRealSeconds())
	order := new(model.InAppOrder)
	order.AccountId = reqJson.AccountId
	order.OrderId = q5.ToString(f5.GetApp().NewLockNodeUuid())
	order.ItemId = reqJson.GoodsId
	order.GameId = q5.SafeToInt32(data["gameid"])
	order.IP = iga.getIP(c)

	order.CreateTime = nowTime
	order.ModifyTime = nowTime

	if err := order.Create(); err != nil {
		f5.RspErr(c, 500, "server internal error")
		return
	}

	rspObj := struct {
		ErrorCode int32  `json:"errcode"`
		ErrMsg    string `json:"errmsg"`
		OrderId   string `json:"order_id"`
	}{
		0,
		"",
		order.OrderId,
	}

	c.JSON(200, rspObj)
}

func (iga *InGameApi) ServerPreOrder(c *gin.Context) {
	accountId := c.Query("account_id")
	goodsid := c.Query("goods_id")
	sessionid := c.Query("session_id")
	sign := c.Query("sign")
	if strings.EqualFold(q5.Md5Str(accountId+goodsid+constant.GLOBAL_SALT+sessionid), strings.ToLower(sign)) {
		f5.RspErr(c, 401, "sign err")
		return
	}

	data, ret := iga.checkSessionData(c, accountId)
	if !ret {
		return
	}

	nowTime := int32(f5.GetApp().GetRealSeconds())
	order := new(model.InAppOrder)
	order.AccountId = accountId
	order.OrderId = q5.ToString(f5.GetApp().NewLockNodeUuid())
	order.ItemId = q5.SafeToInt32(goodsid)
	order.GameId = q5.SafeToInt32(data["gameid"])
	order.IP = c.Query("user_ip")

	order.CreateTime = nowTime
	order.ModifyTime = nowTime

	if err := order.Create(); err != nil {
		f5.RspErr(c, 500, "server internal error")
		return
	}

	rspObj := struct {
		ErrorCode int32  `json:"errcode"`
		ErrMsg    string `json:"errmsg"`
		OrderId   string `json:"order_id"`
	}{
		0,
		"",
		order.OrderId,
	}

	c.JSON(200, rspObj)
}

func (iga *InGameApi) PayDone(c *gin.Context) {
	accountId := c.Query("account_id")
	orderid := c.Query("order_id")
	sessionid := c.Query("session_id")
	sign := c.Query("sign")
	if strings.EqualFold(q5.Md5Str(accountId+orderid+constant.GLOBAL_SALT+sessionid), strings.ToLower(sign)) {
		f5.RspErr(c, 401, "sign err")
		return
	}

	orderModel := new(model.InAppOrder)
	if err, found := orderModel.Find(accountId, orderid); err != nil {
		f5.RspErr(c, 500, "server internal error")
		return
	} else if !found {
		f5.RspErr(c, 1, "order not found")
		return
	}

	data, ret := iga.checkSessionData(c, accountId)
	if !ret {
		return
	}

	sessionkey := q5.SafeToString(data["session_key"])	

	status := orderModel.Status
	rspObj := struct {
		ErrorCode int32  `json:"errcode"`
		ErrMsg    string `json:"errmsg"`
		OrderId   string `json:"order_id"`
		Diamond   int64  `json:"diamond"`
	}{
		OrderId: orderid,
	}
	if status < 2 {
		gameid := q5.SafeToInt64(data["gameid"])
		openid := q5.SafeToString(data["openid"])
		userip := iga.getIP(c)
		balance, errcode, err := int64(0), int32(0), error(nil)
		if status == 0 {
			balance, errcode, err = service.Wxpay.QueryBalance(openid, gameid, userip, sessionkey)
			if err != nil {
				f5.RspErr(c, 500, "system busy")
				return
			}
		} else {
			balance = int64(orderModel.SpAmount)
		}

		if errcode == constant.WX_ERRCODE_OK && balance > 0 {
			errcode = service.Wxpay.QueryPay(openid, gameid, userip, sessionkey, int32(balance), orderModel.OrderId)
			if errcode == constant.WX_ERRCODE_OK {
				status = 1
				orderModel.Status = 2
				orderModel.UpdateFields([]string{"status"})

				rspObj.Diamond = balance
			}
		}

		if errcode == constant.WX_ERRCODE_SESSIONERR || errcode == constant.WX_ERRCODE_SIGERR {
			rspObj.ErrorCode = 402
			rspObj.ErrMsg = "session overtime"
		}
	}

	c.JSON(200, rspObj)
}

func (iga *InGameApi) ServerPrePurchase(c *gin.Context) {
	req := struct {
		AccountId string `binding:"required" json:"account_id"`
		SessionId string `binding:"required" json:"session_id"`
		UserIp    string `binding:"required" json:"user_ip"`
		Sign      string `binding:"required" json:"sign"`
		SigData   string `binding:"required" json:"sig_data"`
	}{}

	if err := c.ShouldBindJSON(&req); err != nil {
		f5.RspErr(c, 401, "post content error")
		return
	}

	if !strings.EqualFold(q5.Md5Str(req.AccountId+constant.GLOBAL_SALT+req.SessionId), req.Sign) {
		f5.RspErr(c, 401, "sign err")
		return
	}

	sigdata := map[string]interface{}{}
	if json.Unmarshal([]byte(req.SigData), &sigdata) != nil {
		f5.RspErr(c, 401, "sigdata err")
		return
	}

	goodsid := q5.SafeToInt32(sigdata["productId"])
	if goodsid == 0 {
		f5.RspErr(c, 401, "sigdata productid err")
		return
	}

	data, ret := iga.checkSessionData(c, req.AccountId)
	if !ret {
		return
	}

	sessionkey := q5.SafeToString(data["session_key"])	

	gameid := q5.SafeToInt64(data["gameid"])

	nowTime := int32(f5.GetApp().GetRealSeconds())
	order := new(model.InAppOrder)
	order.AccountId = req.AccountId
	order.OrderId = "p" + q5.ToString(f5.GetApp().NewLockNodeUuid())
	order.GameId = int32(gameid)
	order.ItemId = goodsid
	order.IP = req.UserIp

	order.CreateTime = nowTime
	order.ModifyTime = nowTime

	sigdata["outTradeNo"] = order.OrderId
	sigdatabyte, _ := json.Marshal(sigdata)
	fullsigdata := string(sigdatabyte)

	paysig, signature := service.Wxpay.GetPurchaseSig(gameid, fullsigdata, sessionkey)
	if len(paysig) == 0 || len(signature) == 0 {
		f5.RspErr(c, 403, "account error")
		return
	}

	if err := order.Create(); err != nil {
		f5.RspErr(c, 500, "server internal error")
		return
	}

	rspObj := struct {
		ErrorCode int32  `json:"errcode"`
		ErrMsg    string `json:"errmsg"`
		OrderId   string `json:"order_id"`
		PaySig    string `json:"pay_sig"`
		Signature string `json:"signature"`
		SigData   string `json:"sig_data"`
	}{
		0,
		"",
		order.OrderId,
		paysig,
		signature,
		fullsigdata,
	}

	c.JSON(200, rspObj)
}

func (iga *InGameApi) OrderInfo(c *gin.Context) {
	reqJson := struct {
		AccountId string `json:"account_id" binding:"required"`
		SessionId string `json:"session_id" binding:"required"`
		OrderId   string `json:"order_id" binding:"required"`
	}{}
	if err := c.ShouldBindJSON(&reqJson); err != nil {
		f5.RspErr(c, 401, "params error")
		return
	}

	data, ret := iga.checkSessionData(c, reqJson.AccountId)
	if !ret {
		return
	}

	orderModel := new(model.InAppOrder)
	if err, found := orderModel.Find(reqJson.AccountId, reqJson.OrderId); err != nil {
		f5.RspErr(c, 500, "server internal error")
		return
	} else if !found {
		f5.RspErr(c, 1, "not found")
		return
	}

	rspObj := struct {
		ErrorCode int32  `json:"errcode"`
		ErrMsg    string `json:"errmsg"`
		OrderId   string `json:"order_id"`
		GoodsId   int32  `json:"goods_id"`
		Count     int32  `json:"count"`
		Others    int32  `json:"others"`
		Status    int32  `json:"status"`
	}{
		OrderId: orderModel.OrderId,
		GoodsId: orderModel.ItemId,
		Count:   orderModel.SpAmount,
		Others:  orderModel.SpOthers,
		Status:  orderModel.Status,
	}

	c.JSON(200, rspObj)

	if orderModel.Status == 0 {
		gameid := q5.SafeToInt64(data["gameid"])
		openid := q5.SafeToString(data["openid"])
		userip := iga.getIP(c)
		sessionkey := q5.SafeToString(data["session_key"])
		balance, errcode, err := service.Wxpay.QueryBalance(openid, gameid, userip, sessionkey)
		if err != nil {
			return
		}

		if errcode == constant.WX_ERRCODE_OK {
			count, err := service.Wxpay.GetGoodsCount(gameid, int64(orderModel.ItemId))
			if err != nil {
				return
			}

			if balance >= count {
				errcode = service.Wxpay.QueryPay(openid, gameid, userip, sessionkey, int32(balance), orderModel.OrderId)
				if errcode == constant.WX_ERRCODE_OK {
					orderModel.Status = 1
					orderModel.SpAmount = int32(count)
					orderModel.SpOthers = int32(balance - count)
					orderModel.UpdateFields([]string{"status", "sp_amount", "sp_others"})
				}
			}
		}

		if errcode == constant.WX_ERRCODE_SESSIONERR || errcode == constant.WX_ERRCODE_SIGERR {
			sessionkeytime := q5.SafeToInt64(data["update_time"])
			service.Wxpay.AddExpireInfo(reqJson.AccountId, sessionkeytime)
		}
	} else if orderModel.Status == 1 {
		orderModel.Status = 2
		orderModel.UpdateFields([]string{"status"})
	}
}

func (iga *InGameApi) OtherOrder(c *gin.Context) {
	reqJson := struct {
		AccountId string `json:"account_id" binding:"required"`
		SessionId string `json:"session_id" binding:"required"`
	}{}
	if err := c.ShouldBindJSON(&reqJson); err != nil {
		f5.RspErr(c, 401, "params error")
		return
	}

	data, ret := iga.checkSessionData(c, reqJson.AccountId)
	if !ret {
		return
	}

	sessionkey := q5.SafeToString(data["session_key"])	
	sessionkeytime := q5.SafeToInt64(data["update_time"])
	rspObj := struct {
		ErrorCode int32  `json:"errcode"`
		ErrMsg    string `json:"errmsg"`
		Count     int64  `json:"count"`
	}{}

	gameid := q5.SafeToInt64(data["gameid"])
	openid := q5.SafeToString(data["openid"])
	userip := iga.getIP(c)
	balance, errcode, err := service.Wxpay.QueryBalance(openid, gameid, userip, sessionkey)
	if err != nil {
		f5.RspErr(c, 500, "wx server busy")
		return
	}

	if errcode == constant.WX_ERRCODE_OK && balance > 0 {
		nowTime := int32(f5.GetApp().GetRealSeconds())
		order := new(model.InAppOrder)
		order.AccountId = reqJson.AccountId
		order.OrderId = "o" + q5.ToString(f5.GetApp().NewLockNodeUuid())

		errcode = service.Wxpay.QueryPay(openid, gameid, userip, sessionkey, int32(balance), order.OrderId)
		if errcode == constant.WX_ERRCODE_OK {
			order.GameId = int32(gameid)			
			order.Channel = q5.SafeToInt32(data["channel"])
			order.IP = iga.getIP(c)
			order.Status = 2
			order.SpOthers = int32(balance)
			order.CreateTime = nowTime
			order.ModifyTime = nowTime

			if err := order.Create(); err != nil {
				f5.GetSysLog().Error("record other order error:%s, %s, %d", reqJson.AccountId, order.OrderId, balance)
			}
			rspObj.Count = balance
		}
	} else if errcode == constant.WX_ERRCODE_SESSIONERR || errcode == constant.WX_ERRCODE_SIGERR {
		service.Wxpay.AddExpireInfo(reqJson.AccountId, sessionkeytime)
		rspObj.ErrorCode = 402
		rspObj.ErrMsg = "invalid session 2"
	}

	c.JSON(200, rspObj)
}

func (iga *InGameApi) QueryPay(c *gin.Context) {
	reqJson := struct {
		AccountId string `json:"account_id" binding:"required"`
		SessionId string `json:"session_id" binding:"required"`
		OrderId   string `json:"order_id" binding:"required"`
	}{}
	if err := c.ShouldBindJSON(&reqJson); err != nil {
		f5.RspErr(c, 401, "params error")
		return
	}

	_, ret := iga.checkSessionData(c, reqJson.AccountId)
	if !ret {
		return
	}

	orderModel := new(model.InAppOrder)
	if err, found := orderModel.Find(reqJson.AccountId, reqJson.OrderId); err != nil {
		f5.RspErr(c, 500, "server internal error")
		return
	} else if !found {
		f5.RspErr(c, 1, "not found")
		return
	}

	rspObj := struct {
		ErrorCode int32  `json:"errcode"`
		ErrMsg    string `json:"errmsg"`
		OrderId   string `json:"order_id"`
		GoodsId   int32  `json:"goods_id"`
		Count     int32  `json:"count"`
		Others    int32  `json:"others"`
		Status    int32  `json:"status"`
	}{
		OrderId: orderModel.OrderId,
		GoodsId: orderModel.ItemId,
		Count:   orderModel.SpAmount,
		Others:  orderModel.SpOthers,
		Status:  orderModel.Status,
	}

	c.JSON(200, rspObj)

	if orderModel.Status == 1 {
		orderModel.Status = 2
		orderModel.UpdateFields([]string{"status"})
	}
}

func (iga *InGameApi) checkSessionData(c *gin.Context, accountId string) (data map[string]interface{}, ret bool){
	strs := strings.Split(accountId, "_")
	if len(strs) < 3 {
		f5.RspErr(c, 401, "params error1")
		return
	}

	rediskey := "ls:accountid:" + accountId
	str, err := service.Redis.Get(constant.LOGIN_REDIS, rediskey)
	if err != nil {
		f5.RspErr(c, 402, "invalid session")
		return
	}

	data = map[string]interface{}{}
	if json.Unmarshal([]byte(str), &data) != nil {
		f5.RspErr(c, 402, "invalid session 1")
		return
	}

	sessionkey := q5.SafeToString(data["session_key"])
	if len(sessionkey) == 0 {
		f5.GetSysLog().Debug("empty sessionkey:%s", accountId)
		f5.RspErr(c, 402, "invalid session 2")
		return
	}

	sessionkeytime := q5.SafeToInt64(data["update_time"])
	if service.Wxpay.CheckExpireCache(accountId, sessionkeytime) {
		f5.RspErr(c, 402, "session expired")
		return
	}

	data["channel"] = strs[0]
	data["gameid"] = strs[1]
	return data, true
}

func (iga *InGameApi) getIP(c *gin.Context) (ip string) {
	ip = c.Request.Header.Get("X-Real-Ip")
	if ip == "" {
		ip = strings.Split(c.Request.Header.Get("X-Forwarded-For"), ",")[0]
	}
	if ip == "" {
		ip = c.Request.RemoteAddr
	}

	return ip
}
