import asyncio
from functools import lru_cache
import inspect
import threading
import uuid
import time
from typing import Optional, Callable, Any, Dict, Union, List

from PySide6.QtWidgets import QVBoxLayout, QWidget, QHBoxLayout, QPlainTextEdit, QLineEdit, QApplication, QStyledItemDelegate, \
        QSizePolicy, QPushButton, QSpinBox, QDoubleSpinBox, QGraphicsOpacityEffect, QFrame, QGroupBox
from PySide6.QtCore import Qt, QPoint, Signal, QDate, QTime, QTimer, QEvent, QSize
from PySide6.QtGui import QCursor

from qtmui.hooks import State
from qtmui.utils.translator import getTranslatedText
from ..li import Li

from ..menu import Menu
from ..menu_item import MenuItem

from .flow_layout import FlowLayout
from .tags_view import TagsView

from ..picker import DatePicker, TimePicker, AMTimePicker, ZhDatePicker, CalendarPicker, ColorPicker

from qtmui.hooks import useState

from qtmui.material.styles import useTheme
from qtmui.i18n.use_translation import translate, i18n
from ..utils._misc import signals_blocked
from ..utils.functions import _get_fn_args

from ...qtmui_assets import QTMUI_ASSETS

from ..py_iconify import PyIconify

from .py_combobox import PyComboBox
from .py_date_edit import PyDateEdit
from .py_date_time_edit import PyDateTimeEdit
from .py_dial import PyDial
from .py_double_spinbox import PyDoubleSpinBox
from .py_font_combobox import PyFontComboBox
from .py_line_edit import PyLineEdit
from .py_line_edit_multiple import PyLineEditMultiple
from .py_plaintext_edit import PyPlainTextEdit
from .py_plaintext_edit_multiple import PyPlainTextEditMultiple
from .py_spin_box import PySpinBox
from .py_text_edit import PyTextEdit
from .py_time_edit import PyTimeEdit

from qtmui.material.styles.create_theme.components.get_qss_styles import get_qss_style

from ..widget_base import PyWidgetBase

from ..utils.validate_params import _validate_param

class TextField(QGroupBox):

    showPlaceholder = Signal()
    valueChanged = Signal(object)
    updatePopupPosition = Signal()
    createMenu = Signal()
    renderSearchMenuItems = Signal(object)

    VALID_COLORS = ['primary', 'secondary', 'error', 'info', 'success', 'warning']
    VALID_MARGINS = ['dense', 'none', 'normal']
    VALID_SIZES = ['medium', 'small']
    VALID_VARIANTS = ['filled', 'outlined', 'standard']

    def __init__(
        self,
        key: str= None,
        autoComplete: Optional[Union[State, bool, str]] = None,
        autoFocus: Optional[Union[State, bool]] = False,
        autoWidth: Optional[Union[State, bool]] = False, # chỉ dùng cho Select
        classes: Optional[Union[State, Dict]] = None,
        color: Optional[Union[State, str]] = "primary",
        defaultOpen: Optional[Union[State, bool]] = False, # chỉ dùng cho Select
        defaultValue: Optional[Union[State, object]] = None,
        disabled: Optional[Union[State, bool]] = False,
        error: Optional[Union[State, bool]] = False,
        FormHelperTextProps: Optional[Union[State, Dict]] = None,
        fullWidth: Optional[Union[State, bool]] = False,
        helperText: Optional[Union[State, str]] = None,
        id: Optional[Union[State, str]] = None,
        InputLabelProps: Optional[Union[State, Dict]] = None,
        inputProps: Optional[Union[State, Dict]] = None,
        InputProps: Optional[Union[State, Dict]] = None,
        inputRef: Optional[Union[State, object]] = None,
        inputValue: Optional[Union[State, object]] = None,
        label: Optional[Union[State, str, Callable]] = None,
        max: Optional[Union[State, int, float]] = None,
        margin: Optional[Union[State, str]] = "none",
        maxRows: Optional[Union[State, int, str]] = None,
        min: Optional[Union[State, int, float]] = None,
        minRows: Optional[Union[State, int, str]] = None,
        multiline: Optional[Union[State, bool]] = False,
        multiple: Optional[Union[State, bool]] = False,
        menuHeight: Optional[Union[State, int]] = 400,
        name: Optional[Union[State, str]] = None,
        getOptionLabel: Optional[Union[State, Callable]] = None,
        open: Optional[Union[State, bool]] = False, # chỉ dùng cho Select
        onOpen: Optional[Union[State, Callable]] = None, # chỉ dùng cho Select
        onClose: Optional[Union[State, Callable]] = None, # chỉ dùng cho Select
        onChange: Optional[Union[State, Callable]] = None,
        onInputChange: Optional[Union[State, Callable]] = None,
        onMousePress: Optional[Union[State, Callable]] = None,
        options: Optional[Union[State, object]] = None,
        hiddenLabel: Optional[Union[State, bool]] = False,
        placeholder: Optional[Union[State, str]] = None,
        required: Optional[Union[State, bool]] = False,
        rows: Optional[Union[State, int, str]] = None,
        select: Optional[Union[State, bool]] = False,
        selected: Optional[Union[State, bool]] = False,
        selectOptions: Optional[Union[State, List]] = None,
        SelectProps: Optional[Union[State, Dict]] = None,
        size: Optional[Union[State, str]] = "medium",
        slotProps: Optional[Union[State, Dict]] = None,
        slots: Optional[Union[State, Dict]] = None,
        sx: Optional[Union[State, Dict, Callable, str]] = None,
        step: Optional[Union[State, int, float]] = None,
        renderSelectOptions: Optional[Union[State, Callable]] = None,
        renderOption: Optional[Union[State, Callable]] = None,
        renderValue: Optional[Union[State, Callable]] = None,
        renderTags: Optional[Union[State, Callable]] = None,
        readOnly: Optional[Union[State, bool]] = False, # chỉ dùng cho Select
        type: Optional[Union[State, str]] = "text",
        treeView: Optional[Union[State, bool]] = False,
        value = None,
        variant: Optional[Union[State, str]] = "outlined",
        *args, **kwargs
    ) -> None:
        super().__init__(*args, **kwargs)
        self.setObjectName(str(uuid.uuid4()))
        start_time = time.time()

        self._key = key
        
        if self._key == "proxyTypekyyyyyyyyyyyyyyyyy":
            print('ProfileTextField__________________', self._key)

        self._key = key
        self._autoComplete = autoComplete
        self._autoFocus = autoFocus
        self._classes = classes
        self._color = color
        self._defaultValue = defaultValue
        self._disabled = disabled
        self._error = error
        self._FormHelperTextProps = FormHelperTextProps
        self._fullWidth = fullWidth
        self._helperText = helperText
        self._id = id
        self._InputLabelProps = InputLabelProps
        self._inputProps = inputProps
        self._InputProps = InputProps
        self._inputRef = inputRef
        self._inputValue = inputValue
        self._label = label
        self._max = max
        self._margin = margin
        self._maxRows = maxRows
        self._min = min
        self._minRows = minRows
        self._multiline = multiline
        self._multiple = multiple
        self._menuHeight = menuHeight
        self._name = name
        self._getOptionLabel = getOptionLabel
        self._onChange = onChange
        self._onInputChange = onInputChange
        self._onMousePress = onMousePress
        self._options = options
        self._hiddenLabel = hiddenLabel
        self._placeholder = placeholder
        self._required = required
        self._rows = rows
        self._select = select
        self._selected = selected
        self._selectOptions = selectOptions
        self._SelectProps = SelectProps
        self._size = size
        self._slotProps = slotProps
        self._slots = slots
        self._sx = sx
        self._step = step
        self._renderSelectOptions = renderSelectOptions
        self._renderOption = renderOption
        self._renderValue = renderValue
        self._renderTags = renderTags
        self._type = type
        self._treeView = treeView
        self._value = value
        self._variant = variant

        self.__init_ui()
        
        end_time = time.time()
        
        # print("time_loading_textfield___________________________", (end_time - start_time)/0.00013971328735351562)


    def _connect_signals_props(self):
        if isinstance(self._value, State):
            self._value.valueChanged.connect(self._set_data)

    def __init_ui(self):
        self._init_done = False

        self._connect_signals_props()

        self.theme = useTheme()
        
        i18n.langChanged.connect(self._set_translated_label)

        if self._fullWidth:
            if not self._sx:
                self._sx = {}
            self._sx.update({"width": "100%"})

        self._data = None

        self._tagsWidget = None
        self._selected_options = {}
        self._inputFieldHasFocusIn = False


        self._inputField = None
        self._popup = None
        self._popupShowing = False
        self._btnSelect = None

        self._isDateTimeType = self._type == "date" or self._type == "time" or self._type == "date-time" or self._type == "color"

        self._menu = None
        self._menuItems, self._setMenuItems = useState([])
        
        self.renderSearchMenuItems.connect(self._render_search_menu_items)


        self.setLayout(QHBoxLayout())
        self.layout().setContentsMargins(0,0,0,0)
        
        self._focused, self._setFocused = useState(False)
        self._hovered, self._setHovered = useState(False)

        if self._type == "int":
            self._inputField: PySpinBox = PySpinBox
            # self._inputField.setValue = self._set_spinbox_value
        elif self._type == "float":
            self._inputField: PyDoubleSpinBox = PyDoubleSpinBox
        elif self._type == "date-time":
            self._inputField: PyDateTimeEdit = PyDateTimeEdit
        elif self._type == "date":
            self._inputField: PyDateEdit = PyDateEdit
        elif self._type == "time":
            self._inputField: PyTimeEdit = PyTimeEdit
        else: # email, password, select
            if self._multiline:
                self._inputField: PyPlainTextEdit = PyPlainTextEdit
            elif self._multiple:
                if self._renderTags:
                    self._inputField: PyPlainTextEditMultiple = PyPlainTextEditMultiple
                else:
                    self._inputField: PyLineEdit = PyLineEdit
            else:
                self._inputField: PyLineEdit = PyLineEdit

        self._inputField = self._inputField(
            label=self._label,
            onMouseEnter=self.onMouseEnter,
            onMouseLeave=self.onMouseLeave,
            onFocusIn=self.onFocusIn,
            onFocusOut=self.onFocusOut,
            onMouseRelease=self.onMouseRelease,
            onChange=self.onChange,
            variant=self._variant,
            size=self._size,
            sx=f"""
                QComboBox, QDateEdit, QDateTimeEdit, QDial, QDoubleSpinBox, QFontComboBox,
                QLineEdit, QPlainTextEdit, QSpinBox, QTextEdit, QTimeEdit {{
                    border: none;
                    width: 100%;
                    background-color: transparent;
                    padding-left: {"0" if (self._InputProps and self._InputProps.get('startAdornment') or self._variant == "standard") else "8"}px;
                }}
                QPlainTextEdit {{
                    border: none;
                    width: 100%;
                    min-height: 22px;
                    background-color: transparent;
                    padding-left: {"0" if (self._InputProps and self._InputProps.get('startAdornment') or self._variant == "standard") else "5"}px;
                }}
            """
        )

        if self._multiple:
            if self._renderTags:
                self.setMinimumHeight(44)
            else:
                self.setMinimumHeight(38)

        if self._type == "date":
            self._inputField.dateChanged.connect(self._set_date_picker)
        elif self._type == "time":
            self._inputField.timeChanged.connect(self._set_time_picker)
        elif self._type == "color":
            pass
        elif  self._type == "text" or self._select: # có thể lỗi ở đây
            # chỗ này nhằm mục đích tạo ra tính năng search autocomplete
            self._inputField.textChanged.connect(self._on_text_changed)

        self.layout().addWidget(self._inputField)

        self._setup_input_props()
        self._setupy_input_type()

        if self._defaultValue is not None:
            self._set_data(self._defaultValue, setText=True)

        if self._value is not None:
            self._set_data( self._value, setText=True)

        if self._disabled:
            self.setEnabled(False)

        PyWidgetBase._installTooltipFilter(self)


        # ASYN INIT
        QTimer.singleShot(0, self._scheduleSetupSelectMode)
        
        # chỗ này nghiên cứu lại kỹ hơn, chưa kiểm định lỗi, ít khi menu dài tới hàm trăm phần tử, nên k nhất thiết load trước
        # QTimer.singleShot(500, self._scheduleCraeteMenu) 

        self.theme.state.valueChanged.connect(self._onThemeChanged)
        # QTimer.singleShot(0, self._scheduleSetStyleSheet)
        self._set_stylesheet()
        
        if self._multiple:
            QTimer.singleShot(0, self._scheduleSetupDefaultTags)
            
        self.destroyed.connect(lambda obj: self._onDestroy())
        
        self._init_done = True

    def _onDestroy(self, obj=None):
        # Cancel task nếu đang chạy
        if hasattr(self, "_setupDefaultTags") and self._setupDefaultTags and not self._setupDefaultTags.done():
            self._setupDefaultTags.cancel()
        if hasattr(self, "_setupStyleSheet") and self._setupStyleSheet and not self._setupStyleSheet.done():
            self._setupStyleSheet.cancel()
        if hasattr(self, "_setupSelectMode") and self._setupSelectMode and not self._setupSelectMode.done():
            self._setupSelectMode.cancel()
        if hasattr(self, "_setupMenu") and self._setupMenu and not self._setupMenu.done():
            self._setupMenu.cancel()
            
    def _onThemeChanged(self):
        if not self.isVisible():
            return
        QTimer.singleShot(0, self._scheduleSetStyleSheet)
            
    def _scheduleSetupDefaultTags(self):
        self._setupDefaultTags = asyncio.ensure_future(self._lazySetupDefautTags())

    async def _lazySetupDefautTags(self):
        if self._defaultValue:
            for option in self._defaultValue:
                key = self._getOptionLabel(option)
                self._selected_options.update({key: option})
            # print('self._selected_options______________', self._selected_options)
            self.tags = self._renderTags(self._selected_options, self.getTagProps)
            # self.tags = self._renderTags(self._defaultValue, self.getTagProps)
            self._setFlowLayout(FlowLayout(self, children=self.tags + [self._inputFieldMultiple]))
            
    def _scheduleSetStyleSheet(self):
        self._setupStyleSheet = asyncio.ensure_future(self._lazy_set_stylesheet())

    async def _lazy_set_stylesheet(self):
        self._set_stylesheet()
        
    def _scheduleSetupSelectMode(self):
        self._setupSelectMode = asyncio.ensure_future(self._lazy_setupSelectMode())

    async def _lazy_setupSelectMode(self):
        self._setup_select_mode()

    def _scheduleCraeteMenu(self):
        self._setupMenu = asyncio.ensure_future(self._lazy_create_menu())

    async def _lazy_create_menu(self):
        self._create_menu()

    def _setup_select_mode(self):
        if self._select:
            self.updatePopupPosition.connect(self._update_popup_position)

            # đặt ở đây để có _selectedKeys truyền cho PyLineEditMultiple => setup placeholder đúng
            if self._options:
                self._selectedKeys, self._setSelectedKeys = useState(self._options[0] if isinstance(self._options[0], str) else self._options[0].get("value") or "")

                if self._multiple:
                    if self._defaultValue:
                        selectedKeys = ["has_defaultValue"] # để thiết lập placeholder cho đúng
                        self._selectedKeys, self._setSelectedKeys = useState(selectedKeys)
                else:
                    self._selectedKeys, self._setSelectedKeys = useState(
                        self._options[0] if isinstance(self._options[0], str) 
                        else self._getOptionLabel(self._options[0]) if self._getOptionLabel else self._options[0].get("value") or ""
                    )
            elif self._renderOption: # cho Select - select thi k render tag ma render text value
                self._selectedKeys, self._setSelectedKeys = useState(self._data)

            elif self._selectOptions: 
                self._selectedKeys, self._setSelectedKeys = useState(self._data)

            self.action_frame = QWidget(self)
            self.action_frame.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
            hlo_action_frame = QHBoxLayout(self.action_frame)
            hlo_action_frame.setContentsMargins(0,0,0,0)
            self.action_frame.setFixedWidth(54)
            self.action_frame.layout().setAlignment(Qt.AlignRight | Qt.AlignVCenter)
            # self.action_frame.setStyleSheet("border: 1px solid red;")
            # self.setup_action_frame_and_adornment()

            if self._multiple:
                if self._renderTags:
                    self._inputFieldMultiple = PyLineEditMultiple(
                        textField=self,
                        label=self._label,
                        placeholder=self._placeholder, 
                        size=self._size, 
                        selectedKeys=self._selectedKeys,
                        onMouseEnter=self.onMouseEnter,
                        onMouseLeave=self.onMouseLeave,
                        onFocusIn=self.onFocusInMultiple,
                        onFocusOut=self.onFocusOut,
                        onMouseRelease=self.onMouseRelease,
                        onChange=self.onChange,
                    )
                    self._inputFieldMultiple.textChanged.connect(self._on_text_changed)
                    self._inputFieldMultiple.installEventFilter(self)
                    self._flowLayout, self._setFlowLayout = useState(FlowLayout(self, [self._inputFieldMultiple]))
                    self._flow_layout_view = TagsView(content=self._flowLayout)
                    self._flow_layout_view.layout().setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
                    self._inputField.setReadOnly(True)
                    self._inputField.setLayout(QHBoxLayout())
                    self._inputField.layout().setContentsMargins(0,0,54,0)
                    self._inputField.setPlaceholderText("")
                    self._inputField.layout().addWidget(self._flow_layout_view)

                elif self._renderValue:
                    self._inputField.setText(self._renderValue(self._data))

                if isinstance(self._renderOption, State):
                    self._renderOption.valueChanged.connect(self._create_menu)
            else:
                if not self._isDateTimeType and not isinstance(self._inputField, QSpinBox)  and not isinstance(self._inputField, QDoubleSpinBox):
                    pass

            if self._options and not self._getOptionLabel:
                raise AttributeError("Opp!! 'getOptionLabel' is required attribute!")
            
            if self._InputProps and self._InputProps.get("type") == "search":
                self._btn_clear_text= QPushButton()
                self._btn_clear_text.setFixedSize(24, 24)
                self._btn_clear_text.setIcon(PyIconify(key=QTMUI_ASSETS.ICONS.CLOSE))
                self._btn_clear_text.setCursor(Qt.PointingHandCursor)
                self._btn_clear_text.clicked.connect(self._on_clear_text)
                self._btn_clear_text.hide()
                hlo_action_frame.addWidget(self._btn_clear_text)
                self._btn_clear_text.hide()
            else:
                self._btn_clear_text= QPushButton()
                self._btn_clear_text.setFixedSize(24, 24)
                self._btn_clear_text.setIcon(PyIconify(key=QTMUI_ASSETS.ICONS.CLOSE))
                self._btn_clear_text.setCursor(Qt.PointingHandCursor)
                self._btn_clear_text.clicked.connect(self._on_clear_text)
                self._btn_clear_text.hide()
                hlo_action_frame.addWidget(self._btn_clear_text)
                self._btn_clear_text.hide()
            self._btn_clear_text.setFocusPolicy(Qt.NoFocus)
            self._btn_clear_text.setAttribute(Qt.WA_ShowWithoutActivating, True)

            self._btnSelect = QPushButton()
            self._btnSelect.setCursor(Qt.PointingHandCursor)
            self._btnSelect.setFixedSize(24, 24)
            if self._isDateTimeType:
                self._btnSelect.hide()
                self._btnSelect.setIconSize(QSize(24, 24))
                
            self._btnSelect.setIcon(PyIconify(key=QTMUI_ASSETS.ICONS.ARROW_DOWN))
            self._btnSelect.clicked.connect(self._btn_select_toggle_popup)
            hlo_action_frame.addWidget(self._btnSelect)

            self._inputField.installEventFilter(self)

            if hasattr(self, '_flow_layout_view'):
                self._flow_layout_view.mousePressEvent = self.onMultipleInputFieldClicked
                
            self.setup_action_frame_and_adornment() # cần thiết khi không async setup stylesheet
            
                

    # @timer
    def _set_stylesheet(self, component_styled=None):
        _theme_mode = useTheme().palette.mode
        self.setProperty("variant", self._variant)
        if self._renderTags:
            _renderTags = "True"
        else:
            _renderTags = "False"

        stylesheet = self._get_stylesheet(self._variant, self._size, _theme_mode, str(self._multiple), str(self._multiline), _renderTags )
        stylesheet = stylesheet.replace("_________object_name_______", self.objectName())
        self.setStyleSheet(stylesheet)

    @classmethod
    @lru_cache(maxsize=128)
    def _get_stylesheet(cls, _variant: str, _size: str, _theme_mode: str, _multiple: str, _multiline: str, _renderTags:str):
        """Set the stylesheet for the Box."""
        theme = useTheme()

        # Chung cho toàn bộ input
        MuiInput = theme.components["MuiInput"].get("styles")
        MuiInput_root = MuiInput.get("root")
        MuiInput_root_qss = get_qss_style(MuiInput_root)
        MuiInput_title = MuiInput.get("title")
        MuiInput_title_qss = get_qss_style(MuiInput_title)
        MuiInput_title_slot_focused_qss = get_qss_style(MuiInput_title["slots"]["focused"])
        MuiInput_title_slot_error_qss = get_qss_style(MuiInput_title["slots"]["error"])
        MuiInput_title_slot_disabled_qss = get_qss_style(MuiInput_title["slots"]["disabled"])
        MuiInput_inputField = MuiInput.get("inputField")
        MuiInput_inputField_qss = get_qss_style(MuiInput_inputField)
        MuiInput_inputField_prop_multiline_qss = get_qss_style(MuiInput_inputField["props"]["multiline"][f"{_size}"])
        MuiInput_inputField_prop_multiple_qss = get_qss_style(MuiInput_inputField["props"]["multiple"])

        MuiInputSize = theme.components["MuiInputSize"]["styles"][f"{_size}"]
        MuiInputSize_qss = get_qss_style(MuiInputSize)

        MuiInputSize_not_multiple_qss = get_qss_style(MuiInputSize["no-multiple"])
        MuiInputSize_no_multiple_height = MuiInputSize["no-multiple"]["height"]

        # thiết lập tuỳ theo variant
        PyVariantInput = theme.components[f"Py{_variant.capitalize()}Input"].get("styles")
        PyVariantInput_root = PyVariantInput.get("root")
        PyVariantInput_root_qss = get_qss_style(PyVariantInput_root)
        PyVariantInput_root_slot_hovered_qss = get_qss_style(PyVariantInput_root["slots"]["hover"])
        PyVariantInput_root_slot_focused_qss = get_qss_style(PyVariantInput_root["slots"]["focus"])
        
        PyVariantInput_inputField_root = PyVariantInput.get("inputField")["root"]
        PyVariantInput_inputField_root_qss = get_qss_style(PyVariantInput_inputField_root)

        PyVariantInput_root_prop_hasStartAdornment_qss = ""
        if PyVariantInput_inputField_root.get("props"):
            PyVariantInput_root_prop_hasStartAdornment_qss = get_qss_style(PyVariantInput_inputField_root["props"]["hasStartAdornment"])

        MuiInputSize = theme.components["MuiInputSize"]["styles"][f"{_size}"]
        MuiInputSize_qss = get_qss_style(MuiInputSize)

        # print('MuiInputSize_qss______________', MuiInputSize_qss)

        _________object_name_______ = "_________object_name_______"


        input_base_stylesheet = f"""
                #{_________object_name_______} {{
                    {MuiInput_root_qss}
                    {MuiInputSize_qss}
                    {MuiInputSize_not_multiple_qss if not (_multiple=="True" and _renderTags=="True") and not _multiline=="True" else ""}
                }}

                #{_________object_name_______} QComboBox, QDateEdit, QDateTimeEdit, QDial, QDoubleSpinBox, QFontComboBox,
                QLineEdit, PyPlainTextEdit, QSpinBox, QTextEdit, QTimeEdit {{
                    {MuiInput_inputField_qss}
                }}
                #{_________object_name_______}[multipleHasValue=true] QPlainTextEdit {{
                    {MuiInput_inputField_prop_multiple_qss}
                }}
                #{_________object_name_______} PyPlainTextEdit, QTextEdit{{
                    {MuiInput_inputField_prop_multiline_qss}
                }}
                #{_________object_name_______} QPlainTextEdit{{
                    min-height: 40px;
                }}
                #{_________object_name_______}::title {{
                    {MuiInput_title_qss}
                }}
                #{_________object_name_______}[focused=true]::title {{
                    {MuiInput_title_slot_focused_qss}
                }}
                #{_________object_name_______}[error=true]::title {{
                    {MuiInput_title_slot_error_qss}
                }}
                #{_________object_name_______}[disabled=true]::title {{
                    {MuiInput_title_slot_disabled_qss}
                }}
            """

        _variant_stylesheet = f"""
                #{_________object_name_______}[variant={_variant}] {{
                    {PyVariantInput_root_qss}
                    {MuiInputSize_qss}
                }}
                #{_________object_name_______}[variant={_variant}]  QLineEdit, QPlainTextEdit, QTextEdit {{
                    {PyVariantInput_inputField_root_qss}
                }}
                #{_________object_name_______}[outlinedHasStartAdornment=true]  QLineEdit, QPlainTextEdit, QTextEdit {{
                    {PyVariantInput_root_prop_hasStartAdornment_qss}
                }}
                #{_________object_name_______}[hovered=true] {{
                    {PyVariantInput_root_slot_hovered_qss}
                }}
                #{_________object_name_______}[focused=true] {{
                    {PyVariantInput_root_slot_focused_qss}
                }}
            """
        
        return input_base_stylesheet + _variant_stylesheet



    def _create_menu(self):
        print("_create_menu called")

        if not self._menu:
            self._menu = Menu(
                maxHeight=self._menuHeight,
                children=self._menuItems
            )

        if isinstance(self._renderOption, State): # dành cho Select, option được tạo luôn khi gọi renderOption
            if isinstance(self._renderOption.value, Callable):
                self._selectOptions = self._renderOption.value()
                for option in self._selectOptions:
                    if isinstance(option, Li):
                        option._set_selected_keys(self._selectedKeys)
                        option._set_on_changed(self._selected_change)

        elif self._selectOptions and len(self._selectOptions) and isinstance(self._selectOptions[0], QWidget):
            for option in self._selectOptions:
                if isinstance(option, MenuItem):
                    if hasattr(self, '_selectedKeys'):
                        option._set_selected_keys(self._selectedKeys)
                    option._set_on_changed(self._selected_change)

        elif self._options:
            self._selectOptions = []
            for _option in self._options:
                props = {}
                option = self._renderOption(props, _option)

                if isinstance(option, Li):
                    option._set_selected_keys(self._selectedKeys)
                    option._set_on_changed(self._selected_change)
                self._selectOptions.append(option)

        elif self._renderOption and isinstance(self._renderOption, Callable):
            self._selectOptions = self._renderOption(**(_get_fn_args(self._renderOption)))
            for option in self._selectOptions:
                if isinstance(option, Li):
                    option._set_selected_keys(self._selectedKeys)
                    option._set_on_changed(self._selected_change)

        self._setMenuItems(self._selectOptions)
      
            
    def onFocusIn(self, event=None):
        self._setFocused(True)
        self._show_label()
        if self._variant == "filled":
            self._set_filled_foucusin_prop(True)
        if hasattr(self, '_inputFieldMultiple'):
                self._inputFieldMultiple.setFocus(Qt.OtherFocusReason)
            
    def onFocusInMultiple(self, event=None):
        self.onFocusIn(event)

    def onFocusOut(self, event=None):
        print('onFocusOut______________5555555_______')
        
        # dành cho thiết lập khi nhấn vào nút clear hay select
        if hasattr(self, '_btn_clear_text') and QApplication.widgetAt(QCursor.pos()) is self._btn_clear_text:
            try:
                QTimer.singleShot(0, lambda: self._inputField.setFocus(Qt.OtherFocusReason))
            except Exception as e:
                print(f"Error setting focus: {e}")
            return

        if hasattr(self, '_btnSelect') and QApplication.widgetAt(QCursor.pos()) is self._btnSelect:
            try:
                QTimer.singleShot(0, lambda: self._inputField.setFocus(Qt.OtherFocusReason))
            except Exception as e:
                print(f"Error setting focus: {e}")
            return
        
        if hasattr(self, '_inputFieldMultiple') and QApplication.widgetAt(QCursor.pos()) is self._inputFieldMultiple:
            try:
                QTimer.singleShot(0, lambda: self._inputFieldMultiple.setFocus(Qt.OtherFocusReason))
            except Exception as e:
                print(f"Error setting focus: {e}")
            return
        
        self._setFocused(False)
        print('onFocusOut______________77777777777_______')
        

        # nếu inputValue hiện tại không nằm trong options thì clear nó đi
        if self._select:
            if self._multiple:
                if len(self._selectedKeys.value) == 0:
                    self._inputFieldMultiple.setText("")
                    self._inputFieldMultiple._set_placer_holder_text()
                    self._hide_label()
            else:
                if isinstance(self._inputField, PyLineEdit):
                    current_input_value = self._inputField.text()
                elif isinstance(self._inputField, PyPlainTextEdit):
                    current_input_value = self._inputField.toPlainText()
                    
                option_values = []
                options = self._options
                if options:
                    for option in options:
                        label = option
                        if self._getOptionLabel:
                            label = self._getOptionLabel(option)
                        option_values.append(label.lower())
                if current_input_value.lower() not in option_values:
                    self._set_data(None)
                    if isinstance(self._inputField, PyLineEdit):
                        self._inputField.setText("")
                    elif isinstance(self._inputField, PyPlainTextEdit):
                        self._inputField.setPlainText("")
                    self._inputField._set_placer_holder_text()
                else:
                    # set lại data dựa trên inputValue
                    for option in options:
                        label = option
                        if self._getOptionLabel:
                            label = self._getOptionLabel(option)
                        if label.lower() == current_input_value.lower():
                            self._set_data(option)
                            self._inputField.setText(label)
                            break

        if self._data is None:
            if self._variant == "filled":
                self._set_filled_foucusin_prop(False)
            self._hide_label()

        if not self.underMouse():
            if hasattr(self, "_btn_clear_text") and self._btn_clear_text.isVisible():
                self._btn_clear_text.setVisible(False)


            

    def onMouseEnter(self, event=None):
        self._setHovered(True)
        if self._data:
            if hasattr(self, "_btn_clear_text") and not self._btn_clear_text.isVisible():
                self._btn_clear_text.setVisible(True)
        
    def onMouseLeave(self, event=None):
        self._setHovered(False)

    def onMouseRelease(self, event=None):
        if self._onMousePress:
            self._onMousePress()
        if self._select:
            if not self._isDateTimeType and not isinstance(self._inputField, QPlainTextEdit):
                if self._inputField.selectedText() != "":
                    return

    def onMultipleInputFieldClicked(self, event=None):
        if self._onMousePress:
            self._onMousePress()
        if self._select:
            if not self._isDateTimeType and not isinstance(self._inputField, QPlainTextEdit):
                if self._inputField.selectedText() != "":
                    return
            self._toggle_popup()

    def onChange(self, value=None):
        # nếu là select thì chưa set_value
        if not self._select:
            self._set_data(value, setText=False)


    def _set_state(self, stateValue: dict):
        value = stateValue.get("value")
        state = stateValue.get("state")

    def _set_data(self, value=None, valueChanged=True, setText=True):
        
        _selected_options = self._selected_options.copy()
        
        for key, val in _selected_options.items():
            if isinstance(value, list) and val not in value:
                del self._selected_options[key]
                
        if isinstance(value, State):
            self._data = value.value
        else:
            self._data = value

        if self._type == "int":
            pass
        if self._type == "int": # PySpinBox
            pass
        elif self._type == "float":
            pass
        elif self._type == "date-time":
            pass
        elif self._type == "date":
            pass
        elif self._type == "time":
            pass
        elif self._multiline:
            pass
        elif self._multiple:
            if self._data is not None:
                self._set_multiple_has_value_prop(True)
            else:
                self._set_multiple_has_value_prop(False)

        else: # email, password, select
            if self._multiline or self._multiple:
                self._inputField = PyPlainTextEdit
            else: # PyLineEdit
                if self._data is not None:
                    if self._variant == "filled":
                        self._set_filled_has_value_prop(True) # làm cho inputField padding top 1 khoảng cho giống mui JS
                else:
                    if self._variant == "filled":
                        self._set_filled_has_value_prop(False) # làm cho inputField padding top 1 khoảng cho giống mui JS

        if value is not None: # value == "" cũng vào đây
            if self._data != "":
                self._show_label()

        if valueChanged:
            self.valueChanged.emit(self._data)

        if setText:
            self._set_text()

    def _set_text(self): # value
        if self._data == None:
            if self._type == "text":
                if isinstance(self._inputField, QPlainTextEdit):
                    self._inputField.setPlainText("")
                elif isinstance(self._inputField, QLineEdit):
                    self._inputField.setText("")
            return
        
        if self._data == "" and not self._select:
            if isinstance(self._inputField, QPlainTextEdit):
                self._inputField.setPlainText("")
            elif isinstance(self._inputField, QLineEdit):
                self._inputField.setText("")
            return

        if self._select:
            if not self._multiple: # multiple thì set tag không cần set text
                if isinstance(self._data, dict):
                    if self._getOptionLabel:
                        text = self._getOptionLabel(self._data)
                        self._inputField.setText(str(text))
                else:
                    if self._getOptionLabel:
                        for option in self._options:
                            if isinstance(option, dict):
                                if self._getOptionLabel(option) == self._data: # options khong phai dạng value label
                                    text = self._getOptionLabel(option)
                                    self._inputField.setText(str(text))
                                    break
                            elif isinstance(option, str):
                                if option == self._data:
                                    text = self._getOptionLabel(option)
                                    self._inputField.setText(str(text))
                                    break
                    else:
                        self._inputField.setText(str(self._data))

            else: # multiple
                if self._renderValue:
                    self._inputField.setText(self._renderValue(self._data))

        else:
            if self._multiline:
                if isinstance(self._data, dict):
                    if self._getOptionLabel:
                        text = self._getOptionLabel(self._data)
                        self._inputField.setPlainText(text)
                else:
                    self._inputField.setPlainText(str(self._data))
            else:
                if isinstance(self._data, dict):
                    if self._getOptionLabel:
                        text = self._getOptionLabel(self._data)
                        self._inputField.setText(text)
                else:
                    if self._type == "date":
                        if isinstance(self._data, str):
                            with signals_blocked(self._inputField):
                                self._inputField.setDate(QDate.fromString(self._data, self._inputField.displayFormat()))
                        else:
                            self._inputField.setDate(self._data)
                    elif self._type == "time":
                        if isinstance(self._data, str):
                            self._inputField.setTime(QTime.fromString(self._data, self._inputField.displayFormat()))
                        else:
                            self._inputField.setTime(self._data)
                    elif self._type == "int":
                        self._inputField.setValue(int(self._data)) 
                    elif self._type == "float":
                        self._inputField.setValue(float(self._data)) 
                    else:
                        if isinstance(self._inputField, QPlainTextEdit):
                            self._inputField.setPlainText(self._data) 
                        if isinstance(self._inputField, QLineEdit):
                            self._inputField.setText(str(self._data)) 


    def _set_slot(self, data):
        if data.get("slot") == "error":
            self._inputField.setProperty("p-error", "true")
        elif data.get("slot") == "valid":
            self._inputField.setProperty("p-error", "false")

    def _on_text_changed(self, s=None):
        # print("s: ", s)
        
        if not self._init_done:
            return

        # Callback khi input thay đổi. Lấy dữ liệu người dùng gõ tuỳ ý mà chưa cần chọn ngay để kiểm soát hoàn toàn quá trình lọc, reset, hoặc validation.
        if self._onInputChange:
            self._onInputChange(s)

        if not self._autoComplete:
            return
        
        if s is None:
            return
        
        if s == "":
            self._selected_options = {}

        if self._InputProps and self._InputProps.get("type") == "search":
            # thay doi icon clear text
            pass
        
        if self._multiline:
            if self._inputField.toPlainText() == "":
                self._btn_clear_text.hide()
            else:
                self._btn_clear_text.show()
        elif self._multiple:
            if self._inputFieldMultiple.text() == "":
                self._btn_clear_text.hide()
            else:
                self._btn_clear_text.show() 
        else:
            if self._inputField.text() == "":
                self._btn_clear_text.hide()
            else:
                self._btn_clear_text.show() 

        # tìm kiếm các key phù hợp kết quả tìm kiếm
        currentValue = None
        if isinstance(self._data, dict):
            if self._getOptionLabel:
                currentValue = self._getOptionLabel(self._data)
        else:
            currentValue = self._data

        # if s != currentValue and self._InputProps and self._InputProps.get("type") == "search":
        if s != currentValue or s == "" and self._init_done:
            search_options_results = []
            if self._options:
                for option in self._options:
                    if isinstance(option, dict):
                        if self._getOptionLabel:
                            key = self._getOptionLabel(option)
                            if s in key:
                                search_options_results.append(option)
                    else:
                        key = option
                        if s in key:
                            search_options_results.append(option)

            self.renderSearchMenuItems.emit(search_options_results)

        if self._error:
            self._error = None
            
        if self._init_done:
            self._show_popup()

    def _render_search_menu_items(self, search_options_results):
        search_menu_items = []
        if len(search_options_results):
            for _option in search_options_results:
                # props = {'text': self._getOptionLabel(_option)}
                props = {}
                option = self._renderOption(props, _option)
                if isinstance(option, Li):
                    option._set_selected_keys(self._selectedKeys)
                    option._set_on_changed(self._selected_change)
                search_menu_items.append(option)
            if not len(search_menu_items):
                search_menu_items = [Li(key="No options", selected=True, value=">>>>>>No options")]
        else:
            search_menu_items = [Li(text="No options", selected=True, value=">>>>>>No options")]
            
        self._setMenuItems(search_menu_items)

    def validate_type_int(self):
        # Kiểm tra tính hợp lệ của min và max
        if self._min is not None and self._max is not None:
            if not isinstance(self._min, int) or not isinstance(self._max, int):
                raise ValueError("Both 'min' and 'max' must be integers.")
            if self._max < self._min:
                raise ValueError("'max' must be greater than or equal to 'min'.")
        else:
            raise ValueError("Both 'min' and 'max' required.")
        return True

    def _setupy_input_type(self):
        if self._type == "int":
            if self._min:
                if not isinstance(self._min, int):
                    raise ValueError("'min' must be integer.")
                self._inputField.setMinimum(self._min)
            if self._max:
                if not isinstance(self._max, int):
                    raise ValueError("'max' must be integer.")
                self._inputField.setMaximum(self._max)
            if self._min and self._max:
                if self._max < self._min:
                    raise ValueError("'max' must be greater than or equal to 'min'.")
            if self._step:
                if not isinstance(self._step, int):
                    raise ValueError("'step' must be integer.")
                self._inputField.setSingleStep(self._step)
        elif self._type == "float":
            if self._min:
                if not isinstance(self._min, float):
                    raise ValueError("'min' must be float.")
                self._inputField.setMinimum(self._min)
            if self._max:
                if not isinstance(self._max, float):
                    raise ValueError("'max' must be float.")
                self._inputField.setMaximum(self._max)
            if self._min and self._max:
                if self._max < self._min:
                    raise ValueError("'max' must be greater than or equal to 'min'.")
            if self._step:
                if not isinstance(self._step, int) or not isinstance(self._step, float):
                    raise ValueError("'step' must be number (int or float).")
                self._inputField.setSingleStep(self._step)

        elif self._type == "password":
            self._inputField.setEchoMode(QLineEdit.Password) 
        elif self._type == "email":
            pass

    def _set_date_picker(self, date: QDate):
        if isinstance(self.startAdornment, DatePicker) or isinstance(self.startAdornment, ZhDatePicker):
            self.startAdornment.setDate(date)
            self._data = date.toString()
            self.valueChanged.emit(self._data)

    def _set_time_picker(self, time: QTime):
        if isinstance(self.startAdornment, TimePicker) or isinstance(self.startAdornment, AMTimePicker):
            self.startAdornment.setTime(time)
            self._data = time.toString()
            self.valueChanged.emit(self._data)

    def _setup_input_props(self):
        if self._InputProps:
            if self._InputProps.get('startAdornment') and isinstance(self._InputProps.get('startAdornment'), QWidget):
                self.startAdornment = self._InputProps.get('startAdornment')
                self.layout().insertWidget(0, self.startAdornment)
                self._set_prop_has_start_adornment(True)
                self._inputField.setProperty('has-start-adornment', "true")
                if isinstance(self.startAdornment, DatePicker) or isinstance(self.startAdornment, ZhDatePicker):
                    self.startAdornment.dateChanged.connect(self._set_data)
                    if isinstance(self.startAdornment, ZhDatePicker):
                        self._inputField.setDisplayFormat("yyyy/MM/dd")
                    else:
                        self._inputField.setDisplayFormat("dd/MM/yyyy")
                    
                if isinstance(self.startAdornment, AMTimePicker) or isinstance(self.startAdornment, TimePicker):
                    self.startAdornment.timeChanged.connect(self._set_data)
                    if isinstance(self.startAdornment, AMTimePicker):
                        self._inputField.setDisplayFormat(f"hh:mm{'{}'.format(':ss') if self.startAdornment.isSecondVisible() else ''} ap")
                    else:
                        self._inputField.setDisplayFormat(f"hh:mm{'{}'.format(':ss') if self.startAdornment.isSecondVisible() else ''}")

            if self._InputProps.get('endAdornment') and isinstance(self._InputProps.get('endAdornment'), QWidget):
                self.endAdornment = self._InputProps.get('endAdornment')
                self.layout().insertWidget(-1, self.endAdornment)

    def _on_clear_text(self):
        self._onClearText = True

        if self._multiple:
            self._setSelectedKeys([])
            self._btn_clear_text.hide()
            self._selected_options = {}
            self._set_data([])
            self._setFlowLayout(FlowLayout(self, children=[self._inputFieldMultiple]))
        elif self._multiline:
            self._set_data("")
            self._inputField.setPlainText("")
        elif self._type == "date":
            self._set_data(None)
            self._inputField.setDate(QDate.currentDate())
        elif self._type == "time":
            self._set_data(None)
            self._inputField.setTime(QTime.currentTime())
        elif self._type == "int":
            self._set_data(None)
            self._inputField.setValue(0)
        elif self._type == "float":
            self._set_data(None)
            self._inputField.setValue(0.0)
        elif self._select:
            self._set_data(None)
            self._inputField.setText("")
        else: # text, email, password
            self._set_data("")
            self._inputField.setText("")
            
        self._btn_clear_text.hide()
        QTimer.singleShot(0, lambda: self._inputField.setFocus(Qt.OtherFocusReason))
        
        if self._onChange:
            self._onChange(self._data)
            
        if self._onInputChange:
            self._onInputChange("")
        

    def _update_popup_position(self):
        if hasattr(self, "_popup") and self._popup is not None:
            popup_position = self.mapToGlobal(QPoint(0, self.height()))
            self._popup.setGeometry(popup_position.x(), popup_position.y(), self.width(), 50)


    def _hide_popup(self):
        if self._select:
            if self._popup and self._popup.isVisible():
                self._popup.setVisible(False)
            if self._isDateTimeType:
                icon = PyIconify(key=QTMUI_ASSETS.ICONS.CALENDAR)
            else:
                icon = PyIconify(key=QTMUI_ASSETS.ICONS.ARROW_DOWN)

            if self._btnSelect:
                self._btnSelect.setIcon(icon)
        self._popupShowing = False


    def _btn_select_toggle_popup(self, e=None):
        if self._popup and self._popupShowing:
            print('__btn_select_toggle_popup_______________hide_popup')
            self._hide_popup()
        else:
            print('__btn_select_toggle_popup______________show_popup')
            self._show_popup()

    def _toggle_popup(self, e=None):
        if self._select and not self._popupShowing:
            self._show_popup()
            return
        if self._select and (self._data or self._data == "") and self._inputField.hasFocus():
            return
        if self._popup and self._popupShowing:
            self._hide_popup()
        else:
            self._show_popup()

    def _show_popup(self, search=None):
        
        if not self._menu:
            self._create_menu()
        
        if self._popup and self._popupShowing:
            return

        if not self._popup:
            self._popup = QFrame(self)
            self._popup.setFocusPolicy(Qt.NoFocus)
            self._popup.setAttribute(Qt.WA_ShowWithoutActivating, True)
            self._popup.setWindowFlags(Qt.Window |  Qt.FramelessWindowHint | Qt.NoDropShadowWindowHint)
            self._popup.setAttribute(Qt.WA_TranslucentBackground, True) # Enable translucent background => setStyleSheet ko có tác dụng gì
            QApplication.instance().installEventFilter(self)
            self._popup.setObjectName(str(uuid.uuid4()))
            self._popup.setLayout(QVBoxLayout())
            self._popup.layout().setContentsMargins(0,0,9,9)
            self._popup.layout().addWidget(self._menu)
            self._popup.setAutoFillBackground(False)
            self._popup.setStyleSheet(f"#{self._popup.objectName()} {{background: transparent;border: 2px solid red;}}")

        # Lấy điểm cục bộ (x, y) của relativeTo và chuyển sang global
        global_point = self.mapToGlobal(QPoint(0, self.height()))
        
        # tính toán vị trí hiển thị popup
        main_window_pos_y = QApplication.instance().mainWindow.height()
        self_pos_y = global_point.y()
        
        self._popup.move(-3000, -3000) # di chuyển tạm để lấy kích thước
        self._popup.setVisible(True)

        if (main_window_pos_y - self_pos_y) < self._menu.height():
            global_point.setY(global_point.y() - self.height() - self._menu.height())
        
        self._popup.move(global_point)
        self._popup.setMinimumWidth(self.width())

        self._menu.setVisible(True)

        if self._isDateTimeType:
            icon = PyIconify(key=QTMUI_ASSETS.ICONS.CALENDAR)
        else:
            icon = PyIconify(key=QTMUI_ASSETS.ICONS.ARROW_UP)
        self._btnSelect.setIcon(icon)
        self._popupShowing = True
        

    def _on_menu_item_selected(self, key):
        self._hide_popup()
        self._data = key

    def _about_to_hide(self):
        self._popupShowing = False


    def getTagProps(self, index):
        return {"onDelete": lambda key=index: self._on_tag_deleted(key)}

    def _on_tag_deleted(self, index):
        self._selected_change(index)

    def _update_selected_options(self, key=None, option=None):
        """
        Hàm này nhằm cập nhật giá trị self._selected_options cho Select
        """
        if key not in list(self._selected_options.values()):
            self._selected_options.update({key: key})
        else:
            self._selected_options.pop(key)

    def _setup_tags(self, key=None, option=None):
        if key:
            for _option in self._options:
                if self._getOptionLabel(_option) == key:
                    option = _option
                    break
        else:
            if option:
                if isinstance(option, dict):
                    key = self._getOptionLabel(option)

        if option not in self._selected_options.values():
            try:
                self._selected_options.update({key: option})
                self.tags = self._renderTags(self._selected_options, self.getTagProps)
            except Exception as e:
                import traceback
                traceback.print_exc()
            self._setFlowLayout(FlowLayout(self, children=self.tags + [self._inputFieldMultiple]))
        else:
            self._selected_options.pop(key)
            self.tags = self._renderTags(self._selected_options, self.getTagProps)
            self._setFlowLayout(FlowLayout(self, children=self.tags + [self._inputFieldMultiple]))

    def _selected_change(self, data):

        if isinstance(data, dict):
            key = data.get("value")
            value = data.get("value")
        else:
            key = data
            value = data

        if self._multiple:
            # for delete option item
            if key in self._selectedKeys.value:
                selected_keys = self._selectedKeys.value.copy()
                selected_keys.remove(key)
                self._setSelectedKeys(selected_keys)
                if self._renderTags:
                    self._setup_tags(key=key)
                elif self._renderValue:
                    self._update_selected_options(key)
                values = []
                for selected_option in self._selected_options.values():
                    values.append(selected_option)

                self._set_data(values)

                if self._onChange:
                    self._onChange(self._data)

                if self._renderValue:
                    self._inputField.setText(self._renderValue(self._data))

                return
            
            if self._renderTags: # cho Autocomplete - render tag
                self._setup_tags(key=key, option=key)
            else: # cho Select
                self._update_selected_options(key)
            
            keys = []
            values = []
            for selected_option in self._selected_options.values():
                if isinstance(selected_option, dict):
                    values.append(selected_option)
                    if self._getOptionLabel:
                        key = self._getOptionLabel(selected_option)
                        keys.append(key)
                elif isinstance(selected_option, str):
                    values.append(selected_option)
                    keys.append(selected_option)

            self._setSelectedKeys(keys)
            self._set_data(values)

            if self._onChange:
                self._onChange(self._data)

            if self._renderValue:
                self._inputField.setText(self._renderValue(self._data))

            self._hide_popup()
        else: # not multiple
            self._setSelectedKeys(key)
            self._set_data(value)
            self._selected_options.update({key: key})                
            self._hide_popup()

            # Call onChange with the new value
            if self._onChange:
                # self._onChange(data) # contronled state mode không đúng, nhận vào list mà onchange lại là dict
                self._onChange(self._data)
                
        if self._menu:
            for item in self._menu.findChildren(Li):
                item.show()

    def _show_label(self):
        if not self._hiddenLabel:
            self._set_translated_label()

    def _set_translated_label(self):
        if not self._hiddenLabel:
            self.setTitle(getTranslatedText(self._label))

    def _hide_label(self):
        print('_hide_label')
        self.setTitle("")

    def _set_hovered_prop(self):
        if self._disabled:
            return
        if isinstance(self._hovered, State):
            self.setProperty("hovered", self._hovered.value)
        elif isinstance(self._hovered, bool):
            self.setProperty("hovered", self._hovered)
        
    def _set_focused_prop(self):
        if isinstance(self._focused, State):
            self.setProperty("focused", self._focused.value)
        elif isinstance(self._focused, bool):
            self.setProperty("focused", self._focused)
        
    def _set_multiple_has_value_prop(self, state):
        self.setProperty("multipleHasValue", state)
        
    def _set_filled_has_value_prop(self, state:bool):
        self.setProperty("filledHasValue", state)

    def _set_filled_foucusin_prop(self, state:bool):
        self.setProperty("filledHasValue", state)

    def _set_prop_has_start_adornment(self, state:bool):
        self.setProperty("hasStartAdornment", state)


    def setup_action_frame_and_adornment(self):
        if self._InputProps and self._InputProps.get('startAdornment') and isinstance(self._InputProps.get('startAdornment'), QWidget):
            pass
        if self._InputProps and self._InputProps.get('endAdornment') and isinstance(self._InputProps.get('endAdornment'), QWidget):
            pass
        
        if hasattr(self, 'action_frame'):
            self.action_frame.move(self.width() - self.action_frame.width(), (self.height() + 6 - self.action_frame.height()) // 2)
            if not self.action_frame.isVisible():
                self.action_frame.show()

    def leaveEvent(self, event):
        if not self.underMouse() and not self._inputField.hasFocus():
            if hasattr(self, "_btn_clear_text") and self._btn_clear_text.isVisible():
                self._btn_clear_text.setVisible(False)
        return super().leaveEvent(event)

    def resizeEvent(self, event):
        self.setup_action_frame_and_adornment()
        return super().resizeEvent(event)
    
    def eventFilter(self, obj, event):
        if self._select:
            if obj == self._inputField or hasattr(self, '_inputFieldMultiple') and obj == self._inputFieldMultiple:
                if event.type() == QEvent.MouseButtonPress:
                    self._show_popup()
            elif self._popup and self._popup.isVisible():
                if event.type() == QEvent.MouseButtonPress and not self.underMouse():
                    if not self._popup.geometry().contains(event.globalPos()):
                        self._hide_popup()
                elif event.type() == QEvent.Wheel and ((not self.underMouse() and not self._popup.underMouse()) or obj == self._inputField):
                    self._hide_popup()
            
        return super().eventFilter(obj, event)

