import collections
import xml.etree.ElementTree as ET

from rest_framework import renderers

import funkwhale_api


# from https://stackoverflow.com/a/8915039
# because I want to avoid a lxml dependency just for outputting cdata properly
# in a RSS feed
def CDATA(text=None):
    element = ET.Element("![CDATA[")
    element.text = text
    return element


ET._original_serialize_xml = ET._serialize_xml


def _serialize_xml(write, elem, qnames, namespaces, **kwargs):
    if elem.tag == "![CDATA[":
        write("<%s%s]]>" % (elem.tag, elem.text))
        return
    return ET._original_serialize_xml(write, elem, qnames, namespaces, **kwargs)


ET._serialize_xml = ET._serialize["xml"] = _serialize_xml
# end of tweaks


def structure_payload(data):
    payload = {
        "funkwhaleVersion": funkwhale_api.__version__,
        "status": "ok",
        "type": "funkwhale",
        "version": "1.16.0",
    }
    payload.update(data)
    if "detail" in payload:
        payload["error"] = {"code": 0, "message": payload.pop("detail")}
    if "error" in payload:
        payload["status"] = "failed"
    return collections.OrderedDict(sorted(payload.items(), key=lambda v: v[0]))


class SubsonicJSONRenderer(renderers.JSONRenderer):
    def render(self, data, accepted_media_type=None, renderer_context=None):
        if not data:
            # when stream view is called, we don't have any data
            return super().render(data, accepted_media_type, renderer_context)
        final = {"subsonic-response": structure_payload(data)}
        return super().render(final, accepted_media_type, renderer_context)


class SubsonicXMLRenderer(renderers.JSONRenderer):
    media_type = "text/xml"

    def render(self, data, accepted_media_type=None, renderer_context=None):
        if not data:
            # when stream view is called, we don't have any data
            return super().render(data, accepted_media_type, renderer_context)
        final = structure_payload(data)
        final["xmlns"] = "http://subsonic.org/restapi"
        tree = dict_to_xml_tree("subsonic-response", final)
        return b'<?xml version="1.0" encoding="UTF-8"?>\n' + ET.tostring(
            tree, encoding="utf-8"
        )


def dict_to_xml_tree(root_tag, d, parent=None):
    root = ET.Element(root_tag)
    for key, value in d.items():
        if isinstance(value, dict):
            root.append(dict_to_xml_tree(key, value, parent=root))
        elif isinstance(value, list):
            for obj in value:
                root.append(dict_to_xml_tree(key, obj, parent=root))
        else:
            if key == "value":
                root.text = str(value)
            elif key == "cdata_value":
                root.append(CDATA(value))
            else:
                root.set(key, str(value))
    return root
