package gameapi

import (
	"../log"
	"../mod"
	pb "../protos"
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/tls"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/shopspring/decimal"
	"go.uber.org/zap"
	"io/ioutil"
	"math"
	"net/http"
	"strconv"
	"strings"
	"time"
)

var Qp_url 			  = "http://api.jygrq.com/apiRequest.do"
var Qp_dbdzAgent 	  = "kgonrmbag"
var Qp_DC 			  = "KGON"
var Qp_aesIvDefValue  = "3f89b67bdd864e78"
var Qp_aesKeyDefValue = "67589cc8708fe6b0"


//踢出玩家 是否能加速 下分过程?
func DBDZ_QP_KICK_USER(guid string) bool{
	var dr dbqp_response
	register := dbqp_kick_off{dbdzqp_kick,time.Now().UnixNano()/1e6,Qp_dbdzAgent,guid}
	if dBDZ_QP_do_request(register, &dr){
		log.Debug("DBDZ_QP_KICK_USER kick success", zap.Any("data", dr))
		return true
	}else{
		log.Debug("DBDZ_QP_KICK_USER kick failed", zap.Any("data", dr))
		return false
	}
}

func DBDZ_QP_Enter_game(guid string, gameParam []string, cash int64, isReconnect bool, res *pb.S_EnterGame) {

	if len(gameParam) != 2 {
		res.Ec = SYS_GAME_PARAM_ERROR
		return
	}

	cash_ := convertBalanceToDbdzQp(cash) //1:100
	//1.注册
	var dr dbqp_response
	register := dbqp_registerUser{dbdzqp_register,time.Now().UnixNano()/1e6,Qp_dbdzAgent,guid,guid}
	if dBDZ_QP_do_request(register, &dr){
		login := dbqp_getToken{dbdzqp_get_token,time.Now().UnixNano()/1e6,guid,"ch",gameParam[0],gameParam[1],"2",false,0}
		//2.登陆
		if dBDZ_QP_do_request(login, &dr){
			if isReconnect {
				res.Ec = SYS_LOGIN_FAILED
				res.GameParam = gameParam
				res.Url = dr.Path
				log.Debug("DBDZ_QP_Enter_game reconnected success", zap.Any("data", dr))
			}else{
				//3.存款
				orderid := GetFormatDate()+guid
				deposit := dbqp_aboutMoney{dbdzqp_deposit_withdraw,time.Now().UnixNano()/1e6,Qp_dbdzAgent,guid,orderid,"0",cash_}
				if dBDZ_QP_do_request(deposit, &dr){
					res.Ec = SUCCESS
					res.GameParam = gameParam
					res.Url = dr.Path
				}else {
					res.Ec = SYS_DEPOSIT_FAILED
					log.Debug("DBDZ_QP_Enter_game deposit failed", zap.Any("data",dr))
				}
			}
		}else {
			res.Ec = SYS_LOGIN_FAILED
			log.Debug("DBDZ_QP_Enter_game login failed", zap.Any("data",dr))
		}
	}else {
		res.Ec = SYS_REGIST_FAILED
		log.Debug("DBDZ_QP_Enter_game register failed", zap.Any("data",dr))
	}
}

func DBDZ_QP_Leave_game(guid string, p_b *pb.S_Ntf_UserBalance, p_r *pb.S_LeaveGame) {
	wd_money ,ok:= DBDZ_QP_withdraw(guid)
	if ok {
		p_r.Ec = SUCCESS
		p_b.Balance = wd_money
	}else {
		p_r.Ec = SYS_WITHDRAW_FAILED
	}
}

//取款
func DBDZ_QP_withdraw(guid string) (balance int64, b bool){
	var dr dbqp_response
	orderid := GetFormatDate()+guid
	deposit := dbqp_aboutMoney{dbdzqp_deposit_withdraw,time.Now().UnixNano()/1e6,Qp_dbdzAgent,guid,orderid,"1",0}
	if dBDZ_QP_do_request(deposit, &dr){
		backMoney := convertBalanceTo(math.Abs(dr.Amount))
		log.Debug("DBDZ_QP 下分成功", zap.String("guid",guid) ,zap.Int64("balance", backMoney))
		return backMoney,true
	}else {
		log.Debug("DBDZ_QP 下分失败", zap.Any("data",dr))
		return 0,false
	}
}

func dBDZ_QP_do_request(param interface{}, res *dbqp_response) bool {

	log.Debug("DBDZ_QP 传入参数", zap.Any("data", param))
	bytesData, err := json.Marshal(param)
	if err != nil {
		log.Debug("DBDZ_QP Marshal err", zap.Error(err))
		return false
	}

	encbyte, err := dbqp_aesEncrypt(bytesData, dbqp_checkAesKey(Qp_aesKeyDefValue))
	if err != nil {
		log.Debug("DBDZ_QP aesEncrypt err", zap.Error(err))
		return false
	}

	encryptString := strings.Replace(strings.Replace(base64.RawStdEncoding.EncodeToString(encbyte), "+", "-", -1), "/", "_", -1)
	log.Debug("DBDZ_QP 请求", zap.Any("encryptString", encryptString))

	payload := strings.NewReader("dc=" + Qp_DC + "&x=" + encryptString)
	req, err := http.NewRequest("POST", Qp_url, payload)
	if err != nil {
		log.Debug("DBDZ_QP NewRequest err", zap.Error(err))
		return false
	}

	tr := &http.Transport{
		TLSClientConfig: &tls.Config{
			InsecureSkipVerify: true,
		},
	}

	client := &http.Client{Transport: tr}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Set("cache-control", "no-cache")
	resp, err := client.Do(req)
	if err != nil {
		log.Debug("DBDZ_QP client.Do err", zap.Error(err))
		return false
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Debug("DBDZ_QP ioutil.ReadAll err", zap.String("data", fmt.Sprintf("%s", body)))
		return false
	}

	if err := json.Unmarshal(body, res); err != nil {
		log.Debug("DBDZ_QP Unmarshal", zap.Error(err))
		return false
	}
	log.Debug("DBDZ_QP 响应", zap.Any("data", *res))
	if res.Status == DBQP_SUCCESS || res.Status == DBQP_USER_EXISTED || res.Status == DBQP_USE_LOSE_ALL{

		if res.Status == DBQP_USE_LOSE_ALL{ //玩家金币输完了
			res.Amount = 0
		}
		return true
	} else {
		return false
	}

}

//强制关端  下分、发送离开游戏事件
func Dbdzqp_Cronjob(guid uint64, gameId int64) bool {

	s_guid := strconv.FormatUint(guid,10)
	var dr dbqp_response
	orderid := GetFormatDate() + s_guid
	deposit := dbqp_aboutMoney{dbdzqp_deposit_withdraw,time.Now().UnixNano()/1e6,Qp_dbdzAgent,s_guid,orderid,"1",0}
	if dBDZ_QP_do_request(deposit, &dr){
		log.Debug("dbdzqp_Cronjob withdraw success ",zap.Float64("balance", dr.Amount))
	}else {
		log.Debug("dbdzqp_Cronjob withdraw  failed", zap.Any("data",dr))
		return false
	}

	balance := ConvertBalanceToPlatform(-dr.Amount)
	//3.给用户加金币
	log.Debug("dbdzqp_Cronjob redis update balance", zap.Int64("balance", balance))
	new_balance, err := mod.AddUserBalance(guid, balance)
	log.Debug("dbdzqp_Cronjob new balance", zap.Int64("new_balance", new_balance))
	if err != nil {
		log.Error("dbdzqp_Cronjob falild", zap.Error(err), zap.Uint64("guid", guid), zap.Int64("balance", balance))
	}
	if new_balance >= 0 {
		//4, 断开游戏连接3分钟以后就不走断线重连,下分成功直接进大厅
		if err := mod.UpdateUserAttr(guid, "regameinfo", ""); err != nil {
			log.Error("dbdzqp_Cronjob clean regameinfo err,please check", zap.Uint64("guid", guid))
		}
	}
	//5.通知后台更新玩家金币变更
	Nats_Ntf_Balance(guid, gameId, strconv.FormatInt(new_balance, 10), false)
	log.Debug("CQ9_Cronjob withdraw success", zap.Uint64("guid", guid))
	return true
}


func dbqp_checkAesKey(strKey string) []byte {
	keyLen := len(strKey)
	arrKey := []byte(strKey)
	if keyLen >= 32 {
		return arrKey[:32]
	}
	if keyLen >= 24 {
		return arrKey[:24]
	}
	if keyLen >= 16 {
		return arrKey[:16]
	}
	tmp := make([]byte, 16)
	for i := 0; i < 16; i++ {
		if i < keyLen {
			tmp[i] = arrKey[i]
		} else {
			tmp[i] = '0'
		}
	}
	return tmp
}

func dbqp_aesEncrypt(plaintext []byte, key []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		fmt.Println("err=", err)
		return nil, errors.New("invalid decrypt key")
	}
	blockSize := block.BlockSize()
	plaintext = dbqp_pKCS5Padding(plaintext, blockSize)
	iv := []byte(Qp_aesIvDefValue)
	blockMode := cipher.NewCBCEncrypter(block, iv)
	ciphertext := make([]byte, len(plaintext))
	blockMode.CryptBlocks(ciphertext, plaintext)
	return ciphertext, nil
}

func dbqp_pKCS5Padding(src []byte, blockSize int) []byte {
	padding := blockSize - len(src)%blockSize
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(src, padtext...)
}

func dbqp_aesDecrypt(ciphertext []byte, key []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, errors.New("invalid decrypt key")
	}
	blockSize := block.BlockSize()
	if len(ciphertext) < blockSize {
		return nil, errors.New("ciphertext too short")
	}
	iv := []byte(AesIvDefValue)
	if len(ciphertext)%blockSize != 0 {
		return nil, errors.New("ciphertext is not a multiple of the block size")
	}
	blockModel := cipher.NewCBCDecrypter(block, iv)
	plaintext := make([]byte, len(ciphertext))
	blockModel.CryptBlocks(plaintext, ciphertext)
	return plaintext, nil
}

//金币换算 平台->游戏
func convertBalanceToDbdzQp(balance int64) float64 {
	return float64(balance) / 100
}

//金币换算 游戏->平台
func convertBalanceTo(balance float64) int64 {
	return decimal.NewFromFloat(balance).Mul(decimal.NewFromFloat(100)).IntPart()
}

//查询余额
func dbqp_queryBalance(guid string)(ok bool,balance int64){
	var dr dbqp_response
	qBalance := dbqp_queryBalance_req{dbdzqp_query_balance,time.Now().UnixNano()/1e6,Qp_dbdzAgent,guid}
	if dBDZ_QP_do_request(qBalance, &dr){
		return false,0
	}else{
		return true,convertBalanceTo(dr.Data.Balance)
	}
}