"""Network Shares — Remote-Verbindungen für Nautilus (SMB/SFTP/FTP/WebDAV/NFS).

Schreibt Remote-Bookmarks über core.shares (GTK-Bookmarks, die Nautilus liest)
und verbindet via `gio mount` (unprivilegiert; GVfs fragt das Passwort selbst
via Keyring/Agent). Nextcloud ist hier als WebDAV-Preset integriert — kein
Sync-Client nötig (leichtgewichtig, Dateien on-demand).

Felder passen sich pro Protokoll an, eine Live-Vorschau zeigt die Ziel-URI.
SMB/NFS brauchen gvfs-smb/gvfs-nfs; fehlen sie, gibt's einen Install-Block.
"""
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 nextcloud as nc
from taninux.core import shares
from taninux.gui import confirm as confirm_mod
from taninux.gui.console import LogConsole, RunnerCard, log_expander
from taninux.gui.process import ProcessRunner

# (Label, scheme). "nextcloud" ist ein Spezial-Scheme (baut den WebDAV-Pfad).
_PROTOCOLS: list[tuple[str, str]] = [
    ("SFTP", "sftp"),
    ("SMB · Windows share", "smb"),
    ("WebDAV", "davs"),
    ("Nextcloud", "nextcloud"),
    ("FTP", "ftp"),
    ("NFS", "nfs"),
]

# Pro Scheme: welche Felder sichtbar sind + ihr Titel. (title, visible)
_FIELDS: dict[str, dict[str, tuple[str, bool]]] = {
    "sftp": {"host": ("Host", True), "path": ("Path (optional)", True),
             "user": ("User (optional)", True), "port": ("Port (optional)", True)},
    "ftp":  {"host": ("Host", True), "path": ("Path (optional)", True),
             "user": ("User (optional)", True), "port": ("Port (optional)", True)},
    "smb":  {"host": ("Host", True), "path": ("Share", True),
             "user": ("User (optional)", True), "port": ("", False)},
    "davs": {"host": ("Host", True), "path": ("Path (optional)", True),
             "user": ("User (optional)", True), "port": ("", False)},
    "nextcloud": {"host": ("Server · e.g. cloud.example.com", True),
                  "path": ("", False), "user": ("Username", True),
                  "port": ("", False)},
    "nfs":  {"host": ("Host", True), "path": ("Export path", True),
             "user": ("", False), "port": ("", False)},
}

_SCHEME_ICON = {
    "smb": "folder-remote-symbolic", "sftp": "folder-remote-symbolic",
    "ftp": "folder-remote-symbolic", "nfs": "folder-remote-symbolic",
    "dav": "folder-cloud-symbolic", "davs": "folder-cloud-symbolic",
}


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

    console = LogConsole()
    runner = ProcessRunner()
    runner.connect("line", lambda _r, line: console.append_line(line))
    runner.connect("done", lambda _r, code: console.append_line(
        "✔ done" if code == 0 else f"✘ exit {code}"))

    # ============================================================ Your shares
    list_group = Adw.PreferencesGroup(
        title="Your shares",
        description="Saved remote locations — they appear in the file manager.",
    )
    refresh_btn = Gtk.Button(icon_name="view-refresh-symbolic")
    refresh_btn.add_css_class("flat")
    refresh_btn.set_valign(Gtk.Align.CENTER)
    refresh_btn.set_tooltip_text("Reload")
    list_group.set_header_suffix(refresh_btn)
    page.add(list_group)

    def _mount(uri: str) -> None:
        console.clear()
        runner.run(shares.mount_argv(uri))

    def _remove(uri: str, label: str) -> None:
        confirm_mod.confirm(
            page, "Remove share?",
            f"Remove “{label}”. The remote data is not deleted.",
            lambda: (shares.remove_bookmark(uri), _fill_list()),
        )

    def _fill_list() -> None:
        _clear_group(list_group)
        bms = shares.list_remote()
        if not bms:
            _track(list_group, Adw.ActionRow(
                title="No shares yet",
                subtitle="Add one below — SFTP, SMB, WebDAV, Nextcloud, FTP or NFS.",
            ))
            return
        for bm in bms:
            row = Adw.ActionRow(title=bm.label or bm.uri, subtitle=bm.uri)
            icon = _SCHEME_ICON.get(bm.scheme, "folder-remote-symbolic")
            row.add_prefix(Gtk.Image.new_from_icon_name(icon))
            connect = _btn("Connect", suggested=True)
            connect.connect("clicked", lambda _b, u=bm.uri: _mount(u))
            row.add_suffix(connect)
            remove = _btn("Remove", destructive=True)
            remove.connect("clicked", lambda _b, u=bm.uri, lb=bm.label: _remove(u, lb))
            row.add_suffix(remove)
            _track(list_group, row)

    refresh_btn.connect("clicked", lambda _b: _fill_list())

    # ============================================================ Add
    add_group = Adw.PreferencesGroup(
        title="Add a connection",
        description="You'll be asked for the password on first connect.",
    )

    proto_row = Adw.ComboRow(title="Type")
    proto_model = Gtk.StringList()
    for label, _scheme in _PROTOCOLS:
        proto_model.append(label)
    proto_row.set_model(proto_model)
    add_group.add(proto_row)

    host_row = Adw.EntryRow(title="Host")
    path_row = Adw.EntryRow(title="Path")
    user_row = Adw.EntryRow(title="User")
    port_row = Adw.EntryRow(title="Port")
    field_rows = {"host": host_row, "path": path_row,
                  "user": user_row, "port": port_row}
    for r in field_rows.values():
        add_group.add(r)

    # Live-Vorschau der Ziel-URI.
    preview = Adw.ActionRow(title="Will create")
    preview_lbl = Gtk.Label(label="—")
    preview_lbl.add_css_class("dim-label")
    preview_lbl.set_selectable(True)
    preview_lbl.set_ellipsize(3)  # END
    preview.add_suffix(preview_lbl)
    add_group.add(preview)

    add_btn = Gtk.Button(label="Add")
    add_btn.add_css_class("suggested-action")
    add_btn.set_valign(Gtk.Align.CENTER)
    add_row = Adw.ActionRow(title="Add to file manager")
    add_row.add_suffix(add_btn)
    add_group.add(add_row)
    page.add(add_group)

    def _scheme() -> str:
        return _PROTOCOLS[proto_row.get_selected()][1]

    def _current_uri() -> str:
        host = host_row.get_text().strip()
        if not host:
            return ""
        scheme = _scheme()
        if scheme == "nextcloud":
            u = user_row.get_text().strip()
            return nc.webdav_uri_from(host, u) if u else ""
        return shares.build_uri(
            scheme, host, path=path_row.get_text(),
            user=user_row.get_text(), port=port_row.get_text(),
        )

    def _update_preview(*_a) -> None:
        preview_lbl.set_label(_current_uri() or "—")

    def _sync_fields(*_a) -> None:
        conf = _FIELDS[_scheme()]
        for key, row in field_rows.items():
            title, visible = conf[key]
            row.set_visible(visible)
            if title:
                row.set_title(title)
        _update_preview()

    proto_row.connect("notify::selected", _sync_fields)
    for r in field_rows.values():
        r.connect("changed", _update_preview)

    def _on_add(_b: Gtk.Button) -> None:
        uri = _current_uri()
        console.clear()
        if not uri:
            console.append_line("✘ fill in the required fields first")
            return
        label = host_row.get_text().strip()
        shares.add_bookmark(uri, label=label)
        for r in field_rows.values():
            r.set_text("")
        console.append_line(f"✔ added {uri}")
        _update_preview()
        _fill_list()

    add_btn.connect("clicked", _on_add)

    # ============================================================ Nextcloud accounts
    nc_accounts = nc.accounts()
    if nc_accounts:
        nc_group = Adw.PreferencesGroup(
            title="Detected Nextcloud accounts",
            description="From the Nextcloud client — add as WebDAV in one click.",
        )
        for acc in nc_accounts:
            row = Adw.ActionRow(title=acc.name, subtitle=acc.url)
            row.add_prefix(Gtk.Image.new_from_icon_name("folder-cloud-symbolic"))
            add = _btn("Add to Files", suggested=True)

            def _add_acc(_b, a=acc) -> None:
                uri = nc.webdav_uri(a)
                shares.add_bookmark(uri, label=a.name)
                console.clear()
                console.append_line(f"✔ added {uri}")
                _fill_list()

            add.connect("clicked", _add_acc)
            row.add_suffix(add)
            nc_group.add(row)
        page.add(nc_group)

    # ============================================================ Backends
    missing: list[tuple[str, str, list[str]]] = []
    if not shares.smb_available():
        missing.append(("SMB · Windows shares", "gvfs-smb", shares.install_smb_argv()))
    if not shares.nfs_available():
        missing.append(("NFS shares", "gvfs-nfs", shares.install_nfs_argv()))
    if missing:
        be_group = Adw.PreferencesGroup(
            title="More connection types",
            description="SMB and NFS need an extra component.",
        )
        for label, pkg, argv in missing:
            be_group.add(Adw.ActionRow(
                title=label, subtitle="Install to enable this connection type."))
            box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12)
            box.set_margin_top(6)
            box.append(RunnerCard("Install", argv, privileged=True))
            be_group.add(box)
        page.add(be_group)

    # ============================================================ Log
    page.add(log_expander(console))

    _sync_fields()
    _fill_list()
    return page


# ----------------------------------------------------------- Helpers

def _clear_group(group: Adw.PreferencesGroup) -> None:
    rows = getattr(group, "_tani_rows", None)
    if not rows:
        return
    for row in list(rows):
        group.remove(row)
    rows.clear()


def _track(group: Adw.PreferencesGroup, row: Gtk.Widget) -> None:
    rows = getattr(group, "_tani_rows", None)
    if rows is None:
        rows = []
        group._tani_rows = rows
    group.add(row)
    rows.append(row)


def _btn(label: str, *, suggested: bool = False,
         destructive: bool = False) -> Gtk.Button:
    btn = Gtk.Button(label=label)
    btn.add_css_class("flat")
    btn.set_valign(Gtk.Align.CENTER)
    if suggested:
        btn.add_css_class("suggested-action")
    if destructive:
        btn.add_css_class("destructive-action")
    return btn
