package wdt

import (
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/common/hexutil"
	"github.com/ethereum/go-ethereum/core/rawdb"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/log"
	"github.com/ethereum/go-ethereum/trie"
	"github.com/ethereum/go-ethereum/triedb"
	nebulav1 "github.com/exchain/go-exchain/exchain/protocol/gen/go/nebula/v1"
	"github.com/exchain/go-exchain/exchain/wrapper"
)

// proofList implements ethdb.KeyValueWriter and collects the proofs as
// hex-strings for delivery to rpc-caller.
type proofList []string

func (n *proofList) Put(key []byte, value []byte) error {
	*n = append(*n, hexutil.Encode(value))
	return nil
}

func (n *proofList) Delete(key []byte) error {
	panic("not supported")
}

type WDT struct {
	id *trie.ID
	st *trie.StateTrie
}

func NewWdt() *WDT {
	virAccount := common.HexToAddress("0xffee")
	id := trie.StorageTrieID(common.Hash{}, crypto.Keccak256Hash(virAccount.Bytes()), common.Hash{})
	trieDB := triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil)
	st, err := trie.NewStateTrie(id, trieDB)
	if err != nil {
		log.Error("failed to create state trie", "err", err)
		return nil
	}

	return &WDT{
		id: id,
		st: st,
	}

}

func (w *WDT) AddTx(tx *nebulav1.Transaction) error {
	// key is withdrawalHash
	// value is []byte("1")
	item := wrapper.NewTxWrapper(tx).WithdrawalHash()
	addr := common.Address{}
	return w.st.UpdateStorage(addr, item.Bytes(), []byte("1"))
}

func (w *WDT) Proof(key []byte) ([]string, error) {
	var proof proofList
	if err := w.st.Prove(key, &proof); err != nil {
		return nil, err
	}
	return proof, nil
}

func (w *WDT) Root() common.Hash {
	// root is the root hash of the trie
	return w.st.Hash()
}
