"""Network — Wi-Fi über core.network (nmcli).

Radio an/aus, Status, verfügbare Netze (async geladen) mit Verbinden inkl.
Passwort-Dialog für gesicherte Netze. Alles User-Level (NetworkManager-polkit),
kein root. Live-Output im Connection-Log.
"""
from __future__ import annotations

import threading

import gi

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

from taninux.core import network
from taninux.gui import confirm
from taninux.gui.console import LogConsole, log_expander
from taninux.gui.process import ProcessRunner


def _signal_icon(sig: int) -> str:
    if sig >= 75:
        return "network-wireless-signal-excellent-symbolic"
    if sig >= 50:
        return "network-wireless-signal-good-symbolic"
    if sig >= 25:
        return "network-wireless-signal-ok-symbolic"
    return "network-wireless-signal-weak-symbolic"


def build() -> Gtk.Widget:
    page = Adw.PreferencesPage()

    if not network.available():
        g = Adw.PreferencesGroup(title="Network")
        g.add(Adw.ActionRow(
            title="NetworkManager not available",
            subtitle="nmcli found no running NetworkManager.",
        ))
        page.add(g)
        return page

    console = LogConsole()
    runner = ProcessRunner()
    runner.connect("line", lambda _r, ln: console.append_line(ln))

    # -- Wi-Fi radio + Status -----------------------------------------
    g_wifi = Adw.PreferencesGroup(title="Wi-Fi")
    wifi_sw = Adw.SwitchRow(title="Wi-Fi", subtitle="Enable the wireless radio")
    wifi_sw.set_active(network.wifi_enabled())
    status_row = Adw.ActionRow(title="Status")
    status_lbl = Gtk.Label(label=network.active_summary())
    status_lbl.add_css_class("dim-label")
    status_lbl.set_selectable(True)
    status_row.add_suffix(status_lbl)

    def on_wifi(sw: Adw.SwitchRow, _p) -> None:
        runner.run(network.set_wifi_argv(sw.get_active()))

    wifi_sw.connect("notify::active", on_wifi)
    g_wifi.add(wifi_sw)
    g_wifi.add(status_row)
    page.add(g_wifi)

    # -- Verfügbare Netze (async) -------------------------------------
    g_nets = Adw.PreferencesGroup(title="Available networks")
    refresh = Gtk.Button(icon_name="view-refresh-symbolic")
    refresh.add_css_class("flat")
    refresh.set_valign(Gtk.Align.CENTER)
    refresh.set_tooltip_text("Rescan")
    g_nets.set_header_suffix(refresh)
    page.add(g_nets)

    rows: list[Gtk.Widget] = []

    def _clear() -> None:
        for r in rows:
            g_nets.remove(r)
        rows.clear()

    def _connect(net: network.WifiNet, saved: bool) -> None:
        def do(pw: str) -> None:
            console.clear()
            runner.run(network.connect_argv(net.ssid, pw))
        # Gesichert, aber schon gespeichert -> ohne Passwort-Dialog verbinden.
        if net.security and not saved:
            _ask_password(refresh, net.ssid, do)
        else:
            do("")

    def _disconnect() -> None:
        argv = network.disconnect_argv()
        if not argv:
            return
        console.clear()
        runner.run(argv)

    def _forget(net: network.WifiNet) -> None:
        def do() -> None:
            console.clear()
            runner.run(network.forget_argv(net.ssid))
        confirm.confirm(
            refresh,
            f"Forget {net.ssid}",
            "Delete this saved Wi-Fi connection.",
            do,
        )

    def _btn(label: str, cb) -> Gtk.Button:
        btn = Gtk.Button(label=label)
        btn.add_css_class("flat")
        btn.set_valign(Gtk.Align.CENTER)
        btn.connect("clicked", lambda _b: cb())
        return btn

    def _render(nets: list[network.WifiNet], saved: set[str]) -> None:
        _clear()
        for net in nets:
            row = Adw.ActionRow(title=net.ssid,
                                subtitle=net.security or "open")
            row.add_prefix(Gtk.Image.new_from_icon_name(_signal_icon(net.signal)))
            if net.security:
                lock = Gtk.Image.new_from_icon_name("network-wireless-encrypted-symbolic")
                lock.add_css_class("dim-label")
                row.add_suffix(lock)
            is_saved = net.ssid in saved
            if net.in_use:
                tag = Gtk.Label(label="connected")
                tag.add_css_class("success")
                tag.add_css_class("caption-heading")
                row.add_suffix(tag)
                row.add_suffix(_btn("Disconnect", _disconnect))
            else:
                row.add_suffix(_btn("Connect",
                                    lambda n=net, s=is_saved: _connect(n, s)))
                if is_saved:
                    row.add_suffix(_btn("Forget", lambda n=net: _forget(n)))
            g_nets.add(row)
            rows.append(row)

    def _load(rescan: bool = False) -> None:
        _clear()
        spin_row = Adw.ActionRow(title="Scanning …" if rescan else "Loading …")
        sp = Adw.Spinner()
        sp.set_valign(Gtk.Align.CENTER)
        spin_row.add_prefix(sp)
        g_nets.add(spin_row)
        rows.append(spin_row)
        status_lbl.set_label(network.active_summary())

        def worker() -> None:
            nets = network.wifi_networks(rescan=rescan)
            saved = network.saved_connections()
            GLib.idle_add(lambda: (_render(nets, saved), False)[1])

        threading.Thread(target=worker, daemon=True).start()

    refresh.connect("clicked", lambda _b: _load(rescan=True))
    GLib.idle_add(lambda: (_load(rescan=False), False)[1])

    # -- Connection log -----------------------------------------------
    def on_done(_r: ProcessRunner, code: int) -> None:
        status_lbl.set_label(network.active_summary())
        console.append_line("✔ done" if code == 0 else f"✘ exit {code}")
        # Liste + Status nach jeder Aktion neu laden.
        _load(rescan=False)

    runner.connect("done", on_done)
    page.add(log_expander(console, title="Connection log"))
    return page


def _ask_password(parent: Gtk.Widget, ssid: str, on_ok) -> None:
    dialog = Adw.AlertDialog(
        heading=f"Connect to {ssid}",
        body="Enter the Wi-Fi password.",
    )
    entry = Gtk.PasswordEntry(show_peek_icon=True)
    entry.set_margin_top(8)
    dialog.set_extra_child(entry)
    dialog.add_response("cancel", "Cancel")
    dialog.add_response("connect", "Connect")
    dialog.set_response_appearance("connect", Adw.ResponseAppearance.SUGGESTED)
    dialog.set_default_response("connect")
    dialog.set_close_response("cancel")

    def on_resp(_d: Adw.AlertDialog, resp: str) -> None:
        if resp == "connect":
            on_ok(entry.get_text())

    dialog.connect("response", on_resp)
    dialog.present(parent)
