import lxml
import datetime
import logging
from collections import defaultdict
from odoo.addons.fusion_shipping.api.canada_post.utils import get_dom_tree, python_2_unicode_compatible
import json
_logger = logging.getLogger(__name__)

@python_2_unicode_compatible
class ResponseDataObject():

    def __init__(self, mydict, datetime_nodes=[]):
        self._load_dict(mydict, list(datetime_nodes))

    def __repr__(self):
        return str(self)

    def __str__(self):
        return "%s" % self.__dict__

    def has_key(self, name):
        try:
            getattr(self, name)
            return True
        except AttributeError:
            return False

    def get(self, name, default=None):
        try:
            return getattr(self, name)
        except AttributeError:
            return default

    def _setattr(self, name, value, datetime_nodes):
        if name.lower() in datetime_nodes:
            try:
                ts = "%s %s" % (value.partition('T')[0], value.partition('T')[2].partition('.')[0])
                value = datetime.datetime.strptime(ts, '%Y-%m-%d %H:%M:%S')
            except ValueError:
                pass

        setattr(self, name, value)

    def _load_dict(self, mydict, datetime_nodes):

        for a in list(mydict.items()):

            if isinstance(a[1], dict):
                o = ResponseDataObject(a[1], datetime_nodes)
                setattr(self, a[0], o)

            elif isinstance(a[1], list):
                objs = []
                for i in a[1]:
                    if i is None or isinstance(i, str) or isinstance(i, str):
                        objs.append(i)
                    else:
                        objs.append(ResponseDataObject(i, datetime_nodes))

                setattr(self, a[0], objs)
            else:
                self._setattr(a[0], a[1], datetime_nodes)

class Response():

    def __init__(self, obj, verb=None, parse_response=True):
        self._obj = obj
        if parse_response:
            try:
                self._dom = self._parse_xml(obj.content)
                self._dict = self._etree_to_dict(self._dom)

                if verb and 'Envelope' in list(self._dict.keys()):
                    elem = self._dom.find('Body').find('%sResponse' % verb)
                    if elem is not None:
                        self._dom = elem

                    self._dict = self._dict['Envelope']['Body'].get('%sResponse' % verb, self._dict)
                elif verb:
                    elem = self._dom.find('%sResponse' % verb)
                    if elem is not None:
                        self._dom = elem

                    self._dict = self._dict.get('%sResponse' % verb, self._dict)

                self.reply = ResponseDataObject(self._dict,[])

            except lxml.etree.XMLSyntaxError as e:
                _logger.debug('Response parse failed: %s' % e)
                self.reply = ResponseDataObject({}, [])
        else:
            self.reply = ResponseDataObject({}, [])

    def _get_node_path(self, t):
        i = t
        path = []
        path.insert(0, i.tag)
        while 1:
            try:
                path.insert(0, i.getparent().tag)
                i = i.getparent()
            except AttributeError:
                break

        return '.'.join(path)

    @staticmethod
    def _pullval(v):
        if len(v) == 1:
            return v[0]
        else:
            return v

    def _etree_to_dict(self, t):
        if type(t) == lxml.etree._Comment:
            return {}

        # remove xmlns from nodes
        t.tag = self._get_node_tag(t)

        d = {t.tag: {} if t.attrib else None}
        children = list(t)
        if children:
            dd = defaultdict(list)
            for dc in map(self._etree_to_dict, children):
                for k, v in list(dc.items()):
                    dd[k].append(v)

            d = {t.tag: dict((k, self._pullval(v)) for k, v in list(dd.items()))}

            parent_path = self._get_node_path(t)
            for k in list(d[t.tag].keys()):
                path = "%s.%s" % (parent_path, k)

        if t.attrib:
            d[t.tag].update(('_' + k, v) for k, v in list(t.attrib.items()))
        if t.text:
            text = t.text.strip()
            if children or t.attrib:
                if text:
                    d[t.tag]['value'] = text
            else:
                d[t.tag] = text
        return d

    def __getattr__(self, name):
        return getattr(self._obj, name)

    def _parse_xml(self, xml):
        return get_dom_tree(xml)

    def _get_node_tag(self, node):
        return node.tag.replace('{' + node.nsmap.get(node.prefix, '') + '}', '')

    def dom(self, lxml=True):
        if not lxml:
            pass
        return self._dom

    def dict(self):
        return self._dict

    def json(self):
        return json.dumps(self.dict())
