from .base import BaseError, claim_id


class UserInputError(BaseError):
    """
    User input errors.
    """


class CommandError(UserInputError):
    """
    Errors preparing to execute commands.
    """


class CommandDoesNotExistError(CommandError):

    def __init__(self, command):
        self.command = command
        super().__init__(f"Command '{command}' does not exist.")


class CommandDeprecatedError(CommandError):

    def __init__(self, command):
        self.command = command
        super().__init__(f"Command '{command}' is deprecated.")


class CommandInvalidArgumentError(CommandError):

    def __init__(self, argument, command):
        self.argument = argument
        self.command = command
        super().__init__(f"Invalid argument '{argument}' to command '{command}'.")


class CommandTemporarilyUnavailableError(CommandError):
    """
    Such as waiting for required components to start.
    """

    def __init__(self, command):
        self.command = command
        super().__init__(f"Command '{command}' is temporarily unavailable.")


class CommandPermanentlyUnavailableError(CommandError):
    """
    such as when required component was intentionally configured not to start.
    """

    def __init__(self, command):
        self.command = command
        super().__init__(f"Command '{command}' is permanently unavailable.")


class InputValueError(UserInputError, ValueError):
    """
    Invalid argument value provided to command.
    """


class GenericInputValueError(InputValueError):

    def __init__(self, value, argument):
        self.value = value
        self.argument = argument
        super().__init__(f"The value '{value}' for argument '{argument}' is not valid.")


class InputValueIsNoneError(InputValueError):

    def __init__(self, argument):
        self.argument = argument
        super().__init__(f"None or null is not valid value for argument '{argument}'.")


class ConfigurationError(BaseError):
    """
    Configuration errors.
    """


class ConfigWriteError(ConfigurationError):
    """
    When writing the default config fails on startup, such as due to permission issues.
    """

    def __init__(self, path):
        self.path = path
        super().__init__(f"Cannot write configuration file '{path}'.")


class ConfigReadError(ConfigurationError):
    """
    Can't open the config file user provided via command line args.
    """

    def __init__(self, path):
        self.path = path
        super().__init__(f"Cannot find provided configuration file '{path}'.")


class ConfigParseError(ConfigurationError):
    """
    Includes the syntax error / line number to help user fix it.
    """

    def __init__(self, path):
        self.path = path
        super().__init__(f"Failed to parse the configuration file '{path}'.")


class ConfigMissingError(ConfigurationError):

    def __init__(self, path):
        self.path = path
        super().__init__(f"Configuration file '{path}' is missing setting that has no default / fallback.")


class ConfigInvalidError(ConfigurationError):

    def __init__(self, path):
        self.path = path
        super().__init__(f"Configuration file '{path}' has setting with invalid value.")


class NetworkError(BaseError):
    """
    **Networking**
    """


class NoInternetError(NetworkError):

    def __init__(self):
        super().__init__("No internet connection.")


class NoUPnPSupportError(NetworkError):

    def __init__(self):
        super().__init__("Router does not support UPnP.")


class WalletError(BaseError):
    """
    **Wallet Errors**
    """


class TransactionRejectedError(WalletError):

    def __init__(self):
        super().__init__("Transaction rejected, unknown reason.")


class TransactionFeeTooLowError(WalletError):

    def __init__(self):
        super().__init__("Fee too low.")


class TransactionInvalidSignatureError(WalletError):

    def __init__(self):
        super().__init__("Invalid signature.")


class InsufficientFundsError(WalletError):
    """
    determined by wallet prior to attempting to broadcast a tx; this is different for example from a TX
    being created and sent but then rejected by lbrycrd for unspendable utxos.
    """

    def __init__(self):
        super().__init__("Not enough funds to cover this transaction.")


class ChannelKeyNotFoundError(WalletError):

    def __init__(self):
        super().__init__("Channel signing key not found.")


class ChannelKeyInvalidError(WalletError):
    """
    For example, channel was updated but you don't have the updated key.
    """

    def __init__(self):
        super().__init__("Channel signing key is out of date.")


class DataDownloadError(WalletError):

    def __init__(self):
        super().__init__("Failed to download blob. *generic*")


class ResolveError(WalletError):

    def __init__(self, url):
        self.url = url
        super().__init__(f"Failed to resolve '{url}'.")


class ResolveTimeoutError(WalletError):

    def __init__(self, url):
        self.url = url
        super().__init__(f"Failed to resolve '{url}' within the timeout.")


class ResolveCensoredError(WalletError):

    def __init__(self, url, censor_hash):
        self.url = url
        self.censor_hash = censor_hash
        super().__init__(f"Resolve of '{url}' was censored by channel with claim id '{claim_id(censor_hash)}'.")


class KeyFeeAboveMaxAllowedError(WalletError):

    def __init__(self, message):
        self.message = message
        super().__init__(f"{message}")


class InvalidPasswordError(WalletError):

    def __init__(self):
        super().__init__("Password is invalid.")


class IncompatibleWalletServerError(WalletError):

    def __init__(self, server, port):
        self.server = server
        self.port = port
        super().__init__(f"'{server}:{port}' has an incompatibly old version.")


class ServerPaymentInvalidAddressError(WalletError):

    def __init__(self, address):
        self.address = address
        super().__init__(f"Invalid address from wallet server: '{address}' - skipping payment round.")


class ServerPaymentWalletLockedError(WalletError):

    def __init__(self):
        super().__init__("Cannot spend funds with locked wallet, skipping payment round.")


class ServerPaymentFeeAboveMaxAllowedError(WalletError):

    def __init__(self, daily_fee, max_fee):
        self.daily_fee = daily_fee
        self.max_fee = max_fee
        super().__init__(f"Daily server fee of {daily_fee} exceeds maximum configured of {max_fee} LBC.")


class BlobError(BaseError):
    """
    **Blobs**
    """


class BlobNotFoundError(BlobError):

    def __init__(self):
        super().__init__("Blob not found.")


class BlobPermissionDeniedError(BlobError):

    def __init__(self):
        super().__init__("Permission denied to read blob.")


class BlobTooBigError(BlobError):

    def __init__(self):
        super().__init__("Blob is too big.")


class BlobEmptyError(BlobError):

    def __init__(self):
        super().__init__("Blob is empty.")


class BlobFailedDecryptionError(BlobError):

    def __init__(self):
        super().__init__("Failed to decrypt blob.")


class CorruptBlobError(BlobError):

    def __init__(self):
        super().__init__("Blobs is corrupted.")


class BlobFailedEncryptionError(BlobError):

    def __init__(self):
        super().__init__("Failed to encrypt blob.")


class DownloadCancelledError(BlobError):

    def __init__(self):
        super().__init__("Download was canceled.")


class DownloadSDTimeoutError(BlobError):

    def __init__(self, download):
        self.download = download
        super().__init__(f"Failed to download sd blob {download} within timeout.")


class DownloadDataTimeoutError(BlobError):

    def __init__(self, download):
        self.download = download
        super().__init__(f"Failed to download data blobs for sd hash {download} within timeout.")


class InvalidStreamDescriptorError(BlobError):

    def __init__(self, message):
        self.message = message
        super().__init__(f"{message}")


class InvalidDataError(BlobError):

    def __init__(self, message):
        self.message = message
        super().__init__(f"{message}")


class InvalidBlobHashError(BlobError):

    def __init__(self, message):
        self.message = message
        super().__init__(f"{message}")


class ComponentError(BaseError):
    """
    **Components**
    """


class ComponentStartConditionNotMetError(ComponentError):

    def __init__(self, components):
        self.components = components
        super().__init__(f"Unresolved dependencies for: {components}")


class ComponentsNotStartedError(ComponentError):

    def __init__(self, message):
        self.message = message
        super().__init__(f"{message}")


class CurrencyExchangeError(BaseError):
    """
    **Currency Exchange**
    """


class InvalidExchangeRateResponseError(CurrencyExchangeError):

    def __init__(self, source, reason):
        self.source = source
        self.reason = reason
        super().__init__(f"Failed to get exchange rate from {source}: {reason}")


class CurrencyConversionError(CurrencyExchangeError):

    def __init__(self, message):
        self.message = message
        super().__init__(f"{message}")


class InvalidCurrencyError(CurrencyExchangeError):

    def __init__(self, currency):
        self.currency = currency
        super().__init__(f"Invalid currency: {currency} is not a supported currency.")