"""The contract every quad module implements.

Adding a module = drop a file in modules/ that defines a top-level `SPEC`
(a ModuleSpec), then append its import to registry.py. Nothing else needs to
change: enable/disable, feature toggles, the card chrome, and the expand/collapse
plumbing are all provided generically.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Callable, Optional

import gi

gi.require_version("Gtk", "4.0")
from gi.repository import Gtk  # noqa: E402


@dataclass
class Feature:
    """A per-quad on/off switch surfaced in the card's settings popover."""
    id: str
    label: str
    default: bool = True


@dataclass
class ModuleInstance:
    """What a module's build() returns."""
    compact: Gtk.Widget                       # shown in the 2x2 cell
    expanded: Optional[Gtk.Widget] = None     # shown when the quad is expanded
    scroll_expanded: bool = True              # wrap the expanded view in a scroller
    on_show: Optional[Callable[[], None]] = None
    on_hide: Optional[Callable[[], None]] = None
    destroy: Optional[Callable[[], None]] = None


@dataclass
class ModuleSpec:
    id: str
    title: str
    icon: str                                 # nerd-font glyph
    build: Callable[["ModuleContext"], ModuleInstance]
    default_enabled: bool = True
    features: list[Feature] = field(default_factory=list)


class ModuleContext:
    """Handed to a module's build(). Scopes settings to the module and exposes the
    expand/collapse requests so a module can drive the layout without knowing it."""

    def __init__(self, spec: ModuleSpec, settings, services,
                 request_expand: Callable[[str], None],
                 request_collapse: Callable[[], None]):
        self.spec = spec
        self.settings = settings
        self.services = services
        self._request_expand = request_expand
        self._request_collapse = request_collapse

    def expand(self) -> None:
        self._request_expand(self.spec.id)

    def collapse(self) -> None:
        self._request_collapse()

    # feature toggles, scoped to this module
    def feature(self, feature_id: str, default: bool = True) -> bool:
        return self.settings.feature(self.spec.id, feature_id, default)

    def on_settings_changed(self, cb: Callable[[], None]) -> None:
        self.settings.subscribe(cb)
