"""The 2x2 quad region and its expand-over-the-others behaviour.

A Gtk.Overlay stacks two things in the same space:
  * base   : a 2x2 Gtk.Grid of QuadCards
  * overlay: a Revealer that, when a quad expands, fills the whole region (the full
             content width, covering all four cells) with that module's expanded
             view wrapped in a small header carrying a collapse button.

Because the overlay fills the region exactly, the expanded quad is as wide as the
appdrawer below it, and the outer letterbox margins (applied further up the tree)
are untouched — so letterboxing stays identical in every state.
"""

from __future__ import annotations

import gi

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

from lib.border import bordered
from module_base import ModuleSpec
from ui.quadcard import QuadCard


class QuadGrid(Gtk.Overlay):
    def __init__(self, specs: list[ModuleSpec], settings, services):
        super().__init__()
        self.add_css_class("quad-region")
        self.set_vexpand(True)
        self.settings = settings
        self._expanded_id: str | None = None

        self.grid = Gtk.Grid(column_homogeneous=True, row_homogeneous=True,
                             column_spacing=10, row_spacing=10)
        self.grid.add_css_class("quad-grid")
        self.set_child(self.grid)

        self.cards: dict[str, QuadCard] = {}
        for index, spec in enumerate(specs[:4]):
            card = QuadCard(spec, settings, services,
                            self.request_expand, self.request_collapse)
            self.cards[spec.id] = card
            self.grid.attach(bordered(card, radius=16, fill_bg=True),
                             index % 2, index // 2, 1, 1)

        # overlay used for the expanded quad
        self._expand_reveal = Gtk.Revealer(
            transition_type=Gtk.RevealerTransitionType.CROSSFADE,
            transition_duration=180, reveal_child=False)
        self._expand_reveal.set_halign(Gtk.Align.FILL)
        self._expand_reveal.set_valign(Gtk.Align.FILL)
        self._expand_holder = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self._expand_holder.add_css_class("quad-expanded")
        self._expand_card = bordered(self._expand_holder, radius=16, fill_bg=True)
        self._expand_reveal.set_child(self._expand_card)
        self.add_overlay(self._expand_reveal)
        # The overlay's holder is opaque; keep the whole overlay hidden unless a quad
        # is actually expanded, otherwise it paints over the 2x2 grid.
        self._expand_reveal.set_visible(False)

        settings.subscribe(self._on_settings_changed)

    # -- expansion ---------------------------------------------------------
    def request_expand(self, module_id: str) -> None:
        card = self.cards.get(module_id)
        if not card or card.instance is None or card.instance.expanded is None:
            return
        # A module opts into expansion by supplying a distinct expanded widget
        # (a separate instance), so no reparenting of the compact cell is needed.
        content = card.instance.expanded
        # reparent content into the expanded holder
        self._clear_expand_holder()
        self._expand_reveal.set_visible(True)
        self._expand_holder.append(self._expanded_header(card.spec.title))
        if card.instance.scroll_expanded:
            wrap = Gtk.ScrolledWindow(vexpand=True,
                                      hscrollbar_policy=Gtk.PolicyType.NEVER)
            wrap.set_child(content)
            self._expand_holder.append(wrap)
        else:
            content.set_vexpand(True)
            self._expand_holder.append(content)
        self._expanded_id = module_id
        self._expand_reveal.set_reveal_child(True)
        # bouncy scale pop (CSS @keyframes); the class was cleared on collapse so
        # re-adding it here replays the animation on every expand.
        self._expand_card.add_css_class("quad-pop")
        self.grid.add_css_class("dimmed")
        card.on_show()

    def request_collapse(self) -> None:
        self._expand_reveal.set_reveal_child(False)
        self._expand_reveal.set_visible(False)
        self._expand_card.remove_css_class("quad-pop")  # reset so the pop replays
        self._expanded_id = None
        self.grid.remove_css_class("dimmed")
        # Drop the reference to the reparented widget so the card can reuse it.
        self._clear_expand_holder()

    def _clear_expand_holder(self) -> None:
        child = self._expand_holder.get_first_child()
        while child:
            # detach any ScrolledWindow's child so it survives for the card
            if isinstance(child, Gtk.ScrolledWindow):
                inner = child.get_child()
                if inner:
                    child.set_child(None)
            self._expand_holder.remove(child)
            child = self._expand_holder.get_first_child()

    def _expanded_header(self, title: str) -> Gtk.Widget:
        header = Gtk.CenterBox()
        header.add_css_class("expanded-header")
        back = Gtk.Button(label="  Back")  # nf arrow
        back.add_css_class("quad-action")
        back.connect("clicked", lambda *_: self.request_collapse())
        header.set_start_widget(back)
        lbl = Gtk.Label(label=title)
        lbl.add_css_class("quad-title")
        header.set_center_widget(lbl)
        return header

    @property
    def is_expanded(self) -> bool:
        return self._expanded_id is not None

    # -- lifecycle ---------------------------------------------------------
    def _on_settings_changed(self) -> None:
        for card in self.cards.values():
            card.on_settings_changed()

    def on_show(self) -> None:
        for card in self.cards.values():
            card.on_show()

    def on_hide(self) -> None:
        if self.is_expanded:
            self.request_collapse()
        for card in self.cards.values():
            card.on_hide()
