from collections import namedtuple
from enum import Enum, IntEnum
from typing import Dict

from eth_typing import ChecksumAddress, HexAddress, HexStr
from eth_utils import keccak

from raiden_contracts.utils.type_aliases import ChainID, Locksroot

# The last digit is supposed to be zero always. See `RELEASE.rst`.
CONTRACTS_VERSION = "0.50.0"
CORUSCANT_VERSION = "0.40.0"
BESPIN_VERSION = "0.37.0"
ALDERAAN_VERSION = "0.37.0"

PRECOMPILED_DATA_FIELDS = ["abi", "bin", "bin-runtime", "metadata"]

# Contract names
CONTRACT_HUMAN_STANDARD_TOKEN = "HumanStandardToken"
CONTRACT_TOKEN_NETWORK_REGISTRY = "TokenNetworkRegistry"
CONTRACT_TOKEN_NETWORK = "TokenNetwork"
CONTRACT_SECRET_REGISTRY = "SecretRegistry"
CONTRACT_CUSTOM_TOKEN = "CustomToken"
CONTRACT_CUSTOM_TOKEN_NO_DECIMALS = "CustomTokenNoDecimals"
CONTRACT_MONITORING_SERVICE = "MonitoringService"
CONTRACT_SERVICE_REGISTRY = "ServiceRegistry"
CONTRACT_USER_DEPOSIT = "UserDeposit"
CONTRACT_ONE_TO_N = "OneToN"
CONTRACT_DEPOSIT = "Deposit"

# Timeouts
TEST_SETTLE_TIMEOUT = 8 * 15  # Timeout in seconds

DEPLOY_SETTLE_TIMEOUT = 1000 * 15  # ~ 4 hours

# Temporary deposit limits for the Red Eyes release in WEI
MAX_ETH_CHANNEL_PARTICIPANT = int(0.075 * 10**18)
MAX_ETH_TOKEN_NETWORK = int(250 * 10**18)

# Special hashes
LOCKSROOT_OF_NO_LOCKS = Locksroot(keccak(b""))
EMPTY_ADDRESS = ChecksumAddress(HexAddress(HexStr("0x0000000000000000000000000000000000000000")))

# Event names
# TokenNetworkRegistry
EVENT_TOKEN_NETWORK_CREATED = "TokenNetworkCreated"

# TokenNetwork
EVENT_DEPRECATION_SWITCH = "DeprecationSwitch"

# SecretRegistry
EVENT_SECRET_REVEALED = "SecretRevealed"

# EndpointRegistry
EVENT_ADDRESS_REGISTERED = "AddressRegistered"

# ServiceRegistry
EVENT_REGISTERED_SERVICE = "RegisteredService"


class ChannelEvent(str, Enum):
    OPENED = "ChannelOpened"
    DEPOSIT = "ChannelNewDeposit"
    WITHDRAW = "ChannelWithdraw"
    BALANCE_PROOF_UPDATED = "NonClosingBalanceProofUpdated"
    CLOSED = "ChannelClosed"
    SETTLED = "ChannelSettled"
    UNLOCKED = "ChannelUnlocked"
    DEPRECATED = "DeprecationSwitch"


class ParticipantInfoIndex(IntEnum):
    """Index for accessing fields in return value of TokenNetwork.getChannelParticipantInfo."""

    DEPOSIT = 0
    WITHDRAWN = 1
    IS_CLOSER = 2
    BALANCE_HASH = 3
    NONCE = 4
    LOCKSROOT = 5
    LOCKED_AMOUNT = 6


class ChannelState(IntEnum):
    """Meaning of values returned by TokenNetwork.getChannelState"""

    NONEXISTENT = 0
    OPENED = 1
    CLOSED = 2
    SETTLED = 3
    REMOVED = 4


class MessageTypeId(IntEnum):
    """Message types, as used by the TokenNetwork contract"""

    BALANCE_PROOF = 1
    BALANCE_PROOF_UPDATE = 2
    WITHDRAW = 3
    COOPERATIVE_SETTLE = 4
    IOU = 5
    MSReward = 6


class MonitoringServiceEvent(str, Enum):
    """Message types used by MonitoringService contract"""

    NEW_BALANCE_PROOF_RECEIVED = "NewBalanceProofReceived"
    REWARD_CLAIMED = "RewardClaimed"


class UserDepositEvent(str, Enum):
    """Message types used by UserDeposit contract"""

    BALANCE_REDUCED = "BalanceReduced"
    WITHDRAW_PLANNED = "WithdrawPlanned"


class OneToNEvent(str, Enum):
    """Message types used by OneToN contract"""

    CLAIMED = "Claimed"


class DeploymentModule(Enum):
    """Groups of contracts that are deployed together"""

    RAIDEN = "raiden"
    SERVICES = "services"
    ALL = "all"


# Network configurations
START_QUERY_BLOCK_KEY = "DefaultStartBlock"

ID_TO_CHAINNAME: Dict[ChainID, str] = {
    ChainID(-5): "goerli_unstable",
    ChainID(1): "mainnet",
    ChainID(3): "ropsten",
    ChainID(4): "rinkeby",
    ChainID(5): "goerli",
    ChainID(42): "kovan",
    ChainID(627): "smoketest",
    ChainID(42161): "arbitrum-one",
    ChainID(421611): "rinkeby-arbitrum",
}

CHAINNAME_TO_ID: Dict[str, ChainID] = {name: id for id, name in ID_TO_CHAINNAME.items()}

# ContractNames


ContractListEntry = namedtuple("ContractListEntry", "module name")

CONTRACT_LIST = [
    ContractListEntry(module=DeploymentModule.RAIDEN, name=CONTRACT_SECRET_REGISTRY),
    ContractListEntry(module=DeploymentModule.RAIDEN, name=CONTRACT_TOKEN_NETWORK_REGISTRY),
    ContractListEntry(module=DeploymentModule.RAIDEN, name=CONTRACT_TOKEN_NETWORK),
    ContractListEntry(module=DeploymentModule.SERVICES, name=CONTRACT_SERVICE_REGISTRY),
    ContractListEntry(module=DeploymentModule.SERVICES, name=CONTRACT_MONITORING_SERVICE),
    ContractListEntry(module=DeploymentModule.SERVICES, name=CONTRACT_ONE_TO_N),
    ContractListEntry(module=DeploymentModule.SERVICES, name=CONTRACT_USER_DEPOSIT),
]
