package common

import (
	"errors"

	storetypes "github.com/cosmos/cosmos-sdk/store/types"
	sdk "github.com/cosmos/cosmos-sdk/types"
	"github.com/ethereum/go-ethereum/accounts/abi"
	"github.com/ethereum/go-ethereum/core/vm"
	"github.com/evmos/ethermint/x/evm/statedb"
)

type PrecompileCommon interface {
	Abi() *abi.ABI
	IsTx(string) bool
	KVGasConfig() storetypes.GasConfig
}

func InitializePrecompileCall(
	p PrecompileCommon,
	evm *vm.EVM,
	contract *vm.Contract,
	readonly bool,
) (
	ctx sdk.Context,
	stateDB *statedb.StateDB,
	method *abi.Method,
	initialGas storetypes.Gas,
	args []interface{},
	err error,
) {
	// parse input
	if len(contract.Input) < 4 {
		return sdk.Context{}, nil, nil, uint64(0), nil, vm.ErrExecutionReverted
	}
	method, err = p.Abi().MethodById(contract.Input[:4])
	if err != nil {
		return sdk.Context{}, nil, nil, uint64(0), nil, vm.ErrExecutionReverted
	}
	args, err = method.Inputs.Unpack(contract.Input[4:])
	if err != nil {
		return sdk.Context{}, nil, nil, uint64(0), nil, err
	}
	// readonly check
	if readonly && p.IsTx(method.Name) {
		return sdk.Context{}, nil, nil, uint64(0), nil, errors.New(ErrWriteOnReadOnly)
	}
	// get state db and context
	stateDB, ok := evm.StateDB.(*statedb.StateDB)
	if !ok {
		return sdk.Context{}, nil, nil, uint64(0), nil, errors.New(ErrGetStateDB)
	}
	ctx, err = stateDB.GetCachedContextForPrecompile()
	if err != nil {
		return sdk.Context{}, nil, nil, uint64(0), nil, err
	}
	// initial gas
	initialGas = ctx.GasMeter().GasConsumed()
	ctx = ctx.WithKVGasConfig(p.KVGasConfig())

	return ctx, stateDB, method, initialGas, args, nil
}