import argparse

from keri import help
from hio.base import doing

from keri.app import indirecting, habbing, grouping, forwarding
from keri.app.cli.common import existing
from keri.app.habbing import GroupHab
from keri.app.notifying import Notifier
from keri.core import serdering
from keri.core.eventing import SealEvent
from keri.peer import exchanging
from keri.vdr import credentialing

logger = help.ogler.getLogger()

parser = argparse.ArgumentParser(description='Initialize a prefix')
parser.set_defaults(handler=lambda args: registryIncept(args),
                    transferable=True)
parser.add_argument('--name', '-n', help='Human readable reference', required=True)
parser.add_argument('--registry-name', '-r', help='Human readable name for registry, defaults to name of Habitat',
                    default=None)
parser.add_argument('--nonce', help='Unique random value to seed the credential registry',
                    default=None, required=False)
parser.add_argument("--no-backers", "-nb", help="do not allow setting up backers different from the ahcnoring KEL "
                                                "witnesses", default=True, action="store")
parser.add_argument('--backers', help='New set of backers different from the anchoring KEL witnesses.  Can '
                                      'appear multiple times', metavar="<prefix>", default=[], action="append",
                    required=False)
parser.add_argument("--establishment-only", "-eo", help="Only allow establishment events for the anchoring events of "
                                                        "this registry", default=False, action="store")
parser.add_argument('--base', '-b', help='additional optional prefix to file location of KERI keystore',
                    required=False, default="")
parser.add_argument('--alias', '-a', help='human readable alias for the new identifier prefix', required=True)
parser.add_argument('--passcode', '-p', help='22 character encryption passcode for keystore (is not saved)',
                    dest="bran", default=None)  # passcode => bran
parser.add_argument('--usage', '-u', help='For multisig issuers, a message to other participants about how this'
                                          ' registry is to be used',
                    default=None)


def registryIncept(args):
    name = args.name
    alias = args.alias
    bran = args.bran
    base = args.base
    registryName = args.registry_name if args.registry_name is not None else alias
    nonce = args.nonce
    estOnly = args.establishment_only
    noBackers = args.no_backers
    backers = args.backers
    usage = args.usage

    if noBackers and backers:
        print("--no-backers and --backers can not both be provided")
        return -1

    icpDoer = RegistryInceptor(name=name, base=base, alias=alias, bran=bran, registryName=registryName,
                               nonce=nonce, estOnly=estOnly, noBackers=noBackers, baks=backers, usage=usage)

    doers = [icpDoer]
    return doers


class RegistryInceptor(doing.DoDoer):
    """ Creates a registry

    """

    def __init__(self, name, base, alias, bran, registryName, usage, **kwa):
        """  Create RegistryIncepter to pass message and process cues

        Parameters:
            name (str): name of habery and shared db and file path
            base (str): optional if "" path component of shared db and files.
            alias(str): human readable name of identifier to use for registry inception
            bran (str): Base64 22 char string that is used as base material for
                        seed. bran allows alphanumeric passcodes generated by key managers
                        like 1password to be key store for seed.
            registryName(str): human readable name for newly created registry

        """
        self.name = name
        self.alias = alias
        self.registryName = registryName
        self.usage = usage
        self.hby = existing.setupHby(name=name, base=base, bran=bran)
        self.rgy = credentialing.Regery(hby=self.hby, name=name, base=base)
        self.hbyDoer = habbing.HaberyDoer(habery=self.hby)  # setup doer
        counselor = grouping.Counselor(hby=self.hby)
        self.postman = forwarding.Poster(hby=self.hby)

        notifier = Notifier(self.hby)
        mux = grouping.Multiplexor(self.hby, notifier=notifier)
        exc = exchanging.Exchanger(hby=self.hby, handlers=[])
        grouping.loadHandlers(exc, mux)

        mbx = indirecting.MailboxDirector(hby=self.hby, topics=["/receipt", "/multisig", "/replay"], exc=exc)
        self.registrar = credentialing.Registrar(hby=self.hby, rgy=self.rgy, counselor=counselor)
        doers = [self.hbyDoer, counselor, self.registrar, self.postman, mbx]
        self.toRemove = list(doers)

        doers.extend([doing.doify(self.inceptDo, **kwa)])
        super(RegistryInceptor, self).__init__(doers=doers)

    def inceptDo(self, tymth, tock=0.0, **kwa):
        """ Process incoming messages to incept a credential registry

        Parameters:
            tymth (function): injected function wrapper closure returned by .tymen() of
                Tymist instance. Calling tymth() returns associated Tymist .tyme.
            tock (float): injected initial tock value

        Returns:  doifiable Doist compatible generator method
        """
        # enter context
        self.wind(tymth)
        self.tock = tock
        _ = (yield self.tock)

        hab = self.hby.habByName(self.alias)
        if hab is None:
            raise ValueError(f"{self.alias} is not a valid AID alias")

        estOnly = "estOnly" in kwa and kwa["estOnly"]
        registry = self.rgy.makeRegistry(name=self.registryName, prefix=hab.pre, **kwa)

        rseal = SealEvent(registry.regk, "0", registry.regd)
        rseal = dict(i=rseal.i, s=rseal.s, d=rseal.d)
        if estOnly:
            anc = hab.rotate(data=[rseal])
        else:
            anc = hab.interact(data=[rseal])

        aserder = serdering.SerderKERI(raw=bytes(anc))  # coring.Serder(raw=bytes(anc))
        self.registrar.incept(iserder=registry.vcp, anc=aserder)

        if isinstance(hab, GroupHab):
            usage = self.usage
            if usage is None:
                usage = input(f"Please enter a description of the credential registry: ")

            smids = hab.db.signingMembers(pre=hab.pre)
            smids.remove(hab.mhab.pre)

            for recp in smids:  # this goes to other participants only as a signaling mechanism
                exn, atc = grouping.multisigRegistryInceptExn(ghab=hab, vcp=registry.vcp.raw, anc=anc, usage=usage)
                self.postman.send(src=hab.mhab.pre,
                                  dest=recp,
                                  topic="multisig",
                                  serder=exn,
                                  attachment=atc)

        while not self.registrar.complete(pre=registry.regk, sn=0):
            self.rgy.processEscrows()
            yield self.tock

        print("Registry:  {}({}) \n\tcreated for Identifier Prefix:  {}".format(self.registryName,
                                                                                registry.regk, hab.pre))

        self.remove(self.toRemove)
