"""Appearance-Seite — Farbschema + Fuji-Akzent.

Voll funktional und persistent (gui.config): Farbschema System/Light/Dark als
ComboRow, die Fuji-Palette als wählbare Swatches. Beide wirken sofort app-weit
über gui.style.
"""
from __future__ import annotations

import gi

gi.require_version("Adw", "1")
from gi.repository import Adw, Gtk, Pango  # noqa: E402

from taninux.gui import config, desktop, eww, style
from taninux.gui.pages.wallpaper import wallpaper_group

# Schema-Reihenfolge der ComboRow ↔ Persistenz-Keys
_SCHEME_LABELS = ["System", "Light", "Dark"]
_SCHEME_KEYS = ["system", "light", "dark"]


def build() -> Gtk.Widget:
    page = Adw.PreferencesPage()
    page.add(_appearance_group())
    page.add(wallpaper_group())  # sehr visuell -> direkt nach Color scheme/Accent
    page.add(_window_buttons_group())
    page.add(_theme_icons_group())
    page.add(_font_group())
    page.add(_cursor_group())
    return page


# -- GTK-Theme + Icon-Theme (nwg-look-Stil, kein root) --------------------

def _combo(title: str, items: list[str], current_value: str, apply_fn) -> Adw.ComboRow:
    """ComboRow, die bei Auswahl apply_fn(value) ruft. Initial-Set feuert nicht
    (connect erst NACH set_selected)."""
    opts = list(items)
    if current_value and current_value not in opts:
        opts.insert(0, current_value)  # Ist-Wert immer wählbar halten
    row = Adw.ComboRow(title=title)
    row.set_model(Gtk.StringList.new(opts or ["(none)"]))
    row.set_enable_search(True)
    if current_value in opts:
        row.set_selected(opts.index(current_value))

    def on_sel(r: Adw.ComboRow, _p) -> None:
        i = r.get_selected()
        if 0 <= i < len(opts):
            apply_fn(opts[i])

    row.connect("notify::selected", on_sel)
    return row


def _window_buttons_group() -> Adw.PreferencesGroup:
    """Einzelne Titlebar-Knöpfe an/aus + Platzierung (wie GNOME Tweaks)."""
    cur = desktop.window_buttons()
    group = Adw.PreferencesGroup(
        title="Window buttons",
        description="Show or hide the titlebar buttons.",
    )

    state = {"ready": False}

    min_row = Adw.SwitchRow(title="Minimize")
    max_row = Adw.SwitchRow(title="Maximize")
    close_row = Adw.SwitchRow(title="Close")
    place_row = Adw.ComboRow(title="Placement")
    place_row.set_model(Gtk.StringList.new(["Right", "Left"]))

    def apply(*_a) -> None:
        if not state["ready"]:
            return
        side = "left" if place_row.get_selected() == 1 else "right"
        desktop.set_window_buttons(
            min_row.get_active(), max_row.get_active(), close_row.get_active(), side,
        )

    # Startwerte ZUERST setzen, dann erst Signale verbinden (kein Initial-Feuern).
    min_row.set_active(cur["minimize"])
    max_row.set_active(cur["maximize"])
    close_row.set_active(cur["close"])
    place_row.set_selected(1 if cur["side"] == "left" else 0)

    min_row.connect("notify::active", apply)
    max_row.connect("notify::active", apply)
    close_row.connect("notify::active", apply)
    place_row.connect("notify::selected", apply)

    group.add(min_row)
    group.add(max_row)
    group.add(close_row)
    group.add(place_row)

    state["ready"] = True
    return group


def _theme_icons_group() -> Adw.PreferencesGroup:
    cur = desktop.current()
    group = Adw.PreferencesGroup(
        title="Theme · icons",
        description="Running apps may need a restart.",
    )
    group.add(_combo(
        "GTK theme", desktop.list_gtk_themes(), cur["gtk_theme"],
        desktop.apply_gtk_theme,
    ))
    group.add(_combo(
        "Icon theme", desktop.list_icon_themes(), cur["icon_theme"],
        desktop.apply_icon_theme,
    ))
    return group


def _font_group() -> Adw.PreferencesGroup:
    cur = desktop.current()
    group = Adw.PreferencesGroup(title="Font")
    row = Adw.ActionRow(
        title="System font",
        subtitle="Default interface font",
    )
    dialog = Gtk.FontDialog()
    fbtn = Gtk.FontDialogButton.new(dialog)
    fbtn.set_valign(Gtk.Align.CENTER)
    if cur["font"]:
        fbtn.set_font_desc(Pango.FontDescription.from_string(cur["font"]))

    def on_font(btn: Gtk.FontDialogButton, _p) -> None:
        desc = btn.get_font_desc()
        if desc is not None:
            desktop.apply_font(desc.to_string())

    fbtn.connect("notify::font-desc", on_font)
    row.add_suffix(fbtn)
    group.add(row)
    return group


def _cursor_group() -> Adw.PreferencesGroup:
    cur = desktop.current()
    group = Adw.PreferencesGroup(
        title="Cursor",
        description="Cursor theme and size.",
    )
    group.add(_combo(
        "Cursor theme", desktop.list_cursor_themes(), cur["cursor_theme"],
        desktop.apply_cursor_theme,
    ))

    try:
        size = int(cur["cursor_size"])
    except (ValueError, TypeError):
        size = 24
    adj = Gtk.Adjustment(value=size, lower=8, upper=128, step_increment=2,
                         page_increment=8)
    spin = Adw.SpinRow(title="Cursor size", adjustment=adj)
    spin.set_subtitle("Pixel")
    # connect NACH dem Setzen des Startwerts -> Initial feuert nicht
    adj.connect("value-changed", lambda a: desktop.apply_cursor_size(int(a.get_value())))
    group.add(spin)
    return group


def _appearance_group() -> Adw.PreferencesGroup:
    cfg = config.load()
    group = Adw.PreferencesGroup(
        title="Appearance",
        description="Color scheme and Fuji accent — applied instantly, saved.",
    )

    # Guard: programmatisches Setzen der Startwerte soll nicht schon speichern.
    state = {"ready": False}

    def persist() -> None:
        if state["ready"]:
            config.save(state["scheme"], state["accent"])
            style.apply(state["scheme"], state["accent"])          # diese App live
            style.write_global_theme(state["scheme"], state["accent"])  # ALLE GTK3+GTK4-Apps + gsettings
            eww.reload_if_running()                                # Bar live nachziehen

    state["scheme"] = cfg["color_scheme"]
    state["accent"] = cfg["accent"]

    # -- Color scheme --------------------------------------------------
    scheme_row = Adw.ComboRow(title="Color scheme")
    scheme_row.set_model(Gtk.StringList.new(_SCHEME_LABELS))
    cur = _SCHEME_KEYS.index(cfg["color_scheme"]) if cfg["color_scheme"] in _SCHEME_KEYS else 0
    scheme_row.set_selected(cur)

    def on_scheme(row: Adw.ComboRow, _param) -> None:
        state["scheme"] = _SCHEME_KEYS[row.get_selected()]
        persist()

    scheme_row.connect("notify::selected", on_scheme)
    group.add(scheme_row)

    # -- Fuji accent (Swatch-Reihe) -----------------------------------
    accent_row = Adw.ActionRow(
        title="Accent color",
        subtitle="Fuji palette",
    )
    swatches = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8)
    swatches.set_valign(Gtk.Align.CENTER)

    leader: Gtk.ToggleButton | None = None
    for acc in style.FUJI_ACCENTS:
        btn = Gtk.ToggleButton()
        btn.add_css_class("fuji-swatch")
        btn.add_css_class(f"swatch-{acc.key}")
        btn.set_tooltip_text(acc.label)
        btn.set_valign(Gtk.Align.CENTER)
        if leader is None:
            leader = btn
        else:
            btn.set_group(leader)
        if acc.key == cfg["accent"]:
            btn.set_active(True)

        def on_toggle(b: Gtk.ToggleButton, key: str = acc.key) -> None:
            if b.get_active():
                state["accent"] = key
                persist()

        btn.connect("toggled", on_toggle)
        swatches.append(btn)

    accent_row.add_suffix(swatches)
    group.add(accent_row)

    state["ready"] = True
    return group
