from typing import Union, Sequence

from starkware.starknet.definitions.transaction_type import TransactionType as TT
from starkware.starknet.public.abi import get_selector_from_name
from starkware.starknet.services.api.contract_definition import CONSTRUCTOR_SELECTOR
from starkware.starknet.services.api.gateway.transaction import (
    InvokeFunction as IF,
    Deploy as D,
    Transaction as T,
)
from starkware.starknet.services.api.gateway.transaction_hash import (
    calculate_transaction_hash,
)

from starknet_py.net.models.chains import StarknetChainId
from starknet_py.utils.crypto.facade import pedersen_hash
from starknet_py.utils.docs import as_our_module

InvokeFunction = as_our_module(IF)
Deploy = as_our_module(D)
Transaction = as_our_module(T)
TransactionType = as_our_module(TT)


def compute_invoke_hash(
    contract_address: int,
    entry_point_selector: Union[int, str],
    calldata: Sequence[int],
    chain_id: StarknetChainId,
) -> int:
    """
    Computes invocation hash.

    :param contract_address: int
    :param entry_point_selector: Union[int, str]
    :param calldata: Sequence[int]
    :param chain_id: StarknetChainId
    :return: calculated hash
    """
    if isinstance(entry_point_selector, str):
        entry_point_selector = get_selector_from_name(entry_point_selector)

    return calculate_transaction_hash(
        tx_type=TransactionType.INVOKE_FUNCTION,
        contract_address=contract_address,
        entry_point_selector=entry_point_selector,
        calldata=calldata,
        chain_id=chain_id.value,
        hash_function=pedersen_hash,
    )


def compute_deploy_hash(
    contract_address: int,
    calldata: Sequence[int],
    chain_id: StarknetChainId,
) -> int:
    """

    :param contract_address: int
    :param calldata: Sequence[int] (constructor arguments)
    :param chain_id: StarknetChainId
    :return: calculated hash
    """
    return calculate_transaction_hash(
        tx_type=TransactionType.DEPLOY,
        contract_address=contract_address,
        entry_point_selector=CONSTRUCTOR_SELECTOR,
        calldata=calldata,
        chain_id=chain_id.value,
        hash_function=pedersen_hash,
    )
