package blockchain

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

	"github.com/astaxie/beego"
	"github.com/astaxie/beego/logs"
	"github.com/ethereum/go-ethereum/accounts/abi"

	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"
	"google.golang.org/grpc"
)

var ktoClient pb.GreeterClient
var ktoRpc = beego.AppConfig.String("hostHttp_mit_kto")
var currentBlock uint64

var TopsMap map[string]string

func init() {
	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")

	tm := make(map[string]string, 0)
	tm[TopicParticipate] = COLLECT_PARTICIPATE
	TopsMap = tm

	go scanBlock()
	go modifyClaimTxs()
}

func scanBlock() {
	var bi models.BlockInfo
	ok, err := dbUtil.Engine.Id(1).Get(&bi)
	if err != nil {
		fmt.Println("获取最新快高错误=", err)
		return
	}
	if !ok {
		fmt.Println("获取最新快高失败!")
		return
	}
	currentBlock = uint64(bi.BlockNumber) + 1
	//currentBlock =62203394
	for {
		time.Sleep(time.Second * 3)
		res, err := ktoClient.GetMaxBlockHeight(context.Background(), &pb.ReqMaxBlockHeight{})
		if err != nil {
			fmt.Println("获取交易block number错误=", err)
			continue
		}
		if res.MaxHeight < currentBlock {
			currentBlock = res.MaxHeight
		}

		bl, err := ktoClient.GetBlockByNum(context.Background(), &pb.ReqBlockByNumber{Height: currentBlock})
		if err != nil || bl.Code != 0 {
			fmt.Println("获取交易block错误=", err)
			continue
		}
		blc, errs := block.Deserialize(bl.Data)
		if errs != nil {
			fmt.Println("解析blcock错误=", errs)
			continue
		}
		var ERR error
		for _, v := range blc.Transactions {
			evm, err := transaction.DecodeEvmData(v.Input)
			if err != nil {
				ERR = err
				break
			}
			if len(evm.Logs) == 0 {
				continue
			}
			for _, l := range evm.Logs {
				th := l.Topics[0].Hex()
				method := TopsMap[th]
				contract := l.Address.String()
				if contract == COLLECTCONTRACT {
					if method == COLLECT_PARTICIPATE {
						var ev EventParticipate
						abi, err := abi.JSON(strings.NewReader(Collect.CollectMetaData.ABI))
						if err != nil {
							ERR = err
							break
						}
						err = abi.UnpackIntoInterface(&ev, method, l.Data)
						if err != nil {
							ERR = err
							break
						}
						fmt.Printf("participate event data=%+v\n ", ev)
						//handle user participate
						err = participate(dbUtil.Engine, ev.User.String(), ev.Inviter.String(), v.HashToString(), float64(ev.Amount.Uint64()))
						if err != nil {
							ERR = err
							break
						}
					} else if method == COLLECT_CLAIMREWARDS {
						var evt EventClaim
						abi, err := abi.JSON(strings.NewReader(Collect.CollectMetaData.ABI))
						if err != nil {
							ERR = err
							break
						}
						err = abi.UnpackIntoInterface(&evt, method, l.Data)
						if err != nil {
							ERR = err
							break
						}
						fmt.Printf("claim event data=%+v\n ", evt)

						err = checkClaim(dbUtil.Engine, evt.User.String(), v.HashToString(), hex.EncodeToString(evt.Signature), float64(evt.Amount.Uint64()))
						if err != nil {
							ERR = err
							break
						}
					}
				}

			}
			if ERR != nil {
				break
			}
		}
		if ERR != nil {
			fmt.Println("处理错误=", ERR)
			continue
		}
		bi.BlockNumber = int64(currentBlock)
		_, err = dbUtil.Engine.ID(1).Update(&bi)
		if err != nil {
			fmt.Println("更新最新快高错误=", err)
			return
		}
		currentBlock++
	}
}

func modifyClaimTxs() {
	for {
		time.Sleep(time.Second)
		mark := 0
		var tx models.ClaimedTxs
		ok, err := dbUtil.Engine.Where("state = ?", 0).Where("droped = ?", 0).Get(&tx)
		if err != nil {
			continue
		}
		if !ok {
			continue
		}
		for {
			time.Sleep(time.Second * 5)
			if mark > 200 {
				var ui models.UserInfo
				_, err := dbUtil.Engine.Id(tx.Hash).Get(&ui)
				if err != nil {
					break
				}
				ui.AvailableClaim += tx.Amount
				ui.TotalClaimed -= tx.Amount
				_, err = dbUtil.Engine.ID(tx.Addr).Cols("total_claimed,available_claim").Update(&ui)
				if err != nil {
					break
				}

				tx.Droped = 1
				_, err = dbUtil.Engine.ID(tx.Hash).Cols("droped").Update(&tx)
				if err != nil {
					break
				}
				break
			}
			_, err = ktoClient.GetTxByHash(context.Background(), &pb.ReqTxByHash{Hash: tx.Hash})
			if err != nil {
				mark++
				continue
			}

			tx.State = 1
			_, err = dbUtil.Engine.ID(tx.Hash).Cols("droped").Update(&tx)
			if err != nil {
				break
			}
		}

	}

}
