# SPDX-License-Identifier: LGPL-3.0-or-later

from .objects import NavigateRequest
from .windows import BaseWindow
from .logging import setup_logger
from .database import AbstractDatabase
from PyQt6.QtWidgets import QApplication
from PyQt6.QtCore import QObject, pyqtSignal, pyqtSlot
from typing import Dict

log = setup_logger(__name__)

class Composer(QObject):
    """
    Application router and window lifecycle manager.

    Manages window registration, navigation between windows,
    and holds the active database connection. Acts as the
    central hub of the application.

    Args:
        app: QApplication instance.
        db: Connected AbstractDatabase instance.
    """
    navigate_request = pyqtSignal(NavigateRequest)

    def __init__(self, app: QApplication, db: AbstractDatabase):
        super().__init__()
        self._db = db
        self._current = None
        self._app = app
        self._registry: Dict[str, Dict[str, object]] = dict()
        self._current_ctx: Dict[str, Dict[str, object]] = dict()

        self.navigate_request.connect(self.navigate)

    @property
    def context(self):
        """
        Return the current navigation context.
        """
        return self._current_ctx

    def register(self, name: str,
                 window: type[BaseWindow],
                 lazy: bool = True):
        """
        Register a window class under a given name.

        Args:
            name: Unique string identifier for the window.
            window: A subclass of BaseWindow.
            lazy: If True, the window is instantiated on first navigation.
                  If False, the window is instantiated immediately.

        Raises:
            TypeError: If window is not a subclass of BaseWindow.
        """
        if not issubclass(window, BaseWindow):
            raise TypeError(f"{window} is not a descendant of BaseWindow")

        entry = {
            "class": window,
            "instance": None,
            "lazy": lazy
        }

        if not lazy:
            entry["instance"] = window(self, self._db)

        self._registry[name] = entry

    def _switch_window(self, window):
        """
        Close the current window and show the new one.
        """
        if self._current:
            self._current.close()
        self._current = window
        self._current.show()

    def _create_window(self, name: str) -> BaseWindow:
        """
        Instantiate or retrieve the window registered under name.

        For lazy windows, creates a new instance on every call.
        For eager windows, returns the existing instance.
        """
        entry = self._registry[name]

        if not entry["lazy"]:
            return entry["instance"]

        instance = entry["class"](self, self._db)
        return instance

    @pyqtSlot(NavigateRequest)
    def navigate(self, request: NavigateRequest):
        """
        Handle a NavigateRequest signal and switch to the target window.

        Raises:
            KeyError: If the target window is not registered.
        """
        if request.target not in self._registry:
            raise KeyError("Window is not registered")

        self._current_ctx = request.context
        window = self._create_window(request.target)
        self._switch_window(window)

    def run(self, start: str):
        """
        Start the application from the given window.

        Args:
            start: Name of the window to show first.

        Raises:
            KeyError: If the start window is not registered.
        """
        if start not in self._registry:
            raise KeyError("Window is not registered")

        window = self._create_window(start)
        self._switch_window(window)
        return self._app.exec()