"""VPN — NetworkManager-VPNs verbinden/importieren + DNS für die aktive
Verbindung. Alles über nmcli (user-level, polkit-backed)."""
from __future__ import annotations

import gi

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

from taninux.core import network
from taninux.gui.process import ProcessRunner


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

    if not network.available():
        g = Adw.PreferencesGroup(title="VPN")
        g.add(Adw.ActionRow(title="NetworkManager is not running"))
        page.add(g)
        return page

    runner = ProcessRunner()
    dns_runner = ProcessRunner()
    dns_queue: list = []
    groups: list[Adw.PreferencesGroup] = []

    # ---- VPN ----
    def _vpn_group() -> Adw.PreferencesGroup:
        g = Adw.PreferencesGroup(title="VPN")
        add_btn = Gtk.Button(icon_name="list-add-symbolic")
        add_btn.add_css_class("flat")
        add_btn.set_valign(Gtk.Align.CENTER)
        add_btn.set_tooltip_text("Import .ovpn or WireGuard .conf")
        add_btn.connect("clicked", lambda _b: _import())
        g.set_header_suffix(add_btn)

        vpns = network.vpn_connections()
        if not vpns:
            g.add(Adw.ActionRow(title="No VPN configured",
                                subtitle="Import a .ovpn or WireGuard .conf file"))
            return g
        for v in vpns:
            row = Adw.ActionRow(title=v.name, subtitle=v.vtype)
            sw = Gtk.Switch()
            sw.set_active(v.active)
            sw.set_valign(Gtk.Align.CENTER)
            sw.connect("state-set", lambda _s, on, n=v.name: (
                runner.run(network.vpn_up_argv(n) if on else network.vpn_down_argv(n)),
                False)[1])
            row.add_suffix(sw)
            g.add(row)
        return g

    def _import() -> None:
        dialog = Gtk.FileDialog(title="Import VPN config")
        filt = Gtk.FileFilter()
        filt.set_name("VPN configs")
        filt.add_pattern("*.ovpn")
        filt.add_pattern("*.conf")
        store = Gio.ListStore.new(Gtk.FileFilter)
        store.append(filt)
        dialog.set_filters(store)

        def done(d, res) -> None:
            try:
                f = d.open_finish(res)
            except GLib.Error:
                return
            argv = network.vpn_import_argv(f.get_path())
            if argv:
                runner.run(argv)

        dialog.open(page.get_root(), None, done)

    # ---- DNS ----
    def _dns_group() -> Adw.PreferencesGroup:
        g = Adw.PreferencesGroup(title="DNS")
        conn = network.primary_connection()
        if not conn:
            g.add(Adw.ActionRow(title="No active connection"))
            return g
        names = list(network.DNS_PRESETS.keys()) + ["Custom"]
        row = Adw.ComboRow(title="DNS servers", subtitle=conn)
        row.set_model(Gtk.StringList.new(names))
        cur = network.get_dns(conn)
        sel = next((i for i, (_, v) in enumerate(network.DNS_PRESETS.items()) if v == cur), None)
        if cur and sel is None:
            sel = len(names) - 1  # Custom
        row.set_selected(sel if sel is not None else 0)
        g.add(row)

        custom = Adw.EntryRow(title="Custom servers")
        custom.set_text(cur)
        custom.set_visible(names[row.get_selected()] == "Custom")
        g.add(custom)

        def apply(servers: str) -> None:
            dns_queue.clear()
            dns_queue.extend(network.set_dns_cmds(conn, servers))
            _dns_next()

        def on_sel(r, _p) -> None:
            choice = names[r.get_selected()]
            custom.set_visible(choice == "Custom")
            if choice != "Custom":
                apply(network.DNS_PRESETS[choice])

        row.connect("notify::selected", on_sel)
        custom.connect("apply", lambda e: apply(e.get_text().strip()))
        return g

    def _dns_next() -> None:
        if dns_queue:
            dns_runner.run(dns_queue.pop(0))

    dns_runner.connect("done", lambda _r, _c: _dns_next())

    def _render() -> None:
        for g in groups:
            page.remove(g)
        groups.clear()
        for g in (_vpn_group(), _dns_group()):
            page.add(g)
            groups.append(g)

    runner.connect("done", lambda _r, _c: _render())
    GLib.idle_add(lambda: (_render(), False)[1])
    return page
