package enx

import (
	"encoding/hex"
	"fmt"
	"pool/internal/db"
	"pool/internal/gbt/coin"
	"pool/internal/gbt/enx/templatemanager"
	"pool/internal/msg"
	"sync"
	"sync/atomic"
	"time"

	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
	"github.com/kaspanet/kaspad/domain/consensus/utils/pow"
	"github.com/kaspanet/kaspad/util/difficulty"
	"go.uber.org/zap"
)

const GBT_ENX_VERSION string = "enx v1.15.2"

type EnxAddrConfig struct {
	Addr string `json:"addr"`
}

type EnxConfig struct {
	Enx EnxAddrConfig `json:"enx"`
}

type GbtEnxContext struct {
	Config EnxConfig
	GbtCtx *coin.GbtContext

	last_time time.Time
	last_gbt  EnxGbtTemplate

	Submits float64

	addressIndex int

	new_block_chan  chan int
	new_block_index int

	last_height uint32
}

var logg *zap.Logger
var GbtEnxCtx GbtEnxContext

// Kaspa GetBlockTemplate 结构体
type EnxGbtTemplate struct {
	Block struct {
		Header struct {
			Version              int        `json:"version"`
			ParentsByLevel       [][]string `json:"parents_by_level"`
			HashMerkleRoot       string     `json:"hash_merkle_root"`
			AcceptedIdMerkleRoot string     `json:"accepted_id_merkle_root"`
			UtxoCommitment       string     `json:"utxo_commitment"`
			Timestamp            uint64     `json:"timestamp"`
			Bits                 uint32     `json:"bits"`
			Nonce                uint64     `json:"nonce"`
			DaaScore             uint64     `json:"daa_score"`
			BlueWork             [3]uint64  `json:"blue_work"` // 可改为 big.Int
			BlueScore            uint64     `json:"blue_score"`
			PruningPoint         string     `json:"pruning_point"`
		} `json:"header"`

		Transactions []struct {
			Version      int    `json:"version"`
			LockTime     uint64 `json:"lock_time"`
			SubnetworkId string `json:"subnetwork_id"`
			Gas          uint64 `json:"gas"`
			PayLoad      string `json:"payload"`
			Mass         uint64 `json:"mass"`

			Inputs  []struct{} `json:"inputs"`
			Outputs []struct{} `json:"outputs"`

			VerboseData interface{} `json:"verbose_data"`
		} `json:"transactions"`
	} `json:"block"`

	IsSync bool `json:"is_sync"`
}

type BlockCheckData struct {
	Height  int
	Nonce   string
	User    string
	Miner   string
	MinerId string
	Hash    string
	SubIdx  int
}

type PushBlkNewMsg struct {
	Coin   string `json:"coin"`
	Height int    `json:"height"`
	Nonce  string `json:"nonce"`
}

type EnxJob struct {
	JobID uint32
	Job   *externalapi.DomainBlock
	State *pow.State
}

type EnxContext struct {
	GbtCtx *coin.GbtContext

	SLock sync.Mutex
	JobID uint32
	Jobs  []EnxJob

	LastHeight uint64

	Submits float64
}

var enxCtx EnxContext

func update_block_confirm(gbt *GbtEnxContext) {}

var hashesTried uint64

func get_blocktemplate(gbt *GbtEnxContext, mineWhenNotSynced bool) (*externalapi.DomainBlock, *pow.State) {
	tryCount := 0
	const sleepTime = 500 * time.Millisecond
	const sleepTimeWhenNotSynced = 5 * time.Second

	for {
		tryCount++

		shouldLog := (tryCount-1)%10 == 0
		template, state, isSynced := templatemanager.Get()
		if template == nil {
			if shouldLog {
				logg.Info("Waiting for the initial template")
			}
			time.Sleep(sleepTime)
			continue
		}
		if !isSynced && !mineWhenNotSynced {
			if shouldLog {
				logg.Warn("Kaspad is not synced. Skipping current block template")
			}
			time.Sleep(sleepTimeWhenNotSynced)
			continue
		}

		return template, state
	}
}

func get_gbt_msg(gbt *GbtEnxContext) []byte {
	// 这里从节点获取区块模版(block template)
	dagInfoMsg, err := gbt.GbtCtx.ClientEnx.GetBlockDAGInfo()
	if err != nil {
		logg.Info("[gbt]", zap.String("GetBlockDAGInfo ", err.Error()))
		return nil
	}
	height := dagInfoMsg.VirtualDAAScore
	if err != nil {
		logg.Info("[gbt]", zap.String("GetBlockCount ", err.Error()))
		return nil
	}
	height = height + 1
	block, state := getBlockForMining(mineWhenNotSynced)
	//state.Nonce = nonce
	state.Nonce = 0
	atomic.AddUint64(&hashesTried, 1)

	var job msg.EnxStratumJob
	enxCtx.SLock.Lock()
	job.Job_id = 0
	job.Gbt_id = enxCtx.JobID
	enxCtx.SLock.Unlock()

	job.CurTime = uint64(state.Timestamp)
	job.Target = hex.EncodeToString(state.Target.Bytes())
	job.Bits = difficulty.BigToCompact(&(state.Target))
	job.Nonce = ""
	job.Extranonce1 = ""
	job.Extranonce2_size = 6
	job.Extranonce2 = ""
	job.Data = hex.EncodeToString(state.GetPrePowHash())
	job.PrevBlockHash = fmt.Sprint("%s", block.Header.DirectParents())
	job.Height = block.Header.BlueScore()

	return []byte{}
}

func getBlockForMining(mineWhenNotSynced bool) (*externalapi.DomainBlock, *pow.State) {
	tryCount := 0

	const sleepTime = 500 * time.Millisecond
	const sleepTimeWhenNotSynced = 5 * time.Second

	for {
		tryCount++

		shouldLog := (tryCount-1)%10 == 0
		template, state, isSynced := templatemanager.Get()
		if template == nil {
			if shouldLog {
				logg.Info("Waiting for the initial template")
			}
			time.Sleep(sleepTime)
			continue
		}
		if !isSynced && !mineWhenNotSynced {
			if shouldLog {
				logg.Warn("Kaspad is not synced. Skipping current block template")
			}
			time.Sleep(sleepTimeWhenNotSynced)
			continue
		}

		return template, state
	}
}

func gbt_notify_running(gbt *GbtEnxContext) {}

func gbt_running(gbt *GbtEnxContext) {}

func enxInit(config *EnxConfig) {}

func Init(GbtCtx *coin.GbtContext, DbCtx *db.DbContext) {}

func Start() {
	go gbt_running(&GbtEnxCtx)
	go gbt_notify_running(&GbtEnxCtx)
	go submit_block_running(&GbtEnxCtx)
}

func Stop() {
	defer close(GbtEnxCtx.new_block_chan)
}

func new_block_into_db(block *GbtEnxContext, user string, miner string, minerid string, height int64, nonce string, hash string, subidx int64) bool {
	return true
}

func submit_block_running(block *GbtEnxContext) {}

func new_job_from_gbt(gbt *GbtEnxContext, rxmsg *msg.GbtMsg) []byte { return []byte{} }