from __future__ import annotations

from collections.abc import Callable, Sequence
from typing import Any

from ui.account_card import AccountCard
from ui.style import add_css_class
from ui.view_model import AccountVM, DashboardCallbacks, ProviderTabVM


def _load_gtk_modules() -> tuple[Any, bool]:
    try:
        import gi

        gi.require_version("Gtk", "3.0")
        from gi.repository import Gtk

        return Gtk, True
    except Exception:
        return _FallbackGtk, False


class _FallbackWidget:
    def __init__(self, text: str = "") -> None:
        self.text = text
        self.label = text
        self.visible = True
        self.sensitive = True
        self.fraction = 0.0
        self.children: list[object] = []

    def set_text(self, text: str) -> None:
        self.text = text
        self.label = text

    def set_label(self, label: str) -> None:
        self.text = label
        self.label = label

    def set_visible(self, visible: bool) -> None:
        self.visible = visible

    def get_visible(self) -> bool:
        return self.visible

    def set_sensitive(self, sensitive: bool) -> None:
        self.sensitive = sensitive

    def set_fraction(self, fraction: float) -> None:
        self.fraction = max(0.0, min(1.0, float(fraction)))

    def add(self, child: object) -> None:
        self.children.append(child)

    def pack_start(self, child: object, *_args: object) -> None:
        self.children.append(child)

    def remove(self, child: object) -> None:
        if child in self.children:
            self.children.remove(child)


class _FallbackLabel(_FallbackWidget):
    pass


class _FallbackButton(_FallbackWidget):
    def __init__(self, label: str = "") -> None:
        super().__init__(label)
        self._callback: Callable[..., Any] | None = None

    def connect(self, signal: str, callback: Callable[..., Any]) -> None:
        if signal == "clicked":
            self._callback = callback

    def emit(self, signal: str) -> None:
        if signal == "clicked" and self.sensitive and self._callback is not None:
            self._callback(self)


class _FallbackProgressBar(_FallbackWidget):
    def __init__(self) -> None:
        super().__init__(text="")
        self.show_text = False

    def set_show_text(self, show_text: bool) -> None:
        self.show_text = bool(show_text)


class _FallbackBox(_FallbackWidget):
    def __init__(self, orientation: int | None = None, spacing: int = 0) -> None:
        super().__init__(text="")
        self.orientation = orientation
        self.spacing = spacing


class _FallbackGtk:
    class Orientation:
        VERTICAL = 1
        HORIZONTAL = 2

    Label = _FallbackLabel
    Box = _FallbackBox
    Button = _FallbackButton
    ProgressBar = _FallbackProgressBar


def _new_box(gtk: Any, orientation: int, spacing: int) -> Any:
    try:
        return gtk.Box(orientation=orientation, spacing=spacing)
    except TypeError:
        return gtk.Box(orientation, spacing)


def _pack(parent: Any, child: Any) -> None:
    if hasattr(parent, "pack_start"):
        parent.pack_start(child, False, False, 0)
    else:
        parent.add(child)


def _remove_child(parent: Any, child: Any) -> None:
    try:
        parent.remove(child)
        return
    except Exception:
        pass
    children = getattr(parent, "children", None)
    if isinstance(children, list) and child in children:
        children.remove(child)


class ProviderTab:
    def __init__(
        self,
        vm: ProviderTabVM,
        callbacks: DashboardCallbacks,
        gtk_module: Any | None = None,
    ) -> None:
        self._callbacks = callbacks
        loaded, _ = _load_gtk_modules()
        self.gtk = gtk_module if gtk_module is not None else loaded
        vertical = getattr(getattr(self.gtk, "Orientation", None), "VERTICAL", 0)
        self.widget = _new_box(self.gtk, vertical, 8)
        add_css_class(self.widget, "dashboard-content")
        add_css_class(self.widget, "provider-tab")
        self.cards: dict[str, AccountCard] = {}
        self._slugs: tuple[str, ...] = ()
        self.update(vm)

    def update(self, vm: ProviderTabVM) -> None:
        slugs = tuple(account.slug for account in vm.accounts)
        if slugs != self._slugs:
            self._rebuild(vm.accounts, slugs)
            return
        for account in vm.accounts:
            self.cards[account.slug].update(account)

    def _rebuild(self, accounts: Sequence[AccountVM], slugs: tuple[str, ...]) -> None:
        for card in self.cards.values():
            _remove_child(self.widget, card.widget)
        self.cards = {}
        for account in accounts:
            card = AccountCard(account, self._callbacks, gtk_module=self.gtk)
            self.cards[account.slug] = card
            _pack(self.widget, card.widget)
        self._slugs = slugs
        if hasattr(self.widget, "show_all"):
            self.widget.show_all()
