"""
This module defines the ComboBoxInput class, a custom QComboBox widget for selecting Enum values,
with optional support for a "None" value and callback mechanisms for value changes and resizing.
Classes:
	ComboBoxInput(QComboBox, InputInterface):
		A combo box widget that allows selection from an Enum, supports optional "None" value,
		and provides callbacks for value changes and resizing.
"""

from enum import Enum
from typing import Any, Callable, Optional

from cotation.acoustic.display.io_widget.input_interface import InputInterface

try:
	from typing import override
except ImportError:
	from typing_extensions import override  # noqa: F401

from PySide6.QtWidgets import (
	QComboBox,
	QWidget,
)

DEFAULT_VALUE_TEXT = "Veuillez selectionner une valeur."


class ComboBoxInput(QComboBox, InputInterface):
	_value_changed_callback: Optional[Callable[[], Any]]
	_resize_callback: Optional[Callable[[int], Any]]

	def __init__(self, options: type[Enum], has_non_value: bool = True):
		"""
		Initializes the ComboBoxInput widget with the given options.
		Args:
			options (type[Enum]): An enumeration class containing the selectable options for the combo box.
			has_non_value (bool, optional): If True, adds a "None" option to the combo box. Defaults to True.
		Notes:
			- The combo box will display each enum value as a selectable item.
			- If `has_non_value` is True, a "None" entry is added at the top of the list.
			- Sets up internal callbacks for value change and resize events.
		"""

		super().__init__()

		self._value_changed_callback = None
		self._resize_callback = None

		if has_non_value:
			self.addItem("None", None)

		for e in options:
			self.addItem(str(e.value), e)

		# Connect to currentIndexChanged using lambda to prevent connection warnings
		self.currentIndexChanged.connect(lambda _: self._handle_value_changed())

	def _handle_value_changed(self):
		"""
		Handles the event when the input value changes.
		This method triggers the registered value changed callback, if any.
		If a resize callback is registered and the current input value is not None,
		it also triggers the resize callback, passing the value of the current input.
		Callbacks:
			- self._value_changed_callback: Called with no arguments when the value changes.
			- self._resize_callback: Called with the current input value as an argument if it exists.
		Returns:
			None
		"""

		if self._value_changed_callback:
			self._value_changed_callback()
		if self._resize_callback and self.get_input_value() is not None:
			self._resize_callback(self.get_input_value().value)

	@override
	def get_widget(self) -> QWidget:
		"""
		Returns the current widget instance.

		Returns:
			QWidget: The widget instance represented by this object.
		"""
		return self

	def set_value_changed_callback(self, callback: Callable[[], Any]) -> None:
		"""
		Sets the callback function to be invoked when the value changes.

		Args:
			callback (Callable[[], Any]): A function to be called when the value changes.
				This function should take no arguments and can return any value.

		Returns:
			None
		"""
		self._value_changed_callback = callback

	@override
	def set_resize_callback(self, callback: Callable[[int], Any]) -> None:
		"""
		Sets a callback function to be invoked when a resize event occurs.

		Args:
			callback (Callable[[int], Any]): A function that takes an integer (typically representing the new size)
				as its argument and performs an action when a resize event is triggered.

		Returns:
			None
		"""
		self._resize_callback = callback

	def get_input_value(self):
		"""
		Retrieves the current data value from the combobox input widget.

		Returns:
			Any: The data associated with the currently selected item in the combobox.
		"""
		return self.currentData()

	def set_input_value(self, value):
		"""
		Sets the current selection of the combo box based on the provided value.

		Args:
			value: The data value to select in the combo box.

		Notes:
			If the value is found among the combo box's items, the corresponding index is selected.
			If the value is not found, the current selection will be set to an invalid index (-1).
		"""
		value_idx = self.findData(value)
		self.setCurrentIndex(value_idx)

	def get_is_filled(self) -> bool:
		"""
		Checks whether the input value is filled.

		Returns:
			bool: True if the input value is not None, indicating the field is filled; False otherwise.
		"""
		return self.get_input_value() is not None
