from dataclasses import dataclass, field
from typing import List, Optional, Dict
from xsdata.formats.dataclass.serializers import XmlSerializer
from xsdata.formats.dataclass.serializers.config import SerializerConfig


@dataclass
class PropertyType:
    value: Optional[str] = field(
        default=None,
    )
    name: Optional[str] = field(
        default=None,
        metadata={
            "name": "Name",
            "type": "Attribute",
            "required": True,
        }
    )


@dataclass
class Adi:
    class Meta:
        name = "ADI"

    namesp: Optional[str] = field(
        default=None,
        metadata={
            "name": "xmlns.xsi",
            "type": "Attribute",
        }
    )
    objects: Optional["Adi.Objects"] = field(
        default=None,
        metadata={
            "name": "Objects",
            "type": "Element",
        }
    )
    mappings: Optional["Adi.Mappings"] = field(
        default=None,
        metadata={
            "name": "Mappings",
            "type": "Element",
        }
    )
    reply: Optional["Adi.Reply"] = field(
        default=None,
        metadata={
            "name": "Reply",
            "type": "Element",
        }
    )
    domain: Optional[str] = field(
        default=None,
        metadata={
            "name": "BizDomain",
            "type": "Attribute",
            "required": True,
        }
    )

    @dataclass
    class Objects:
        object: List["Adi.Objects.Object"] = field(
            default_factory=list,
            metadata={
                "name": "Object",
                "type": "Element",
            }
        )

        @dataclass
        class Object:
            property: List[PropertyType] = field(
                default_factory=list,
                metadata={
                    "name": "Property",
                    "type": "Element",
                }
            )
            element_type: Optional[str] = field(
                default=None,
                metadata={
                    "name": "ElementType",
                    "type": "Attribute",
                    "required": True,
                }
            )
            id: Optional[str] = field(
                default=None,
                metadata={
                    "name": "ID",
                    "type": "Attribute",
                    "required": True,
                }
            )
            code: Optional[str] = field(
                default=None,
                metadata={
                    "name": "Code",
                    "type": "Attribute",
                    "required": True,
                }
            )
            action: Optional[str] = field(
                default=None,
                metadata={
                    "name": "Action",
                    "type": "Attribute",
                    "required": True,
                }
            )
            parentcode: Optional[str] = field(
                default=None,
                metadata={
                    "name": "ParentCode",
                    "type": "Attribute",
                    "required": True,
                }
            )

    @dataclass
    class Mappings:
        mapping: List["Adi.Mappings.Mapping"] = field(
            default_factory=list,
            metadata={
                "name": "Mapping",
                "type": "Element",
            }
        )

        @dataclass
        class Mapping:
            property: List[PropertyType] = field(
                default_factory=list,
                metadata={
                    "name": "Property",
                    "type": "Element",
                }
            )
            id: Optional[str] = field(
                default=None,
                metadata={
                    "name": "ID",
                    "type": "Attribute",
                }
            )
            parent_type: Optional[str] = field(
                default=None,
                metadata={
                    "name": "ParentType",
                    "type": "Attribute",
                    "required": True,
                }
            )
            parent_code: Optional[str] = field(
                default=None,
                metadata={
                    "name": "ParentCode",
                    "type": "Attribute",
                    "required": True,
                }
            )
            parent_id: Optional[str] = field(
                default=None,
                metadata={
                    "name": "ParentID",
                    "type": "Attribute",
                    "required": True,
                }
            )
            element_type: Optional[str] = field(
                default=None,
                metadata={
                    "name": "ElementType",
                    "type": "Attribute",
                    "required": True,
                }
            )
            element_id: Optional[str] = field(
                default=None,
                metadata={
                    "name": "ElementID",
                    "type": "Attribute",
                    "required": True,
                }
            )
            element_code: Optional[str] = field(
                default=None,
                metadata={
                    "name": "ElementCode",
                    "type": "Attribute",
                    "required": True,
                }
            )
            action: Optional[str] = field(
                default=None,
                metadata={
                    "name": "Action",
                    "type": "Attribute",
                    "required": True,
                }
            )

    @dataclass
    class Reply:
        result: Optional[str] = field(
            default=None,
            metadata={
                "name": "Result",
                "type": "Element",
                "required": True,
            }
        )
        description: Optional[str] = field(
            default=None,
            metadata={
                "name": "Description",
                "type": "Element",
                "required": True,
            }
        )

    @staticmethod
    def _format_property(data: dict):
        tmp = []
        tmp.extend(data["object_list"])
        tmp.extend(data["mapping_list"])
        for i in tmp:
            if i.get("property"):
                property_obj_list = []
                for j in i["property"]:
                    name = list(j.keys())[0]
                    value = list(j.values())[0]
                    property_obj_list.append(PropertyType(name=name, value=value))

                i["property"] = property_obj_list

    @classmethod
    def to_xml(cls, data: Dict, flag="reply"):
        cls._format_property(data)
        object_obj_list = [cls.Objects.Object(**i) for i in data["object_list"]]
        mapping_obj_list = [cls.Mappings.Mapping(**i) for i in data["mapping_list"]]
        objects = cls.Objects(object=object_obj_list)
        mappings = cls.Mappings(mapping=mapping_obj_list)
        domain = data.get("domain")
        namesp = data.get("namesp")

        if flag == "reply":
            reply = cls.Reply(**data["reply"])
            obj = cls(
                objects=objects, mappings=mappings, reply=reply, domain=domain, namesp=namesp
            )
        else:
            obj = cls(objects=objects, mappings=mappings, domain=domain, namesp=namesp)
        serializer = XmlSerializer(config=SerializerConfig(pretty_print=True))
        x = serializer.render(obj).replace("&lt;", "<").replace("&gt;", ">").replace(".xsi", ":xsi").replace("&amp;",
                                                                                                             "&")
        return x
