package cron

import (
	"context"
	"encoding/hex"
	"errors"
	"strings"
	"time"

	"github.com/33cn/chain33/common"
	coinsTypes "github.com/33cn/chain33/system/dapp/coins/types"
	"github.com/33cn/chain33/types"
	"go.uber.org/zap"
	"gorm.io/gorm"
	"courseSign/server/global"
	"courseSign/server/model/entrustPool"
	"courseSign/server/utils"
	//"courseSign/server/utils/ai"
	"courseSign/server/utils/chain33"

	types3 "github.com/33cn/plugin/plugin/dapp/evm/types"
	types2 "github.com/33cn/plugin/plugin/dapp/ticket/types"
)

const (
	EvmAddress = "19tjS51kjwrCoSQS13U3owe7gYBLfSfoFm"
	EvmTicketAddr = "0x0000000000000000000000000000000000200005"
	MetaMaskTxExpire = 3600 * 3
)

type SyncBlockJob struct {
	JobName    string
	Client     types.Chain33Client
	PoolMap    map[string]*entrustPool.Pool
	JobVars    *SyncVars
}

type SyncVars struct {
	Height int64
	FeeRate int64
	FeeAddr string
}

var isSyncRun bool

// Run
// 同步相关联action至 pool_action_txs
// 同步miner交易至 pool_addr_txs
// 同步开始绑定到解绑完成对应的交易和状态至 pool_addr
// 更新最小高度到 pool_config
func (job SyncBlockJob) Run() {
	job.JobName = "SyncBlockJob"
	if isSyncRun {
		global.GVA_LOG.Info(job.JobName,zap.String("job","already run"))
		return
	}
	isSyncRun = true
	global.GVA_LOG.Info(job.JobName, zap.String("job", "start"))
	t := time.Now()
	defer func() {
		isSyncRun = false
		global.GVA_LOG.Info(job.JobName, zap.String("cost", time.Since(t).String()))
	}()
	var err error
	job.JobVars = &SyncVars{}
	job.Client = global.Client
	poolMap := make(map[string]*entrustPool.Pool,0)
	job.PoolMap = poolMap
	pools := make([]*entrustPool.Pool,0)
	err = global.GVA_DB.Model(&entrustPool.Pool{}).Find(&pools).Error
	if err != nil {
		global.GVA_LOG.Error(job.JobName, zap.String("get pools err", err.Error()))
		return
	}
	for _,v := range pools {
		job.PoolMap[v.MinerAddr] = v
	}

	err = job.ParseBlock()
	if err != nil {
		global.GVA_LOG.Error(job.JobName, zap.String("ParseBlock err", err.Error()))
		return
	}
	err = job.QueryFeeHash()
	if err != nil {
		global.GVA_LOG.Error(job.JobName, zap.String("QueryFeeHash err", err.Error()))
		return
	}
	// err = job.CreateFeature()
	// if err != nil {
	// 	global.GVA_LOG.Error(job.JobName, zap.String("CreateFeature err", err.Error()))
	// 	return
	// }
	global.GVA_LOG.Info(job.JobName, zap.String("job", "end"))
}

// func(job SyncBlockJob) CreateFeature() error {
// 	if global.GVA_CONFIG.VoiceServer.AppId == "" {
// 		return errors.New("voice config not set")
// 	}
// 	list := make([]airDrop.RedPackage,0)
// 	err := global.GVA_DB.Debug().Model(&airDrop.RedPackage{}).Where("verify_type = ? and group_id = ? ",airDrop.PackageVerifyVoice,-1).Find(&list).Error
// 	if err != nil {
// 		return err
// 	}
// 	global.GVA_LOG.Info("未创建group ",zap.Int("num",len(list)))
// 	for _,v := range list {
// 		groupId := ai.GetGroupId(int64(v.ID))
// 		req := &ai.ReqVpsInfo{
// 			ApiKey:      global.GVA_CONFIG.VoiceServer.ApiKey,
// 			ApiSecret:   global.GVA_CONFIG.VoiceServer.ApiSecret,
// 			AppId:       global.GVA_CONFIG.VoiceServer.AppId,
// 			ApiName:     "createGroup",
// 			GroupId:     groupId,
// 			GroupName:   groupId,
// 			GroupInfo:   groupId,
// 		}
// 		res,code,err := ai.Vrg(req)
// 		global.GVA_LOG.Debug("createGroup",zap.Any("res",res),zap.Any("code",code),zap.Any("err",err))
// 		if err != nil {
// 			global.GVA_LOG.Error("createGroup",zap.Error(err))
// 			return err
// 		}
// 		if res != nil && res.GroupId != "" {
// 			err = global.GVA_DB.Model(&airDrop.RedPackage{}).Where("id = ?",v.ID).Update("group_id",res.GroupId).Error
// 			if err != nil {
// 				global.GVA_LOG.Error("createGroup ok update db err",zap.Error(err))
// 				return err
// 			}
// 			global.GVA_LOG.Info("createGroup Ok")
// 		} else {
// 			global.GVA_LOG.Error("createGroup failed",zap.Error(err))
// 		}
// 	}
// 	return nil
// }

// QueryFeeHash 确认fee hash 是否成功 并更新数据
func(job SyncBlockJob) QueryFeeHash() error {
	list := make([]entrustPool.PoolAddr,0)
	err := global.GVA_DB.Model(&entrustPool.PoolAddr{}).Find(&list).Error
	if err != nil {
		return err
	}
	for _,v := range list {
		if v.FeeHash != "" {
			var checkHash entrustPool.PoolAddrFeeHistory
			err = global.GVA_DB.Model(&entrustPool.PoolAddrFeeHistory{}).Where("hash = ?",v.FeeHash).First(&checkHash).Error
			if err != nil && err != gorm.ErrRecordNotFound{
				return err
			}
			// 此hash已计算过  不重复计算
			if err == nil && checkHash.ID > 0 {
				err = global.GVA_DB.Model(&entrustPool.PoolAddr{}).Where("id =?",v.ID).Updates(map[string]interface{}{"fee_hash":"","fee_tbd":0}).Error
				if err != nil {
					return err
				}
				continue
			}
			var feeAddr string
			var pool entrustPool.Pool
			err = global.GVA_DB.Model(&entrustPool.Pool{}).Where("id = ?",*v.PoolId).First(&pool).Error
			if err != nil {
				return err
			}
			if pool.FeeAddr != "" {
				feeAddr = pool.FeeAddr
			}
			if feeAddr == "" {
				feeAddr = job.JobVars.FeeAddr
			}
			global.GVA_LOG.Info("fee hash",zap.Any(" string",v.FeeHash))
			hashByte,err := common.FromHex(v.FeeHash)
			if err != nil {
				return err
			}
			result,err := job.Client.QueryTransaction(context.Background(),&types.ReqHash{Hash: hashByte})
			if err != nil {
				return err
			}
			if strings.ToLower(result.Tx.To) != feeAddr {
				return errors.New("to not eq FeeAddr")
			}
			// 成功处理
			if result.Height < job.JobVars.Height && job.JobVars.Height != 0 && result.Receipt.Ty == 2{
				var evmAction types3.EVMContractAction
				err = types.Decode(result.Tx.Payload,&evmAction)
				if err != nil {
					return err
				}
				tx := global.GVA_DB.Begin()
				global.GVA_LOG.Info("evm transfer action",zap.Any("amount",evmAction.Amount))
				err = tx.Model(&entrustPool.PoolAddr{}).Where("id =?",v.ID).Updates(map[string]interface{}{"fee_hash":"","fee_tbd":0,"fee_send":gorm.Expr("fee_send+?",evmAction.Amount/1e6)}).Error
				if err != nil {
					tx.Rollback()
					return err
				}
				var exitTime,reward,fee int32
				exitTime = int32(result.Blocktime)
				reward = int32(evmAction.Amount/1e5)
				fee = int32(evmAction.Amount/1e6)
				var exitDate time.Time
				exitDate = utils.GetDate(result.Blocktime)
				err = tx.Create(&entrustPool.PoolAddrFeeHistory{
					Addr:      v.Addr,
					Hash:      v.FeeHash,
					ExitTime:  &exitTime,
					Reward:    &reward,
					Fee:       &fee,
					ExitDate:  &exitDate,
				}).Error
				if err != nil {
					tx.Rollback()
					return err
				}
				tx.Commit()
				global.GVA_LOG.Info("update fee hash ",zap.Any("ok",evmAction.Amount))
			} else if result.Receipt.Ty == 1 { // 执行失败
				global.GVA_LOG.Info("tx exec failed")
				err = global.GVA_DB.Model(&entrustPool.PoolAddr{}).Where("id =?",v.ID).Updates(map[string]interface{}{"fee_hash":"","fee_tbd":0}).Error
				if err != nil {
					return err
				}
			}
		}
	}
	return nil
}

// ParseBlock 解析区块 同步相关业务逻辑至对应数据表
func (job SyncBlockJob) ParseBlock() error {
	var err error
	err = job.CheckClientIsSync()
	if err != nil {
		return err
	}
	err = job.InitConfig()
	if err != nil {
		return err
	}
	headers,err := job.Client.GetLastHeader(context.Background(),&types.ReqNil{})
	if err != nil {
		global.GVA_LOG.Error(job.JobName, zap.String("GetLastHeader err", err.Error()))
		return err
	}
	global.GVA_LOG.Info(job.JobName,zap.Any("lastHeader",headers.Height),zap.Any("job height",job.JobVars.Height))
	if headers.Height - 6 <= job.JobVars.Height {
		global.GVA_LOG.Info(job.JobName, zap.Any("waiting new block ,block height ", headers.Height),zap.Any("server height",job.JobVars.Height))
		return nil
	}
	var startHeight ,endHeight int64
	startHeight = job.JobVars.Height + 1
	endHeight = headers.Height - 6
	for i := startHeight;i <= endHeight;i++ {
		info ,err := job.Client.GetBlocks(context.Background(),&types.ReqBlocks{
			Start:    i,
			End:      i,
			IsDetail: true,
		})
		if err != nil {
			global.GVA_LOG.Error(job.JobName, zap.String("GetBlocks err", err.Error()))
			return err
		}
		if !info.IsOk {
			global.GVA_LOG.Error(job.JobName, zap.String("GetBlocks reply not ok msg", string(info.Msg)))
			return nil
		}
		var blocks types.BlockDetails
		err = types.Decode(info.Msg,&blocks)
		if err != nil {
			global.GVA_LOG.Error(job.JobName, zap.String("GetBlocks decode err", err.Error()))
			return err
		}
		err = job.ParserBlockDetails(blocks)
		if err != nil {
			global.GVA_LOG.Error(job.JobName, zap.String("ParserBlockDetails  err", err.Error()))
			return err
		}

	}
	return nil
}

func (job SyncBlockJob) ParserBlockDetails(blocks types.BlockDetails) error {
	for _,v :=range blocks.Items {
		err := job.ParserBlockDetailToSql(v)
		if err != nil {
			global.GVA_LOG.Error(job.JobName, zap.String("ParserBlockDetails  err", err.Error()))
			return  err
		}
	}
	return nil
}



// ParserBlockDetailToSql 解析区块链动作台同步至sql表
// 其他关联的合约可以对应开发
func (job SyncBlockJob) ParserBlockDetailToSql(blockDetail *types.BlockDetail) error {
	tx := global.GVA_DB.Begin()
	var err error
	for k, v := range blockDetail.Block.Txs {
		// 忽略错误交易
		if blockDetail.Receipts[k].Ty != 2 {
			continue
		}
		switch string(v.Execer) {
		case "ticket":
			err = job.ParseTicketExecer(blockDetail.Block,tx,v,blockDetail.Receipts[k].Logs)
			if err != nil {
				tx.Rollback()
				return err
			}
		case "coins":
			// err = job.ParseCoinsExecer(blockDetail.Block,tx,v,blockDetail.Receipts[k].Logs)
			// if err != nil {
			// 	tx.Rollback()
			// 	return err
			// }
		case "evm":
			// 转账
			// 委托挖矿绑定
			err = job.ParseEvmExecer(blockDetail.Block,tx,v,blockDetail.Receipts[k].Logs)
			if err != nil {
				tx.Rollback()
				return err
			}
		}

	}
	err = tx.Model(&entrustPool.PoolConfig{}).Where("job_name",job.JobName).Update("height",blockDetail.Block.Height).Error
	if err != nil {
		tx.Rollback()
		return err
	}
	return tx.Commit().Error
}
// ParseEvmExecer 解析evm 操作 转账 委托挖矿 合约操作 等等
func (job SyncBlockJob) ParseEvmExecer(block *types.Block,tx *gorm.DB,trans *types.Transaction,logs []*types.ReceiptLog) error {

	var evmAction types3.EVMContractAction
	err := types.Decode(trans.Payload,&evmAction)
	if err != nil {
		global.GVA_LOG.Error(job.JobName, zap.String("ParseEvmExecer  err", err.Error()))
		return err
	}
	// evm 转账
	if evmAction.ContractAddr == EvmAddress && common.ToHex(evmAction.Para) == trans.To {

	}

	// evm para action
	// para 有可能是各种内置chain合约的action
	if evmAction.ContractAddr == EvmTicketAddr && trans.To == EvmTicketAddr{
		// 1、 para => ticket action
		var action2 types.Transaction
		err = types.Decode(evmAction.Para,&action2)
		if err != nil {
			return err
		}
		var action types2.TicketAction
		err = types.Decode(action2.Payload,&action)
		if err != nil {
			return err
		}
		switch action.Ty {
		case types2.TicketActionBind:
			to := chain33.GetEthToLowerAddr(action.GetTbind().MinerAddress)
			from := chain33.GetEthToLowerAddr(action.GetTbind().ReturnAddress)
			// 不是pool地址 不入库
			if _,ok := job.PoolMap[to];!ok {
				return nil
			}

			var status int
			var poolId int32
			poolId = int32(job.PoolMap[to].ID)
			if action.GetTbind().ReturnAddress == action.GetTbind().MinerAddress {
				status = entrustPool.PoolAddrStatusUnbind
			} else {
				status = entrustPool.PoolAddrStatusBind
			}
			var pa entrustPool.PoolAddr
			err = tx.Model(&entrustPool.PoolAddr{}).Where("addr = ? and pool_id = ?",from,poolId).First(&pa).Error
			if err != nil && err != gorm.ErrRecordNotFound {
				return err
			} else if err != nil && err == gorm.ErrRecordNotFound {
				poolAddr := &entrustPool.PoolAddr{
					Addr:      from,
					PoolId:    &poolId,
					Status:    &status,
				}
				err = tx.Create(&poolAddr).Error
				if err != nil {
					return err
				}
			}
			// else {
			// 	err = tx.Model(&entrustPool.PoolAddr{}).Where("addr = ?  ",from).Updates(map[string]interface{}{"status":status,"pool_id":poolId}).Error
			// 	if err != nil {
			// 		return err
			// 	}
			// }


			err = job.RecordAction(trans,block,tx,0,"TicketActionBind")
			if err != nil {
				return err
			}
		}
		// 2 、 coins action
		var action3 types.Transaction
		err = types.Decode(evmAction.Para,&action3)
		if err != nil {
			return err
		}
		var cAction coinsTypes.CoinsAction
		err := types.Decode(action3.Payload,&cAction)
		if err != nil {
			return err
		}
		switch cAction.Ty {
		case coinsTypes.CoinsActionWithdraw: // 委托票数
			err = job.RecordWithdrawHistory(cAction.GetWithdraw().Amount,trans,block,tx)
			if err != nil {
				return err
			}
			err = job.RecordAction(trans,block,tx,cAction.GetWithdraw().Amount/1e8,"CoinsActionWithdraw")
			if err != nil {
				return err
			}
		case coinsTypes.CoinsActionTransferToExec: // 委托票数
			err = job.RecordAction(trans,block,tx,cAction.GetTransferToExec().Amount/1e8,"CoinsActionTransferToExec")
			if err != nil {
				return err
			}
		}
	}

	return nil
}

// ParseTicketExecer 解析常规ticket模块
func (job SyncBlockJob) ParseTicketExecer(block *types.Block,tx *gorm.DB,trans *types.Transaction,logs []*types.ReceiptLog) error {
	var action types2.TicketAction
	err := types.Decode(trans.Payload,&action)
	if err != nil {
		global.GVA_LOG.Error(job.JobName, zap.String("ParseTicketExecer  err", err.Error()))
		return err
	}
	from := chain33.GetEthToLowerAddr(trans.From())
	var height ,bt int32
	var bd time.Time
	height = int32(block.Height)
	bt = int32(block.BlockTime)
	switch action.Ty {
	case types2.TicketActionMiner: // 判断是pool对应出的矿写入pool_addr_txs

		// 不是pool地址挖的矿 不入库
		if _,ok := job.PoolMap[from];!ok {
			return nil
		}


		returnAddr,amount,err := job.GetReturnAddrAmountByReceipts(logs)
		if err != nil {
			global.GVA_LOG.Error(job.JobName, zap.String("GetReturnAddrAmountByReceipts  err", err.Error()))
			return err
		}
		if returnAddr == "" {
			tid := action.GetMiner().TicketId
			returnAddr,_,err = job.GetReturnAddrAmountByTicketId(tid)
			if err != nil {
				return err
			}
			if returnAddr == "" {
				return errors.New("return addr should not empty")
			}
		}
		returnAddr = chain33.GetEthToLowerAddr(returnAddr)
		// 自己挖矿忽略
		if returnAddr == from {
			global.GVA_LOG.Info(job.JobName,zap.String("return addr eq return addr",returnAddr))
			return nil
		}
		var pa entrustPool.PoolAddr
		err = tx.Model(&pa).Where("addr = ? and pool_id = ?",returnAddr,job.PoolMap[from].ID).First(&pa).Error
		if err != nil && err != gorm.ErrRecordNotFound{
			global.GVA_LOG.Error(job.JobName, zap.String("pa not exists err", err.Error()))
			return err
		} else if err != nil && err == gorm.ErrRecordNotFound {
			// elf开头的矿池  补全相关信息 pool_addr 信息
			if strings.Contains(job.PoolMap[from].Name,"elf") {
				elfCreate := entrustPool.PoolAddr{
					Addr:      returnAddr,
					PoolId: new(int32),
					Status: new(int),
				}
				*elfCreate.PoolId = int32(job.PoolMap[from].ID)
				*elfCreate.Status = entrustPool.PoolAddrStatusBind
				err = tx.Create(&elfCreate).Error
				if err != nil {
					global.GVA_LOG.Info(job.JobName,zap.String("elf create pool add err",err.Error()))
					return err
				}
			} else {
				return nil
			}
		}
		pool := job.PoolMap[from]
		var fee int64
		if pool.FeeRate != nil && *pool.FeeRate > 0 {
			fee = int64(*pool.FeeRate)
		} else {
			fee = job.JobVars.FeeRate
		}
		err = tx.Model(&entrustPool.PoolAddr{}).Where("addr = ? and pool_id = ?",returnAddr,job.PoolMap[from].ID).Update("fee",gorm.Expr("fee+?",amount*100*fee/100)).Error
		if err != nil {
			return err
		}
		err = tx.Model(&entrustPool.Pool{}).Where("id = ?",pool.ID).Update("last_time",block.BlockTime).Error
		if err != nil {
			return err
		}
		var am int32
		am = int32(amount)
		bd = utils.GetDate(block.BlockTime)
		err = tx.Create(&entrustPool.PoolAddrTxs{
			Height:     &height,
			Hash:       common.ToHex(trans.Hash()),
			MinerAddr:  from,
			ReturnAddr: returnAddr,
			Blocktime:  &bt,
			BlockDate:  &bd,
			Amount:     &am,
			Name: job.PoolMap[from].Name,
		}).Error
		if err != nil {
			return err
		}
		global.GVA_LOG.Info(job.JobName, zap.String("  ParseTicketExecer return addr ", returnAddr),zap.String(" pool addr",from))
	case types2.TicketActionBind:  // 小狐狸委托挖矿不走此步
		// // 不是pool地址 不入库
		// if _,ok := job.PoolMap[from];!ok {
		// 	return nil
		// }
		//
		// var poolId,status int
		// poolId = int(job.PoolMap[from].ID)
		// if action.GetTbind().ReturnAddress == action.GetTbind().MinerAddress {
		// 	status = entrustPool.PoolAddrStatusUnbind
		// } else {
		// 	var pa entrustPool.PoolAddr
		// 	err = tx.Model(&entrustPool.PoolAddr{}).Where("addr = ?",action.GetTbind().ReturnAddress).First(&pa).Error
		// 	if err == nil {
		// 		global.GVA_LOG.Error(job.JobName, zap.String("TicketActionBind  already ", "bind"))
		// 		return nil
		// 	}
		// 	if err != nil && err != gorm.ErrRecordNotFound {
		// 		return err
		// 	} else if err != nil && err == gorm.ErrRecordNotFound {
		// 		status = entrustPool.PoolAddrStatusBind
		// 	}
		//
		// }
		// poolAddr := &entrustPool.PoolAddr{
		// 	Addr:      action.GetTbind().ReturnAddress,
		// 	PoolId:    &poolId,
		// 	Status:    &status,
		// }
		// err = tx.Create(&poolAddr).Error
		// if err != nil {
		// 	return err
		// }
		// err = job.RecordAction(trans,block,tx)
		// if err != nil {
		// 	return err
		// }
	}
	return nil
}

// ParseCoinsExecer 解析coins模块
func (job SyncBlockJob) ParseCoinsExecer(block *types.Block,tx *gorm.DB,trans *types.Transaction,logs []*types.ReceiptLog) error {
	// var action coinsTypes.CoinsAction
	// err := types.Decode(trans.Payload,&action)
	// if err != nil {
	// 	return err
	// }
	// switch action.Ty {
	// case coinsTypes.CoinsActionTransferToExec: // 委托票数
	// 	if trans.To == "16htvcBNSEA7fZhAdLJphDwQRQJaHpyHTp" {
	// 		err = job.RecordAction(trans,block,tx)
	// 		if err != nil {
	// 			return err
	// 		}
	// 	}
	// 
	// }
	return nil
}

// RecordWithdrawHistory 记录各种需要的withdraw 记录
func (job SyncBlockJob) RecordWithdrawHistory(amount int64,trans *types.Transaction,block *types.Block,tx *gorm.DB) error {
	var height,bt,im64 int64
	im64 = amount
	height = block.Height
	bt = block.BlockTime
	var wd time.Time
	wd = utils.GetDate(bt)
	return tx.Create(&entrustPool.PoolAddrWithdrawList{
		Addr:         trans.From(),
		Amount:       &im64,
		Height:       &height,
		Hash:         common.ToHex(trans.Hash()),
		WithdrawTime: &bt,
		WithdrawDate: &wd,
	}).Error
}

// RecordAction 记录各种需要的action txs
func (job SyncBlockJob) RecordAction(v *types.Transaction,block *types.Block,tx *gorm.DB,amount int64,action string) error {
	var im ,height,bt int32
	im = int32(amount)
	height = int32(block.Height)
	bt = int32(block.BlockTime)
	return tx.Create(&entrustPool.PoolActionTxs{
		From:      v.From(),
		To:        v.To,
		Amount:    &im,
		Action:    action,
		Execer:    string(v.Execer),
		Height:    &height,
		Txhash:    common.ToHex(v.Hash()),
		Blocktime: &bt,
	}).Error
}

func (job SyncBlockJob) InitConfig() error {
	var config entrustPool.PoolConfig
	var startHeight = int32(29530000)
	var fee int32
	fee = int32(10)
	err := global.GVA_DB.Table("pool_config").Where("job_name",job.JobName).First(&config).Error
	if err != nil && err != gorm.ErrRecordNotFound {
		return err
	} else if err != nil && err == gorm.ErrRecordNotFound {
		global.GVA_DB.Save(&entrustPool.PoolConfig{
			StartHeight: &startHeight,
			JobName: job.JobName,
			FeeRate:&fee,
		})
		job.JobVars.Height = int64(startHeight)
		job.JobVars.FeeRate = int64(fee)
	} else {
		if config.Height != nil && *config.Height > 0 {
			job.JobVars.Height = int64(*config.Height)
		} else {
			job.JobVars.Height = int64(startHeight)
		}
		if config.FeeRate != nil && *config.FeeRate > 0 {
			job.JobVars.FeeRate = int64(*config.FeeRate)
		} else {
			job.JobVars.FeeRate = int64(10)
		}
		if config.FeeAddr != "" {
			job.JobVars.FeeAddr = config.FeeAddr
		}
	}
	return nil
}

func (job SyncBlockJob) CheckClientIsSync() error {
	isSync, err := job.Client.IsSync(context.Background(), &types.ReqNil{})
	if err != nil {
		global.GVA_LOG.Error(job.JobName, zap.String("get IsSync err", err.Error()))
		return err
	}
	if !isSync.IsOk {
		global.GVA_LOG.Error(job.JobName, zap.String("get IsSync not ok", ""))
		return errors.New("reply not ok")
	}
	return nil
}

func (job SyncBlockJob) QueryBalance(addr string, stateHash []byte) (int, error) {
	req := &types.ReqBalance{
		Addresses:   []string{addr},
		Execer:      "ticket",
	}
	if stateHash != nil {
		req.StateHash = "0x"+ hex.EncodeToString(stateHash)
	}
	accounts, err := job.Client.GetBalance(context.Background(), req)
	if err != nil {
		return 0, err
	}
	return int(accounts.Acc[0].Frozen / 1e8), nil
}

// GetReturnAddrAmountByReceipts 获取挖矿真正的收益地址和金额
func (job *SyncBlockJob) GetReturnAddrAmountByReceipts(logs []*types.ReceiptLog) (string, int64, error) {
	var addr string
	var amount int64
	for _, v := range logs {
		if v.Ty == 8 { // LogExecDeposit
			var logAcc types.ReceiptExecAccountTransfer
			err := types.Decode(v.Log, &logAcc)
			if err != nil {
				return "", 0, err
			}
			if logAcc.ExecAddr == "" {
				return "", 0, nil
			}
			addr = logAcc.Current.Addr
			amount = (logAcc.Current.Frozen - logAcc.Prev.Frozen) / 1e8
			break
		}
	}
	return addr, amount, nil
}

func (job *SyncBlockJob) GetReturnAddrAmountByTicketId(id string) (string, int64, error) {
	reply, err := job.Client.QueryChain(context.Background(), &types.ChainExecutor{Driver: "ticket", FuncName: "TicketInfos", Param: types.Encode(&types2.TicketInfos{TicketIds: []string{id}})})
	if err != nil {
		global.GVA_LOG.Error("SyncTxsJob query ticketinfos ", zap.Any("err",err))
		return "", 0, err
	}
	var tlist types2.ReplyTicketList
	err = types.Decode(reply.GetMsg(), &tlist)
	if err != nil {
		return "", 0, err
	}
	return tlist.Tickets[0].ReturnAddress, tlist.Tickets[0].MinerValue / 1e8, nil
}