import os
import yaml
import click
import logging
from bitshares import BitShares
from bitshares.exceptions import WrongMasterPasswordException
from bitshares.instance import set_shared_bitshares_instance
from functools import update_wrapper
from .ui import print_message

log = logging.getLogger(__name__)


def verbose(f):
    """ Add verbose flags and add logging handlers
    """

    @click.pass_context
    def new_func(ctx, *args, **kwargs):
        global log
        verbosity = ["critical", "error", "warn", "info", "debug"][
            int(min(ctx.obj.get("verbose", 0), 4))
        ]
        log.setLevel(getattr(logging, verbosity.upper()))
        formatter = logging.Formatter(
            "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
        )
        ch = logging.StreamHandler()
        ch.setLevel(getattr(logging, verbosity.upper()))
        ch.setFormatter(formatter)
        log.addHandler(ch)

        # GrapheneAPI logging
        if ctx.obj.get("verbose", 0) > 4:
            verbosity = ["critical", "error", "warn", "info", "debug"][
                int(min(ctx.obj.get("verbose", 4) - 4, 4))
            ]
            log = logging.getLogger("grapheneapi")
            log.setLevel(getattr(logging, verbosity.upper()))
            log.addHandler(ch)

        if ctx.obj.get("verbose", 0) > 8:
            verbosity = ["critical", "error", "warn", "info", "debug"][
                int(min(ctx.obj.get("verbose", 8) - 8, 4))
            ]
            log = logging.getLogger("graphenebase")
            log.setLevel(getattr(logging, verbosity.upper()))
            log.addHandler(ch)

        return ctx.invoke(f, *args, **kwargs)

    return update_wrapper(new_func, f)


def offline(f):
    """ This decorator allows you to access ``ctx.bitshares`` which is
        an instance of BitShares with ``offline=True``.
    """

    @click.pass_context
    @verbose
    def new_func(ctx, *args, **kwargs):
        ctx.obj["offline"] = True
        ctx.bitshares = BitShares(**ctx.obj)
        ctx.blockchain = ctx.bitshares
        ctx.bitshares.set_shared_instance()
        return ctx.invoke(f, *args, **kwargs)

    return update_wrapper(new_func, f)


def customchain(**kwargsChain):
    """ This decorator allows you to access ``ctx.bitshares`` which is
        an instance of BitShares. But in contrast to @chain, this is a
        decorator that expects parameters that are directed right to
        ``BitShares()``.

        ... code-block::python

                @main.command()
                @click.option("--worker", default=None)
                @click.pass_context
                @customchain(foo="bar")
                @unlock
                def list(ctx, worker):
                   print(ctx.obj)

    """

    def wrap(f):
        @click.pass_context
        @verbose
        def new_func(ctx, *args, **kwargs):
            newoptions = ctx.obj
            newoptions.update(kwargsChain)
            ctx.bitshares = BitShares(**newoptions)
            ctx.blockchain = ctx.bitshares
            set_shared_bitshares_instance(ctx.bitshares)
            return ctx.invoke(f, *args, **kwargs)

        return update_wrapper(new_func, f)

    return wrap


def chain(f):
    """ This decorator allows you to access ``ctx.bitshares`` which is
        an instance of BitShares.
    """

    @click.pass_context
    @verbose
    def new_func(ctx, *args, **kwargs):
        ctx.bitshares = BitShares(**ctx.obj)
        ctx.blockchain = ctx.bitshares
        set_shared_bitshares_instance(ctx.bitshares)
        return ctx.invoke(f, *args, **kwargs)

    return update_wrapper(new_func, f)


def unlock(f):
    """ This decorator will unlock the wallet by either asking for a
        passphrase or taking the environmental variable ``UNLOCK``
    """

    @click.pass_context
    def new_func(ctx, *args, **kwargs):
        if not ctx.obj.get("unsigned", False):
            if ctx.bitshares.wallet.created():
                while True:
                    if "UNLOCK" in os.environ:
                        pwd = os.environ["UNLOCK"]
                    else:
                        pwd = click.prompt("Current Wallet Passphrase", hide_input=True)
                    try:
                        ctx.bitshares.wallet.unlock(pwd)
                    except WrongMasterPasswordException:
                        print_message("Incorrect Wallet passphrase!", "error")
                        continue
                    break
            else:
                print_message("No wallet installed yet. Creating ...", "warning")
                if "UNLOCK" in os.environ:
                    pwd = os.environ["UNLOCK"]
                else:
                    pwd = click.prompt(
                        "Wallet Encryption Passphrase",
                        hide_input=True,
                        confirmation_prompt=True,
                    )
                ctx.bitshares.wallet.create(pwd)
        return ctx.invoke(f, *args, **kwargs)

    return update_wrapper(new_func, f)


def configfile(f):
    """ This decorator will parse a configuration file in YAML format
        and store the dictionary in ``ctx.config``
    """

    @click.pass_context
    def new_func(ctx, *args, **kwargs):
        ctx.config = yaml.load(open(ctx.obj["configfile"]))
        return ctx.invoke(f, *args, **kwargs)

    return update_wrapper(new_func, f)


# Aliases
onlineChain = chain
online = chain
offlineChain = offline
unlockWallet = unlock