import logging

from lxml import etree

from zeep import exceptions
from zeep.xsd.const import NotSet
from zeep.xsd.elements.element import Element

logger = logging.getLogger(__name__)

__all__ = ["Attribute", "AttributeGroup"]


class Attribute(Element):
    def __init__(self, name, type_=None, required=False, default=None):
        super(Attribute, self).__init__(name=name, type_=type_, default=default)
        self.required = required
        self.array_type = None

    def parse(self, value):
        try:
            return self.type.pythonvalue(value)
        except (TypeError, ValueError):
            logger.exception("Error during xml -> python translation")
            return None

    def render(self, parent, value, render_path=None):
        if value in (None, NotSet) and not self.required:
            return

        self.validate(value, render_path)

        value = self.type.xmlvalue(value)
        parent.set(self.qname, value)

    def validate(self, value, render_path):
        try:
            self.type.validate(value, required=self.required)
        except exceptions.ValidationError as exc:
            raise exceptions.ValidationError(
                "The attribute %s is not valid: %s" % (self.qname, exc.message),
                path=render_path,
            )

    def clone(self, *args, **kwargs):
        array_type = kwargs.pop("array_type", None)
        new = super(Attribute, self).clone(*args, **kwargs)
        new.array_type = array_type
        return new

    def resolve(self):
        retval = super(Attribute, self).resolve()
        self.type = self.type.resolve()
        if self.array_type:
            retval.array_type = self.array_type.resolve()
        return retval


class AttributeGroup(object):
    def __init__(self, name, attributes):
        if not isinstance(name, etree.QName):
            name = etree.QName(name)

        self.name = name.localname
        self.qname = name
        self.type = None
        self._attributes = attributes
        self.is_global = True

    @property
    def attributes(self):
        result = []
        for attr in self._attributes:
            if isinstance(attr, AttributeGroup):
                result.extend(attr.attributes)
            else:
                result.append(attr)
        return result

    def resolve(self):
        resolved = []
        for attribute in self._attributes:
            value = attribute.resolve()
            assert value is not None
            if isinstance(value, list):
                resolved.extend(value)
            else:
                resolved.append(value)
        self._attributes = resolved
        return self

    def signature(self, schema=None, standalone=True):
        return ", ".join(attr.signature(schema) for attr in self._attributes)
