package util

import (
	"context"
	"encoding/hex"
	"fmt"
	"ktogame/dbUtil"
	"ktogame/enums"
	"ktogame/models"
	"os"
	"strings"
	"time"

	"github.com/astaxie/beego"
	"github.com/astaxie/beego/logs"
	"github.com/ethereum/go-ethereum/accounts/abi"
	"github.com/ethereum/go-ethereum/accounts/abi/bind"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/ethclient"
	utilAddress "github.com/korthochain/korthochain/pkg/address"
	"github.com/korthochain/korthochain/pkg/block"
	pb "github.com/korthochain/korthochain/pkg/server/grpcserver/message"
	"github.com/korthochain/korthochain/pkg/transaction"
	"github.com/shopspring/decimal"
	"google.golang.org/grpc"
)

var ktoClient pb.GreeterClient
var conn *ethclient.Client
var ktoRpc = beego.AppConfig.String("hostHttp_mit_kto")
var ethRpc = beego.AppConfig.String("miner_eth")
var IdWork *Snowflake
var kx int64

func init() {
	fmt.Println("a3")
	IdWork, _ = NewSnowflake(0)
	kc, err := grpc.Dial(ktoRpc, grpc.WithInsecure(), grpc.WithBlock())
	if err != nil {
		logs.Info(err)
		os.Exit(1)
	}
	ktoClient = pb.NewGreeterClient(kc)
	utilAddress.SetNetWork("mainnet")
	conn, err = ethclient.Dial(ethRpc)
	if err != nil {
		fmt.Printf("Failed to connect to etherum client: %v\n", err)
	}
	go getNewBlock()
	go listenKto()
	go cheakWithdrawal()
	//go fenfa()
}

func listenKto() {
	time.Sleep(time.Second * 2)
	kxredis := dbUtil.GetValue(enums.USE_FHL_BLOCK, 1)
	if kxredis.(int64) == 0 {
		nb := dbUtil.GetValue(enums.MAX_FHL_BLOCK, 1)
		kx = nb.(int64)
	} else {
		kx = kxredis.(int64)
	}
	//kx =62203394
	for {
		nb := dbUtil.GetValue(enums.MAX_FHL_BLOCK, 1)
		fmt.Println("当前=", kx, "最新=", nb.(int64), "相差=", nb.(int64)-kx)
		if nb.(int64) < kx+1 {
			time.Sleep(time.Second * 10)
			continue
		}
		func() {
			reqBlock := new(pb.ReqBlockByNumber)
			reqBlock.Height = uint64(kx)
			bl, err := ktoClient.GetBlockByNum(context.Background(), reqBlock)
			if err != nil || bl.Code != 0 {
				fmt.Println("获取交易block错误=", err)
				time.Sleep(time.Second * 5)
				return
			}
			blc, errs := block.Deserialize(bl.Data)
			if errs != nil {
				time.Sleep(time.Second * 5)
				fmt.Println("获取交易blc错误=", errs)
			}
			for _, v := range blc.Transactions {
				evm, errLog := transaction.DecodeEvmData(v.Input)
				if errLog != nil {
					continue
				}
				if len(evm.Logs) != 1 {
					continue
				}
				for _, l := range evm.Logs {
					th := l.Topics[0].Hex()
					method := dbUtil.TopsMap[th]
					addr := l.Address.String()
					if method == enums.NEWDEPOSIT && addr == enums.USDTMINTCONTRACT {
						var ev models.Ihot
						abi, err := abi.JSON(strings.NewReader(FhlMetaData.ABI))
						if err != nil {
							logs.Error("abi err1=", err)
						}
						err = abi.UnpackIntoInterface(&ev, method, l.Data)
						if err != nil {
							logs.Error("abi err2=", err)
						}
						fmt.Printf("data=%+v\n ", ev)
						usdrido(ev.User.String(), ev.Referrer.String(), v.HashToString(), decimal.NewFromBigInt(ev.Amount, -11), decimal.NewFromBigInt(ev.Price, -18), kx)
					} else if method == enums.WITHDRAWAL && addr == enums.WITHDRAWCONTRACT {
						var ev models.Withdraw
						abi, err := abi.JSON(strings.NewReader(FhlMetaData.ABI))
						if err != nil {
							logs.Error("abi err1=", err)
						}
						err = abi.UnpackIntoInterface(&ev, method, l.Data)
						if err != nil {
							logs.Error("abi err2=", err)
						}
						fmt.Printf("data=%+v\n ", ev)
						withdrawal(kx, "0x"+v.HashToString(), ev.User.String(), decimal.NewFromBigInt(ev.Amount, -18))
					}
				}
			}
		}()
		kx++
		dbUtil.SetValue(enums.USE_FHL_BLOCK, kx)
		continue
		//return
	}
}

func getNewBlock() {
	for {
		number := new(pb.ReqMaxBlockHeight)
		num, err := ktoClient.GetMaxBlockHeight(context.Background(), number)
		if err != nil {
			logs.Info("断开重连2", err)
			time.Sleep(time.Second * 10)
			continue
		} else {
			dbUtil.SetValue(enums.MAX_FHL_BLOCK, int64(num.MaxHeight))
		}
		time.Sleep(time.Second * 10)
	}
}

func usdrido(addr, bindAddr, hash string, da, price decimal.Decimal, block int64) {
	fmt.Println("==进来==", addr)
	//当前用户
	var user models.UserAddr

	//上级
	var bindUser models.UserAddr
	dbUtil.Engine.Id(bindAddr).Get(&bindUser)

	//上上级
	var bossUser models.UserAddr
	bb, _ := dbUtil.Engine.Where("id_flag=?", bindUser.Pid).Get(&bossUser)

	//原始上上级
	var realBossUser models.UserAddr
	var rb bool
	if bindUser.Pid != bindUser.RealPid {
		rb, _ = dbUtil.Engine.Where("id_flag=?", bindUser.RealPid).Get(&realBossUser)
	}

	//上上上级
	var pbossUser models.UserAddr
	pbb, _ := dbUtil.Engine.Where("id_flag=?", bossUser.Pid).Get(&pbossUser)

	//上上上上级
	var ppbossUser models.UserAddr
	ppbb, _ := dbUtil.Engine.Where("id_flag=?", pbossUser.Pid).Get(&ppbossUser)

	var com models.CommonInfo
	dbUtil.Engine.Get(&com)
	//1.处理用户
	var msxSort models.UserAddr
	dbUtil.Engine.Desc("id_flag").Get(&msxSort) //最大序号
	user.Addr = addr
	user.IdFlag = msxSort.IdFlag + 1
	//上级是boss，不转移
	if bindUser.Level > 0 || bindUser.Pid == -1 {
		user.Pid = bindUser.IdFlag
		user.RealPid = bindUser.IdFlag
		user.PidFlag = bindUser.PidFlag + "," + fmt.Sprint(bindUser.IdFlag)
	} else {
		user.Pid = bossUser.IdFlag
		user.RealPid = bindUser.IdFlag
		user.PidFlag = bossUser.PidFlag + "," + fmt.Sprint(bossUser.IdFlag)
	}
	user.Msg = "暂无惩罚"
	user.BadTime = NowTimeString()
	user.CreateTime = NowTimeString()
	user.Amount, _ = da.Float64()
	user.Price, _ = price.Float64()
	user.Block = block
	user.Hash = hash
	//-----------处理用户结束--------------

	nt := time.Now().Unix()
	var sbbu, sbbo, sbpb, sbpbp models.SuccessBill
	//2.处理用户的直推
	var isUp bool
	if bindUser.Pid != -1 {
		te, _ := time.ParseInLocation("2006-01-02 15:04:05", bindUser.BadTime, time.Local)
		sbbu = models.SuccessBill{IdWork.Generate(), bindUser.Addr, 0, 1, 0, "", addr, 1, NowTimeString()}
		if bindUser.Level > 0 && te.Unix() < nt { //在处罚中
			sbbu.Amount, _ = da.Mul(decimal.NewFromFloat(0.3)).Float64()
			bindUser.AmountLv, _ = decimal.NewFromFloat(bindUser.AmountLv).Add(decimal.NewFromFloat(0.3)).Float64()
		} else {
			sbbu.Amount, _ = da.Mul(decimal.NewFromFloat(0.5)).Float64()
			bindUser.AmountLv, _ = decimal.NewFromFloat(bindUser.AmountLv).Add(decimal.NewFromFloat(0.5)).Float64()
		}
		bindUser.UseAmount, _ = decimal.NewFromFloat(bindUser.UseAmount).Add(decimal.NewFromFloat(sbbu.Amount)).Float64()
		bindUser.Profit1, _ = decimal.NewFromFloat(bindUser.Profit1).Add(decimal.NewFromFloat(sbbu.Amount)).Float64()
		if bindUser.RemovePeople == 0 {
			bindUser.RemovePeople = bindUser.RemovePeople + 1
		} else if bindUser.RemovePeople == 1 {
			isUp = true
			bindUser.RemovePeople = bindUser.RemovePeople + 1
			if bindUser.Level < 3 {
				bindUser.Level = 1
			}
			format := time.Now().AddDate(0, 0, 7).Format("2006-01-02 15:04:05")
			bindUser.BadTime = format
			bindUser.Msg = "当给上级boss转移的2位员工发生直推时，将重置惩罚时间"
		} else {
			bindUser.People = bindUser.People + 1
		}
	} else {
		mul, _ := da.Mul(decimal.NewFromFloat(0.5)).Float64()
		sbbu = models.SuccessBill{IdWork.Generate(), bindUser.Addr, mul, 1, 0, "", addr, 1, NowTimeString()}
		bindUser.Profit1, _ = decimal.NewFromFloat(bindUser.Profit1).Add(decimal.NewFromFloat(mul)).Float64()
		bindUser.UseAmount, _ = decimal.NewFromFloat(bindUser.UseAmount).Add(decimal.NewFromFloat(mul)).Float64()
	}

	//-----------处理上级结束--------------

	//3.处理上上级
	if bb {
		te, _ := time.ParseInLocation("2006-01-02 15:04:05", bossUser.BadTime, time.Local)
		sbbo = models.SuccessBill{IdWork.Generate(), bossUser.Addr, 0, 1, 0, "", addr, 2, NowTimeString()}
		if bossUser.Pid != -1 {
			if bossUser.Level > 0 && te.Unix() < nt { //在处罚中
				sbbo.Amount, _ = da.Mul(decimal.NewFromFloat(0.1)).Float64()
				bossUser.AmountLv, _ = decimal.NewFromFloat(bossUser.AmountLv).Add(decimal.NewFromFloat(0.1)).Float64()
			} else {
				sbbo.Amount, _ = da.Mul(decimal.NewFromFloat(0.3)).Float64()
				bossUser.AmountLv, _ = decimal.NewFromFloat(bossUser.AmountLv).Add(decimal.NewFromFloat(0.3)).Float64()
			}
			bossUser.Profit2, _ = decimal.NewFromFloat(bossUser.Profit2).Add(decimal.NewFromFloat(sbbo.Amount)).Float64()
			bossUser.UseAmount, _ = decimal.NewFromFloat(bossUser.UseAmount).Add(decimal.NewFromFloat(sbbo.Amount)).Float64()
			if isUp {
				if bb {
					if bossUser.TeamBoss > 0 && bossUser.TeamBoss < 3 {
						bossUser.Level = bossUser.TeamBoss + 1
					}
					bossUser.TeamBoss = bossUser.TeamBoss + 1
				}
			}
		} else {
			f, _ := da.Mul(decimal.NewFromFloat(0.3)).Float64()
			sbbo.Amount = f
			bossUser.UseAmount, _ = decimal.NewFromFloat(bossUser.UseAmount).Add(decimal.NewFromFloat(f)).Float64()
			bossUser.Profit2, _ = decimal.NewFromFloat(bossUser.Profit2).Add(decimal.NewFromFloat(f)).Float64()
		}
	}

	//-----------处理上上级结束--------------

	//4.处理原始上上级
	if rb {
		var rpUser []models.UserAddr
		dbUtil.Engine.Where("real_pid=? and pid!=real_pid", bindUser.RealPid).Find(&rpUser)
		if realBossUser.RemovePeople == 2 {
			e1 := rpUser[0].RemovePeople
			e2 := rpUser[1].RemovePeople
			if bindUser.IdFlag == rpUser[0].IdFlag {
				e1 = e1 + 1
			}
			if bindUser.IdFlag == rpUser[1].IdFlag {
				e2 = e2 + 1
			}
			if e1+e2 > 1 && e1 != 0 && e2 != 0 {
				if e1 > 0 && e2 > 0 && (e1 < 2 || e2 < 2) {
					format := time.Now().AddDate(0, 0, 7).Format("2006-01-02 15:04:05")
					realBossUser.BadTime = format
					realBossUser.Msg = "当给上级boss转移的2位员工都成为BOSS时，您将永久解除惩罚"
				} else if e1 > 1 && e2 > 1 {
					realBossUser.BadTime = "2099-01-01 08:00:00"
					realBossUser.Msg = "您已永久解除惩罚"
				}
			}
		}
	}
	//-----------处理原始上上级结束--------------

	//5.处理上上上级
	if pbb {
		te, _ := time.ParseInLocation("2006-01-02 15:04:05", pbossUser.BadTime, time.Local)
		sbpb = models.SuccessBill{IdWork.Generate(), pbossUser.Addr, 0, 1, 0, "", addr, 3, NowTimeString()}
		if pbossUser.Pid != -1 {
			if pbb {
				ru1 := da.Mul(decimal.NewFromFloat(0.04))
				if pbossUser.Level > 0 && te.Unix() < nt { //在处罚中
					pbossUser.LockAmount2, _ = decimal.NewFromFloat(pbossUser.LockAmount2).Add(ru1).Float64()
				} else {
					sbpb.Amount, _ = ru1.Float64()
					pbossUser.AmountLv, _ = decimal.NewFromFloat(pbossUser.AmountLv).Add(decimal.NewFromFloat(0.04)).Float64()
				}
				pbossUser.UseAmount, _ = decimal.NewFromFloat(pbossUser.UseAmount).Add(decimal.NewFromFloat(sbpb.Amount)).Float64()
				pbossUser.Profit3, _ = decimal.NewFromFloat(pbossUser.Profit3).Add(decimal.NewFromFloat(sbpb.Amount)).Float64()
			}
		} else {
			mul, _ := da.Mul(decimal.NewFromFloat(0.04)).Float64()
			sbpb.Amount = mul
			pbossUser.UseAmount, _ = decimal.NewFromFloat(pbossUser.UseAmount).Add(decimal.NewFromFloat(mul)).Float64()
			pbossUser.Profit3, _ = decimal.NewFromFloat(pbossUser.Profit3).Add(decimal.NewFromFloat(mul)).Float64()
		}
	}

	//5.处理上上上上级
	if ppbb {
		te, _ := time.ParseInLocation("2006-01-02 15:04:05", ppbossUser.BadTime, time.Local)
		sbpbp = models.SuccessBill{IdWork.Generate(), ppbossUser.Addr, 0, 1, 0, "", addr, 4, NowTimeString()}
		ru1 := da.Mul(decimal.NewFromFloat(0.06))
		if ppbossUser.Pid != -1 {
			if ppbb {
				if ppbossUser.Level > 0 && te.Unix() < nt { //在处罚中
					ppbossUser.LockAmount2, _ = decimal.NewFromFloat(ppbossUser.LockAmount2).Add(ru1).Float64()
				} else {
					sbpbp.Amount, _ = ru1.Float64()
					ppbossUser.AmountLv, _ = decimal.NewFromFloat(ppbossUser.AmountLv).Add(decimal.NewFromFloat(0.06)).Float64()
				}
				ppbossUser.UseAmount, _ = decimal.NewFromFloat(ppbossUser.UseAmount).Add(decimal.NewFromFloat(sbpbp.Amount)).Float64()
				ppbossUser.Profit3, _ = decimal.NewFromFloat(ppbossUser.Profit3).Add(decimal.NewFromFloat(sbpbp.Amount)).Float64()
			}
		} else {
			sbpbp.Amount, _ = ru1.Float64()
			ppbossUser.UseAmount, _ = decimal.NewFromFloat(ppbossUser.UseAmount).Add(ru1).Float64()
			ppbossUser.Profit3, _ = decimal.NewFromFloat(ppbossUser.Profit3).Add(ru1).Float64()
		}
	}

	dbUtil.Engine.Insert(&user)
	dbUtil.Engine.ID(bindUser.Addr).Update(&bindUser)

	if bb {
		dbUtil.Engine.ID(bossUser.Addr).Update(&bossUser)
	}
	if rb {
		dbUtil.Engine.ID(realBossUser.Addr).Update(&realBossUser)
	}

	if pbb {
		dbUtil.Engine.ID(pbossUser.Addr).Update(&pbossUser)
	}
	if ppbb {
		dbUtil.Engine.ID(ppbossUser.Addr).Update(&ppbossUser)
	}
	var suc decimal.Decimal
	if sbbu.Amount > 0 {
		suc = suc.Add(decimal.NewFromFloat(sbbu.Amount))
		dbUtil.Engine.Insert(&sbbu)
	}

	if sbbo.Amount > 0 {
		suc = suc.Add(decimal.NewFromFloat(sbbo.Amount))
		dbUtil.Engine.Insert(&sbbo)
	}

	if sbpb.Amount > 0 {
		suc = suc.Add(decimal.NewFromFloat(sbpb.Amount))
		dbUtil.Engine.Insert(&sbpb)
	}

	if sbpbp.Amount > 0 {
		suc = suc.Add(decimal.NewFromFloat(sbpbp.Amount))
		dbUtil.Engine.Insert(&sbpbp)
	}
	sub := da.Sub(suc)
	com.Amount, _ = decimal.NewFromFloat(com.Amount).Add(sub).Float64()
	dbUtil.Engine.ID(com.Id).Update(&com)
}

func Sign(amountStr, addr, timeStr string) string {
	keccakA := crypto.Keccak256([]byte(amountStr), []byte(enums.DIGIT))
	keccakS := crypto.Keccak256(keccakA, []byte(timeStr), []byte(addr))
	return "0x" + hex.EncodeToString(keccakS)
}

//提币
func withdrawal(kx int64, hash, addr string, amount decimal.Decimal) {
	var ua models.UserAddr
	b, _ := dbUtil.Engine.Id(addr).Get(&ua)
	if !b {
		return
	}
	f, _ := amount.Float64()
	var wb models.WithdrawalBill
	bw, _ := dbUtil.Engine.Where("addr=? and status=0 and hash=?", addr, hash).Get(&wb)
	if !bw {
		bill := models.WithdrawalBill{IdWork.Generate(), addr, f, 1, kx, kx, hash, NowTimeString()}
		dbUtil.Engine.Insert(&bill)
		ua.UseAmount, _ = decimal.NewFromFloat(ua.UseAmount).Sub(amount).Float64()
		dbUtil.Engine.ID(ua.Addr).Cols("use_amount").Update(&ua)
		return
	}
	wb.Status = 1
	wb.SucBlock = kx
	dbUtil.Engine.ID(wb.Id).Cols("status,suc_block").Update(&wb)
}

func cheakWithdrawal() {
	for {
		h, _ := time.ParseDuration("-1200s")
		format := time.Now().AddDate(0, 0, 0).Add(h).Format("2006-01-02 15:04:05")
		var wb []models.WithdrawalBill
		dbUtil.Engine.Where("status=0 AND create_block<? AND create_time<?", kx-int64(30), format).Find(&wb)
		for _, v := range wb {
			req1 := new(pb.ReqTxByHash)
			req1.Hash = v.Hash
			data, err := ktoClient.GetTxByHash(context.Background(), req1)
			if err != nil {
				logs.Error("addr=", v.Addr, "block=", kx, "err=", err)
				continue
			}
			tx, err := transaction.DeserializeFinishedTransaction(data.Data)
			if err != nil {
				v.Status = -1
				dbUtil.Engine.ID(v.Id).Cols("status").Update(&v)
				var ua models.UserAddr
				dbUtil.Engine.Id(v.Addr).Get(&ua)
				ua.UseAmount, _ = decimal.NewFromFloat(ua.UseAmount).Add(decimal.NewFromFloat(v.Amount)).Float64()
				dbUtil.Engine.ID(ua.Addr).Cols("use_amount").Update(&ua)
			} else {
				v.Status = 1
				v.SucBlock = int64(tx.BlockNum)
				v.Hash = tx.HashToString()
				dbUtil.Engine.ID(v.Id).Cols("status,suc_block,hash").Update(&v)
			}
		}
		time.Sleep(time.Second * 300)
	}
}

func getAmountOut() decimal.Decimal {
	ic, err := NewRouter(common.HexToAddress(enums.AMOUNTCONTRACT), conn)
	if err != nil {
		logs.Error("err=", err)
		return decimal.Zero
	}
	amountIn := decimal.New(1, 11).BigInt()
	addrs := make([]common.Address, 0)
	gdlAddress := common.HexToAddress(enums.KTOCONTRACT)
	usdtAddress := common.HexToAddress(enums.USDT)
	addrs = append(addrs, gdlAddress, usdtAddress)
	b, err := ic.GetAmountsOut(&bind.CallOpts{}, amountIn, addrs)
	if err != nil {
		logs.Error("err=", err)
		return decimal.Zero
	}
	return decimal.NewFromBigInt(b[1], -18)
}
