import attr


def model(maybe_cls=None, name=None, ns=None, ns_map=None, order=None, **kwargs):
    """
    The decorator maps a class to an XML element. It uses a class
    name as a default element name. The default name can be altered using the decorator
    `name` argument. The `ns` argument defines a namespace of the element. By default
    class fields are serialized in in-class definition order. The order can be altered
    using `order` attribute.

    Internally the decorator adds a `dunder <https://wiki.python.org/moin/DunderAlias>`_\
    attribute.

    :param maybe_cls: decorated class if it is used as `@model` or `None` if it is used as `@model()`
    :param str name: model name. If `None` class name will be used
    :param str ns: element namespace. If `None` empty namespace will be used or if the model
               is a nested one - namespace is inherited from the containing model
    :param dict ns_map: mapping from a namespace prefix to a full name. It is applied to the current model
                        and it's elements and all nested models
    :param tuple order: class fields serialization order. If `None` in-class definition order is used
    :param kwargs: arguments that will be passed to :py:func:`attr.ib`
    """

    def decorator(cls):
        attrs_kwargs = {'kw_only': True, **kwargs}
        cls = attr.attrs(cls, **attrs_kwargs)
        if order:
            for element_name in order:
                if not hasattr(getattr(cls, '__attrs_attrs__'), element_name):
                    raise AssertionError("order element '{}' not declared in model".format(element_name))

        cls.__paxb_attrs__ = (name, ns, ns_map, order)

        return cls

    # maybe_cls's type depends on the usage of the decorator.  It's a class
    # if it's used as `@model` but ``None`` if used as `@model()`.
    if maybe_cls is None:
        return decorator
    else:
        return decorator(maybe_cls)