# -*- coding: utf-8 -*-
"""
Module containing base classes for middleware.

This module defines the base class for form widgets, which are used to
represent form fields in a PDF document. The Widget class provides
common attributes and methods for all form widgets, such as name, value,
and schema definition.
"""

from typing import Any, List, Optional, TextIO


class Widget:
    """
    Base class for form widget.

    The Widget class provides a base implementation for form widgets,
    which are used to represent form fields in a PDF document. It
    defines common attributes and methods for all form widgets, such
    as name, value, and schema definition.
    """

    def __init__(
        self,
        name: str,
        value: Any = None,
    ) -> None:
        """
        Initialize a new widget.

        Args:
            name (str): The name of the widget.
            value (Any): The initial value of the widget. Defaults to None.
        """
        super().__init__()
        self.SET_ATTR_TRIGGER_HOOK_MAP = {
            "x": "update_field_x",
            "y": "update_field_y",
            "width": "update_field_width",
            "height": "update_field_height",
            "readonly": "flatten_field",
            "required": "update_field_required",
            "hidden": "update_field_hidden",
            "tooltip": "update_field_tooltip",
            "on_hovered_over_javascript": "update_field_on_hovered_over_javascript",
            "on_hovered_off_javascript": "update_field_on_hovered_off_javascript",
            "on_mouse_pressed_javascript": "update_field_on_mouse_pressed_javascript",
            "on_mouse_released_javascript": "update_field_on_mouse_released_javascript",
            "on_focused_javascript": "update_field_on_focused_javascript",
            "on_blurred_javascript": "update_field_on_blurred_javascript",
        }
        self.attr_set_tracker = {}

        self._name = name
        self._value = value

        self.tooltip: Optional[str] = None
        self.readonly: Optional[bool] = None
        self.required: Optional[bool] = None
        self.hidden: Optional[bool] = None
        self.hooks_to_trigger: list = []

        # coordinate & dimension
        self.x: Optional[float | List[float]] = None
        self.y: Optional[float | List[float]] = None
        self.width: Optional[float | List[float]] = None
        self.height: Optional[float | List[float]] = None

        # javascript
        self.on_hovered_over_javascript: Optional[str | TextIO] = None
        self.on_hovered_off_javascript: Optional[str | TextIO] = None
        self.on_mouse_pressed_javascript: Optional[str | TextIO] = None
        self.on_mouse_released_javascript: Optional[str | TextIO] = None
        self.on_focused_javascript: Optional[str | TextIO] = None
        self.on_blurred_javascript: Optional[str | TextIO] = None

    def __setattr__(self, name: str, value: Any) -> None:
        """
        Set an attribute on the widget.

        This method overrides the default __setattr__ method to
        trigger hooks when certain attributes are set. It also
        tracks the attributes that have been set.

        Args:
            name (str): The name of the attribute.
            value (Any): The value of the attribute.
        """
        if (
            hasattr(self, "SET_ATTR_TRIGGER_HOOK_MAP")
            and name in self.SET_ATTR_TRIGGER_HOOK_MAP
            and value is not None
        ):
            self.hooks_to_trigger.append((self.SET_ATTR_TRIGGER_HOOK_MAP[name], value))

        if (
            hasattr(self, "attr_set_tracker")
            and name in self.__dict__
            and value is not None
        ):
            self.attr_set_tracker[name] = value

        super().__setattr__(name, value)

    @property
    def name(self) -> str:
        """
        Get the name of the widget.

        Returns:
            str: The name of the widget.
        """
        return self._name

    @property
    def value(self) -> Any:
        """
        Get the value of the widget.

        Returns:
            Any: The value of the widget.
        """
        return self._value

    @value.setter
    def value(self, value: Any) -> None:
        """
        Set the value of the widget.

        Args:
            value (Any): The value to set.
        """
        self._value = value

    @property
    def schema_definition(self) -> dict:
        """
        Get the schema definition of the widget.

        This method returns a dictionary that defines the schema
        for the widget. The schema definition is used to validate
        the widget's value.

        Returns:
            dict: The schema definition of the widget.
        """
        result = {}

        if self.tooltip is not None:
            result["description"] = self.tooltip

        return result

    @property
    def sample_value(self) -> Any:
        """
        Get a sample value for the widget.

        Raises:
            NotImplementedError: This method must be implemented by subclasses.
        """
        raise NotImplementedError
