"""
Utils related to processing or registering for webhooks

A model registers itself here if it wants to be in the list of processing
functions for a particular webhook. Each processor will have the ability
to modify the event object, access event data, and do what it needs to do

registrations are keyed by top-level event type (e.g. "invoice", "customer", etc)
Each registration entry is a list of processors
Each processor in these lists is a function to be called
The function signature is:
    <Event object>

There is also a "global registry" which is just a list of processors (as defined above)

NOTE: global processors are called before other processors.
"""
import functools
import itertools
from collections import defaultdict

__all__ = ["handler", "handler_all", "call_handlers"]


registrations = defaultdict(list)
registrations_global = list()

# Legacy. In previous versions of Stripe API, all test events used this ID.
# Check out issue #779 for more information.
TEST_EVENT_ID = "evt_00000000000000"


def handler(*event_types):
    """
    Decorator that registers a function as a webhook handler.

    Functions can be registered for event types (e.g. 'customer') or
    fully qualified event sub-types (e.g. 'customer.subscription.deleted').

    If an event type is specified, the handler will receive callbacks for
    ALL webhook events of that type.  For example, if 'customer' is specified,
    the handler will receive events for 'customer.subscription.created',
    'customer.subscription.updated', etc.

    :param event_types: The event type(s) that should be handled.
    :type event_types: str.
    """

    def decorator(func):
        for event_type in event_types:
            registrations[event_type].append(func)
        return func

    return decorator


def handler_all(func=None):
    """
    Decorator that registers a function as a webhook handler for ALL webhook events.

    Handles all webhooks regardless of event type or sub-type.
    """
    if not func:
        return functools.partial(handler_all)

    registrations_global.append(func)

    return func


def call_handlers(event):
    """
    Invoke all handlers for the provided event type/sub-type.

    The handlers are invoked in the following order:

    1. Global handlers
    2. Event type handlers
    3. Event sub-type handlers

    Handlers within each group are invoked in order of registration.

    :param event: The event model object.
    :type event: ``djstripe.models.Event``
    """
    chain = [registrations_global]

    # Build up a list of handlers with each qualified part of the event
    # category and verb.  For example, "customer.subscription.created" creates:
    #   1. "customer"
    #   2. "customer.subscription"
    #   3. "customer.subscription.created"
    for index, _ in enumerate(event.parts):
        qualified_event_type = ".".join(event.parts[: (index + 1)])
        chain.append(registrations[qualified_event_type])

    for handler_func in itertools.chain(*chain):
        handler_func(event=event)
