# Written by retoor@molodetz.nl

# This code defines a framework for handling HTML elements as Python objects, including specific classes for HTML, form input, and form button elements. It offers methods to convert these elements to JSON, manipulate them, and validate form data.

# This code uses the `snek.system.model` library for managing model fields.

# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from snek.system import model


class HTMLElement(model.ModelField):
    def __init__(
        self,
        id=None,
        tag="div",
        name=None,
        html=None,
        class_name=None,
        text=None,
        *args,
        **kwargs,
    ):
        self.tag = tag
        self.text = text
        self.id = id
        self.class_name = class_name or name
        self.html = html
        super().__init__(name=name, *args, **kwargs)

    async def to_json(self):
        result = await super().to_json()
        result["text"] = self.text
        result["id"] = self.id
        result["html"] = self.html
        result["class_name"] = self.class_name
        result["tag"] = self.tag
        return result


class FormElement(HTMLElement):
    pass


class FormInputElement(FormElement):
    def __init__(self, type="text", place_holder=None, *args, **kwargs):
        super().__init__(tag="input", *args, **kwargs)
        self.place_holder = place_holder
        self.type = type

    async def to_json(self):
        data = await super().to_json()
        data["place_holder"] = self.place_holder
        data["type"] = self.type
        return data


class FormButtonElement(FormElement):
    def __init__(self, tag="button", *args, **kwargs):
        super().__init__(tag=tag, *args, **kwargs)


class Form(model.BaseModel):
    @property
    def html_elements(self):
        return [element for element in self.fields if isinstance(element, HTMLElement)]

    def set_user_data(self, data):
        return super().set_user_data(data.get("fields"))

    async def to_json(self, encode=False):
        elements = await super().to_json()
        html_elements = {}
        for element in elements.keys():
            if element == "is_valid":
                # is_valid is async get property so we can't do getattr on it
                continue
            field = getattr(self, element)
            if isinstance(field, HTMLElement):
                try:
                    html_elements[element] = elements[element]
                except KeyError:
                    pass

        is_valid = all(field["is_valid"] for field in html_elements.values())
        return {
            "fields": html_elements,
            "is_valid": is_valid,
            "errors": await self.errors,
        }

    @property
    async def errors(self):
        result = []
        for field in self.html_elements:
            result += await field.errors
        return result

    @property
    async def is_valid(self):
        # This is not good, but timebox to resolve issue exceeded.
        return False
