package chain

import (
	"context"
	"encoding/json"
	"math/big"

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

// The code below is mostly copied from go-ethereum/ethclient (see TransactionInBlock), to keep the exact same behavior as the
// normal Transaction items, but exposing the additional data obtained in the `rpcTransaction` struct`.
// Unfortunately, the functions and classes used are not exposed outside of the package.
type FullTransaction struct {
	Tx *types.Transaction
	TxExtraInfo
}

type TxExtraInfo struct {
	BlockNumber *hexutil.Big    `json:"blockNumber,omitempty"`
	BlockHash   *common.Hash    `json:"blockHash,omitempty"`
	From        *common.Address `json:"from,omitempty"`
}

func callBlockHashByTransaction(ctx context.Context, rpc *rpc.Client, number *big.Int, index uint) (common.Hash, error) {
	var json *FullTransaction
	err := rpc.CallContext(ctx, &json, "eth_getTransactionByBlockNumberAndIndex", toBlockNumArg(number), hexutil.Uint64(index))

	if err != nil {
		return common.HexToHash(""), err
	}
	if json == nil {
		return common.HexToHash(""), ethereum.NotFound
	}
	return *json.BlockHash, nil
}

func (tx *FullTransaction) UnmarshalJSON(msg []byte) error {
	if err := json.Unmarshal(msg, &tx.Tx); err != nil {
		return err
	}
	return json.Unmarshal(msg, &tx.TxExtraInfo)
}

func toBlockNumArg(number *big.Int) string {
	if number == nil {
		return "latest"
	}
	pending := big.NewInt(-1)
	if number.Cmp(pending) == 0 {
		return "pending"
	}
	finalized := big.NewInt(int64(rpc.FinalizedBlockNumber))
	if number.Cmp(finalized) == 0 {
		return "finalized"
	}
	safe := big.NewInt(int64(rpc.SafeBlockNumber))
	if number.Cmp(safe) == 0 {
		return "safe"
	}
	return hexutil.EncodeBig(number)
}