package mockengine

import (
	"fmt"
	"github.com/exchain/go-exchain/exchain"
	"github.com/exchain/go-exchain/exchain/chaindb"
	nebulav1 "github.com/exchain/go-exchain/exchain/protocol/gen/go/nebula/v1"
	"github.com/exchain/go-exchain/exchain/wrapper"
	log "github.com/sirupsen/logrus"
)

type MockEngine struct {
	chain chaindb.ChainDB
}

func (m MockEngine) Start() error {
	return nil
}

func (m MockEngine) NewPayload(params exchain.PayloadParams) (exchain.ExecutionResult, error) {
	parent, err := m.chain.GetBlockByLabel(chaindb.ExChainBlockLatest)
	if err != nil {
		return exchain.ExecutionResult{}, err
	}
	wParent := wrapper.NewBlkWrapper(parent)
	header := &nebulav1.BlockHeader{
		Height:     parent.Header.Height + 1,
		ParentHash: wParent.Hash().Bytes(),
		Timestamp:  params.Timestamp,
		Proposer:   params.Proposer.Bytes(),
		L1Hash:     params.L1Info.BlockHash.Bytes(),
		L1Height:   params.L1Info.Number,
		AppRoot:    make([]byte, 0),
	}
	receipts, err := m.ProcessTx(header, params.Transactions)
	if err != nil {
		log.WithError(err).Error("failed to process txs")
		return exchain.ExecutionResult{}, err
	}

	result := exchain.ExecutionResult{
		Payload: &nebulav1.Block{
			Header:       header,
			Transactions: params.Transactions,
		},
		Receipts: receipts,
	}
	return result, nil
}

func (m MockEngine) ProcessPayload(block *nebulav1.Block) (exchain.ExecutionResult, error) {
	genesis := block.Header.Height == 0
	if !genesis {
		parent, err := m.chain.GetBlockByLabel(chaindb.ExChainBlockLatest)
		if err != nil {
			return exchain.ExecutionResult{}, err
		}
		if parent.Header.Height+1 != block.Header.Height {
			return exchain.ExecutionResult{}, fmt.Errorf("invalid block height")
		}
	}
	receipts, err := m.ProcessTx(block.Header, block.Transactions)
	if err != nil {
		return exchain.ExecutionResult{}, err
	}

	return exchain.ExecutionResult{
		Payload:  block,
		Receipts: receipts,
	}, nil
}

func NewEngine(chain chaindb.ChainDB) exchain.Engine {
	return &MockEngine{
		chain: chain,
	}
}
