"""
This module provides a base class for windows that load their UI from external UI files.
"""

import logging
from abc import abstractmethod
from typing import Optional, TypeVar

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

from PySide6.QtCore import QObject
from PySide6.QtUiTools import QUiLoader
from PySide6.QtWidgets import QMainWindow, QWidget

PlaceHolderType = TypeVar("PlaceHolderType", bound=QObject)

logger = logging.getLogger(__name__)


class LoadedWindow(QMainWindow):
	"""
	A base class for windows that load their UI from external .ui files.

	This abstract class provides functionality to load UI files created with Qt Designer
	and set them as the central widget of a QMainWindow. Subclasses must implement
	the setup method to initialize any additional components or connections.

	Attributes:
		No public attributes specific to this class.
	"""

	setUp: bool

	def __init__(self, ui_file_path: str, parent: Optional[QWidget] = None):
		"""
		Initialize a new window with a UI loaded from a file.

		Args:
			ui_file_path: Path to the .ui file created with Qt Designer.
			parent: Optional parent widget. Defaults to None.
		"""

		super().__init__(parent=parent)
		self.setUp = False
		loader = QUiLoader()
		loaded_widget: QWidget = loader.load(ui_file_path, parent)
		self.setCentralWidget(loaded_widget)
		self.setGeometry(loaded_widget.geometry())

	@abstractmethod
	def setup(self):
		"""
		Set up the window after the UI has been loaded.

		This method must be implemented by subclasses to perform any necessary
		initialization steps, such as connecting signals to slots, setting up
		validators, or initializing components.
		"""

		pass

	@override
	def show(self):
		"""
		Display the window and ensure setup is completed first.

		This method overrides QMainWindow's show method to guarantee that
		the setup method is called before the window is displayed. The setup
		method will only be called once, even if show is called multiple times.
		"""

		if not self.setUp:
			self.setup()
			self.setUp = True

		return super().show()

	def find_element(self, type: type, name: str) -> PlaceHolderType:
		"""
		Find a UI element by type and name in the loaded window.

		This method searches for a child widget of the specified type with the
		given name. If the element is not found, it logs an error and exits the program.

		Args:
						type: The type of the UI element to find (e.g., QLabel, QPushButton).
						name: The object name of the UI element as set in Qt Designer.

		Returns:
						The found UI element of the specified type.

		Raises:
						SystemExit: If the element with the given name and type is not found.
		"""
		child = self.window().findChild(type, name)

		if child is None:
			logger.error(f"{name} element not found in UI")
			exit(1)

		return child
