package cron

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

	"github.com/33cn/chain33/types"
	"go.uber.org/zap"
	"gorm.io/gorm"
	"courseSign/server/global"
	"courseSign/server/model/entrustPool"
	"courseSign/server/utils"
)

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

var isTicketJobRun bool

func (job TicketDailyJob) Run() {
	job.JobName = "TicketDailyJob"
	if isTicketJobRun {
		global.GVA_LOG.Info(job.JobName, zap.String("job", "already run"))
		return
	}
	isTicketJobRun = true
	global.GVA_LOG.Info(job.JobName, zap.String("job", "start"))
	t := time.Now()
	defer func() {
		isTicketJobRun = false
		global.GVA_LOG.Info(job.JobName, zap.String("cost", time.Since(t).String()))
	}()
	var err error
	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.QueryTickets()
	if err != nil {
		global.GVA_LOG.Error(job.JobName, zap.String("QueryTickets err", err.Error()))
		return
	}
	return
}

func (job TicketDailyJob) QueryTickets() error {
	jobDate := utils.GetDate(time.Now().Unix())
	for _, v:= range job.PoolMap {
		cold,err := job.GetColdAddress(v.MinerAddr)
		if err != nil && !strings.Contains(err.Error(),"ErrNotFound"){
			return err
		} else if err != nil && strings.Contains(err.Error(),"ErrNotFound") {
			global.GVA_LOG.Info(job.JobName, zap.String("QueryTickets err", err.Error()))
			err = global.GVA_DB.Model(&entrustPool.Pool{}).Where("id = ?",v.ID).Update("tickets",0).Error
			if err != nil {
				return err
			}
			continue
		}
		var tickets int
		for _,vv := range cold {
			balance, err := job.QueryBalance(vv, nil)
			if err != nil {
				return err
			}
			var ticket,pid int32
			ticket = int32(balance / 3000)
			tickets += int(ticket)
			pid = int32(v.ID)
			var rr entrustPool.PoolAddrTicketDaily
			err = global.GVA_DB.Where("addr = ? and job_date = ?",vv,jobDate).First(&rr).Error
			if err != nil && err != gorm.ErrRecordNotFound {
				return err
			} else if err != nil && err == gorm.ErrRecordNotFound {
				err = global.GVA_DB.Create(&entrustPool.PoolAddrTicketDaily{
					Addr:      vv,
					Ticket:    &ticket,
					JobDate:   &jobDate,
					PoolId:    &pid,
				}).Error
				if err != nil {
					return err
				}
			} else {
				err = global.GVA_DB.Model(&entrustPool.PoolAddrTicketDaily{}).Where("id = ?",rr.ID).Update("ticket",ticket).Error
				if err != nil {
					return err
				}
			}
		}
		err = global.GVA_DB.Model(&entrustPool.Pool{}).Where("id = ?",v.ID).Update("tickets",tickets).Error
		if err != nil {
			return err
		}
	}
	return nil
}

func (job TicketDailyJob) 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
}

func (job *TicketDailyJob) GetColdAddress(addr string) ([]string, error) {
	reply, err := job.Client.QueryChain(context.Background(), &types.ChainExecutor{
		Driver: "ticket",
		FuncName: "MinerSourceList",
		Param: types.Encode(&types.ReqString{Data: addr})})
	if err != nil {
		global.GVA_LOG.Error("TicketDailyJob query MinerSourceList ", zap.Any("err",err))
		return nil, err
	}
	var replyStrings types.ReplyStrings
	err = types.Decode(reply.GetMsg(), &replyStrings)
	if err != nil {
		global.GVA_LOG.Error("TicketDailyJob decode  ", zap.Any("err",err))
		return nil,err
	}

	return replyStrings.GetDatas(), nil
}