"""Widget base class."""

import json
import secrets
from typing import TYPE_CHECKING, Any, ClassVar, Generic, Self, TypeVar

from markupsafe import Markup
from pydantic import Field, model_validator

from fastlife.adapters.xcomponent.registry import PYDANTICFORM_CATALOG_NS
from fastlife.domain.model.template import XTemplate

if TYPE_CHECKING:
    from fastlife.service.templates import AbstractTemplateRenderer

T = TypeVar("T")


class Widget(XTemplate, Generic[T]):
    """
    Base class for widget of pydantic fields.

    :param name: field name.
    :param value: field value.
    :param title: title for the widget.
    :param hint: hint for human.
    :param aria_label: html input aria-label value.
    :param value: current value.
    :param error: error of the value if any.
    :param children_types: childrens types list.
    :param removable: display a button to remove the widget for optional fields.
    :param token: token used to get unique id on the form.
    """

    namespace: ClassVar[str] = PYDANTICFORM_CATALOG_NS

    name: str
    "variable name, nested variables have dots."
    id: str = Field(default="")
    "variable name, nested variables have dots."
    value: T | None = Field(default=None)
    """Value of the field."""
    title: str = Field(default="")
    "Human title for the widget."
    hint: str | None = Field(default=None)
    "A help message for the the widget."

    error: str | None = Field(default=None)
    "Error message."

    aria_label: str | None = Field(default=None)
    "Non visible text alternative."
    token: str = Field(default="")
    "Unique token to ensure id are unique in the DOM."
    removable: bool = Field(default=False)
    "Indicate that the widget is removable from the dom."

    @model_validator(mode="after")
    def fill_props(self) -> Self:
        self.title = self.title or self.name.split(".")[-1]
        self.token = self.token or secrets.token_urlsafe(4).replace("_", "-")
        self.id = self.id or f"{self.name}-{self.token}".replace("_", "-").replace(
            ".", "-"
        )
        return self

    def to_html(self, renderer: "AbstractTemplateRenderer[XTemplate]") -> Markup:
        """Return the html version."""
        return Markup(renderer.render_template(self))


TWidget = TypeVar("TWidget", bound=Widget[Any])


class CustomWidget(Generic[TWidget]):
    """Type to annotate a field in order to override its default assitated widget."""

    typ: type[Any]

    def __init__(self, typ: type[TWidget], **kwargs: Any) -> None:
        self.typ = typ
        self.kwargs = kwargs or {}


class TypeWrapper:
    """
    Wrap children types for union type.

    :param typ: Wrapped type.
    :param route_prefix: route prefix used for ajax query to build type.
    :param name: name of the field wrapped.
    :param token: unique token to render unique id.
    :param title: title to display.

    """

    def __init__(
        self,
        typ: type[Any],
        route_prefix: str,
        name: str,
        token: str,
        title: str | None = None,
    ):
        self.typ = typ
        self.route_prefix = route_prefix
        self.name = name
        self.title = title or typ.__name__
        self.token = token

    @property
    def fullname(self) -> str:
        """Full name for the type."""
        return f"{self.typ.__module__}:{self.typ.__name__}"

    @property
    def id(self) -> str:
        """Unique id to inject in the DOM."""
        name = self.name.replace("_", "-").replace(".", "-").replace(":", "-")
        typ = self.typ.__name__.replace("_", "-")
        return f"{name}-{typ}-{self.token}"

    @property
    def params(self) -> str:
        """Params for the widget to render."""
        params = {
            "name": self.name,
            "token": self.token,
            "title": self.title,
        }
        return json.dumps(params)

    @property
    def url(self) -> str:
        """Url to fetch the widget."""
        ret = f"{self.route_prefix}/pydantic-form/widgets/{self.fullname}"
        return ret
