package multisend

import (
	"context"
	"crypto/ecdsa"
	"math/big"

	"github.com/ethereum/go-ethereum"
	"github.com/ethereum/go-ethereum/common/hexutil"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/ethclient"

	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/crypto"

	"code.wuban.net.cn/multisend/internal/logging"
)

var toAddress common.Address = common.HexToAddress("0x0071B39fd266F8aeF392fb50F078A233b2218a0b")

func init() {
	if err := RegisterClientFactory("ethclient", NewEthClientFactory()); err != nil {
		panic(err)
	}
}

type EthClientFactory struct{}

type EthClient struct {
	PrivateKey *ecdsa.PrivateKey
	FromAddr   common.Address
	NodeUrl    string
	Nonce      uint64
	GasPrice   *big.Int
	ChainId    *big.Int
	GasLimit   uint64
	logger     logging.Logger
}

var _ ClientFactory = (*EthClientFactory)(nil)
var _ Client = (*EthClient)(nil)

func NewEthClientFactory() *EthClientFactory {
	return &EthClientFactory{}
}

func (f *EthClientFactory) ValidateConfig(cfg Config) error {
	return nil
}

func (f *EthClientFactory) NewClient(cfg Config) (Client, error) {

	sendTxPrivatekeyAsECDSA, err := crypto.HexToECDSA(cfg.SendTxPrivateKey)
	if err != nil {
		panic(err)
	}
	sendTxPublicKey := sendTxPrivatekeyAsECDSA.Public()
	sendTxPublicKeyECDSA, ok := sendTxPublicKey.(*ecdsa.PublicKey)
	if !ok {
		panic("publicKey.(*ecdsa.PublicKey) not ok")
	}
	sendTxFromAddress := crypto.PubkeyToAddress(*sendTxPublicKeyECDSA)

	buildTxParam := EthClient{
		PrivateKey: sendTxPrivatekeyAsECDSA,
		FromAddr:   sendTxFromAddress,
		NodeUrl:    "http://13.40.31.153:8545",
		logger:     logging.NewLogrusLogger("EthClient"),
	}

	cli, err := ethclient.Dial(buildTxParam.NodeUrl)
	if err != nil {
		panic(err)
	}

	nonce, err := cli.PendingNonceAt(context.Background(), buildTxParam.FromAddr)
	if err != nil {
		panic(err)
	}

	gasPrice, err := cli.SuggestGasPrice(context.Background())
	if err != nil {
		panic(err)
	}

	ChainId, err := cli.NetworkID(context.Background())
	if err != nil {
		panic(err)
	}

	buildTxParam.Nonce = nonce
	buildTxParam.GasPrice = gasPrice
	buildTxParam.ChainId = ChainId
	buildTxParam.GasLimit = 2000000

	return &buildTxParam, nil
}

func (c *EthClient) GenerateTx() (*types.Transaction, error) {

	select {
	case txRootHashList := <-originalTxsHashQueue:
		if c.GasLimit == 0 {
			cli, err := ethclient.Dial(c.NodeUrl)
			if err != nil {
				panic(err)
			}

			gasLimit, err := cli.EstimateGas(context.Background(), ethereum.CallMsg{
				To:   &toAddress,
				Data: *txRootHashList,
			})
			if err != nil {
				return nil, err
			}

			c.GasLimit = gasLimit

		}

		tx, err := buildSendTx(c.Nonce, toAddress, big.NewInt(0), c.GasLimit, c.GasPrice, *txRootHashList, c.ChainId, c.PrivateKey)
		if err != nil {
			return nil, err
		}

		data, err := tx.MarshalBinary()
		if err != nil {
			return nil, err
		}

		args := hexutil.Encode(data)

		c.logger.Info("build tx", "nonce", c.Nonce, "TxAsHex", args)

		c.Nonce += 1

		return tx, nil

	}
}

func (c *EthClient) BuildTx(data *[]byte) (*types.Transaction, error) {

	tx, err := buildSendTx(c.Nonce, toAddress, big.NewInt(0), c.GasLimit, c.GasPrice, *data, c.ChainId, c.PrivateKey)
	if err != nil {
		return nil, err
	}

	txAsBytes, err := tx.MarshalBinary()
	if err != nil {
		return nil, err
	}

	txAsHex := hexutil.Encode(txAsBytes)

	c.logger.Info("build tx", "nonce", c.Nonce, "TxAsHex", txAsHex)

	c.Nonce += 1

	return tx, nil

}
