"""Audio — pavucontrol-artige Steuerung (Output/Input) über core.audio (pactl).

Pro Richtung: Geräte-Auswahl (= setzt Default), Lautstärke-Slider, Mute.
Alles User-Level, kein root. Live-Updates von aussen (anderes Tool ändert
Volume) holt man per Refresh — eine pactl-subscribe-Schleife wäre der nächste
Ausbau.
"""
from __future__ import annotations

import gi

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

from taninux.core import audio


def build() -> Gtk.Widget:
    page = Adw.PreferencesPage()
    if not audio.available():
        group = Adw.PreferencesGroup(title="Audio")
        group.add(Adw.ActionRow(
            title="No audio server",
            subtitle="pactl found no PulseAudio/PipeWire server.",
        ))
        page.add(group)
        return page

    page.add(_audio_group(
        "sink", "Output",
        "audio-volume-high-symbolic", "audio-volume-muted-symbolic",
    ))
    page.add(_audio_group(
        "source", "Input",
        "audio-input-microphone-symbolic", "microphone-disabled-symbolic",
    ))
    page.add(_apps_group())
    return page


def _apps_group() -> Adw.PreferencesGroup:
    """Per-App-Lautstärke der gerade abspielenden Sink-Inputs."""
    group = Adw.PreferencesGroup(title="Applications")

    refresh = Gtk.Button(icon_name="view-refresh-symbolic")
    refresh.add_css_class("flat")
    refresh.set_valign(Gtk.Align.CENTER)
    refresh.set_tooltip_text("Refresh")
    group.set_header_suffix(refresh)

    rows: list[Gtk.Widget] = []

    def populate() -> None:
        for r in rows:
            group.remove(r)
        rows.clear()

        streams = audio.playback_streams()
        if not streams:
            row = Adw.ActionRow(title="No apps are playing audio")
            group.add(row)
            rows.append(row)
            return

        for st in streams:
            rows.append(_stream_row(st))
            group.add(rows[-1])

    refresh.connect("clicked", lambda _b: populate())
    populate()
    return group


def _stream_row(st: audio.Stream) -> Adw.ActionRow:
    icon_on = "audio-volume-high-symbolic"
    icon_off = "audio-volume-muted-symbolic"
    row = Adw.ActionRow(title=st.app)

    adj = Gtk.Adjustment(value=0, lower=0, upper=150, step_increment=1,
                         page_increment=5)
    scale = Gtk.Scale(orientation=Gtk.Orientation.HORIZONTAL, adjustment=adj)
    scale.set_hexpand(True)
    scale.set_size_request(200, -1)
    scale.set_draw_value(False)
    scale.set_valign(Gtk.Align.CENTER)
    scale.add_mark(100, Gtk.PositionType.BOTTOM, None)

    mute_btn = Gtk.ToggleButton()
    mute_btn.add_css_class("flat")
    mute_btn.set_valign(Gtk.Align.CENTER)

    row.add_suffix(scale)
    row.add_suffix(mute_btn)

    # -- Initialwerte VOR connect setzen (kein Initial-Fire) -----------
    adj.set_value(min(st.volume, 150))
    mute_btn.set_active(st.muted)
    mute_btn.set_icon_name(icon_off if st.muted else icon_on)

    def on_vol(a: Gtk.Adjustment) -> None:
        audio.set_stream_volume(st.index, int(a.get_value()))

    def on_mute(b: Gtk.ToggleButton) -> None:
        m = b.get_active()
        audio.set_stream_mute(st.index, m)
        b.set_icon_name(icon_off if m else icon_on)

    adj.connect("value-changed", on_vol)
    mute_btn.connect("toggled", on_mute)
    return row


def _audio_group(kind: str, title: str, icon_on: str, icon_off: str) -> Adw.PreferencesGroup:
    devices = audio.list_devices(kind)
    group = Adw.PreferencesGroup(title=title)

    if not devices:
        group.add(Adw.ActionRow(title="No devices found"))
        return group

    names = [d.name for d in devices]
    descs = [d.description for d in devices]
    default = audio.get_default(kind)
    state = {"name": default if default in names else names[0]}

    # -- Geräte-Auswahl (= Default setzen) ----------------------------
    combo = Adw.ComboRow(title="Device")
    combo.set_model(Gtk.StringList.new(descs))
    combo.set_selected(names.index(state["name"]))

    # -- Lautstärke-Zeile: Slider + % + Mute --------------------------
    vol_row = Adw.ActionRow(title="Volume")
    adj = Gtk.Adjustment(value=0, lower=0, upper=150, step_increment=1,
                         page_increment=5)
    scale = Gtk.Scale(orientation=Gtk.Orientation.HORIZONTAL, adjustment=adj)
    scale.set_hexpand(True)
    scale.set_size_request(220, -1)
    scale.set_draw_value(False)
    scale.set_valign(Gtk.Align.CENTER)
    scale.add_mark(100, Gtk.PositionType.BOTTOM, None)  # 100%-Markierung

    pct = Gtk.Label()
    pct.add_css_class("dim-label")
    pct.add_css_class("numeric")
    pct.set_width_chars(4)

    mute_btn = Gtk.ToggleButton()
    mute_btn.add_css_class("flat")
    mute_btn.set_valign(Gtk.Align.CENTER)

    vol_row.add_suffix(scale)
    vol_row.add_suffix(pct)
    vol_row.add_suffix(mute_btn)

    guard = {"sync": False}

    def load(name: str) -> None:
        guard["sync"] = True
        v = audio.get_volume(kind, name)
        m = audio.get_muted(kind, name)
        adj.set_value(min(v, 150))
        pct.set_label(f"{v}%")
        mute_btn.set_active(m)
        mute_btn.set_icon_name(icon_off if m else icon_on)
        guard["sync"] = False

    load(state["name"])

    def on_combo(c: Adw.ComboRow, _p) -> None:
        i = c.get_selected()
        if 0 <= i < len(names):
            state["name"] = names[i]
            audio.set_default(kind, names[i])
            load(names[i])

    def on_vol(a: Gtk.Adjustment) -> None:
        if guard["sync"]:
            return
        v = int(a.get_value())
        pct.set_label(f"{v}%")
        audio.set_volume(kind, state["name"], v)

    def on_mute(b: Gtk.ToggleButton) -> None:
        if guard["sync"]:
            return
        m = b.get_active()
        audio.set_mute(kind, state["name"], m)
        b.set_icon_name(icon_off if m else icon_on)

    combo.connect("notify::selected", on_combo)
    adj.connect("value-changed", on_vol)
    mute_btn.connect("toggled", on_mute)

    group.add(combo)
    group.add(vol_row)
    return group
