"""Users — eigenes Konto (Info + Passwort) und weitere Benutzer verwalten."""
from __future__ import annotations

import re
import threading
from pathlib import Path

import gi

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

from taninux.core import account, avatar
from taninux.gui import confirm
from taninux.gui.process import ProcessRunner

_NAME_RE = re.compile(r"^[a-z_][a-z0-9_-]*$")


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

    runner = ProcessRunner()
    queue: list = []
    others = Adw.PreferencesGroup(title="Other users")
    rows: list[Gtk.Widget] = []

    # -- eigenes Konto --------------------------------------------------
    g_info = Adw.PreferencesGroup(title="Account")

    av = Adw.Avatar(size=64, text=me.fullname, show_initials=True)
    _load_avatar(av)
    pic_row = Adw.ActionRow(title="Profile picture", subtitle="Click to change")
    pic_row.add_prefix(av)
    pic_row.set_activatable(True)
    pic_row.connect("activated", lambda _r: _pick_avatar(page, av))
    g_info.add(pic_row)

    g_info.add(_row("Name", me.fullname))
    g_info.add(_row("Username", me.username))
    g_info.add(_row("Role", "Administrator" if me.admin else "Standard"))
    page.add(g_info)

    page.add(_password_group())

    # -- weitere Benutzer ----------------------------------------------
    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("Add user")
    others.set_header_suffix(add_btn)
    page.add(others)

    def _run_queue() -> None:
        if queue:
            runner.run(queue.pop(0), privileged=True)

    def _start(cmds: list) -> None:
        queue.clear()
        queue.extend(cmds)
        _run_queue()

    def _render() -> None:
        for r in rows:
            others.remove(r)
        rows.clear()
        rest = [u for u in account.list_users() if u.username != me.username]
        if not rest:
            row = Adw.ActionRow(title="No other users")
            others.add(row)
            rows.append(row)
            return
        for u in rest:
            row = Adw.ActionRow(
                title=u.fullname,
                subtitle=f"{u.username} · {'Administrator' if u.admin else 'Standard'}",
            )
            rm = Gtk.Button(icon_name="user-trash-symbolic")
            rm.add_css_class("flat")
            rm.set_valign(Gtk.Align.CENTER)
            rm.connect("clicked", lambda _b, name=u.username: confirm.confirm(
                page, f"Remove {name}?",
                "Their account and home folder are deleted. This cannot be undone.",
                lambda n=name: _start([account.delete_user_argv(n)])))
            row.add_suffix(rm)
            others.add(row)
            rows.append(row)

    add_btn.connect("clicked", lambda _b: _add_dialog(page, _start))

    def on_done(_r: ProcessRunner, code: int) -> None:
        if queue:
            _run_queue()
        else:
            _render()

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


def _add_dialog(parent: Gtk.Widget, start) -> None:
    dialog = Adw.Dialog()
    dialog.set_title("Add user")
    dialog.set_content_width(420)

    page = Adw.PreferencesPage()
    tv = Adw.ToolbarView()
    tv.add_top_bar(Adw.HeaderBar())
    tv.set_content(page)
    dialog.set_child(tv)

    group = Adw.PreferencesGroup()
    full = Adw.EntryRow(title="Full name")
    user = Adw.EntryRow(title="Username")
    admin = Adw.SwitchRow(title="Administrator",
                          subtitle="Can install software and change system settings")
    group.add(full)
    group.add(user)
    group.add(admin)
    page.add(group)

    status = Gtk.Label(xalign=0.0, wrap=True)
    status.add_css_class("dim-label")
    create = Gtk.Button(label="Create user")
    create.add_css_class("suggested-action")
    create.add_css_class("pill")
    create.set_halign(Gtk.Align.START)
    create.set_margin_top(6)

    # Username aus Vollname vorschlagen
    def _suggest(_e) -> None:
        if not user.get_text():
            user.set_text(re.sub(r"[^a-z0-9_-]", "", full.get_text().lower().split(" ")[0]))
    full.connect("changed", _suggest)

    def on_create(_b) -> None:
        name = user.get_text().strip()
        if not _NAME_RE.match(name):
            status.set_label("Username must be lowercase letters, digits, - or _.")
            return
        start(account.add_user_cmds(name, full.get_text().strip(), admin.get_active()))
        dialog.close()

    create.connect("clicked", on_create)
    box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8)
    box.set_margin_top(6)
    box.append(create)
    box.append(status)
    group.add(box)

    dialog.present(parent)


def _password_group() -> Adw.PreferencesGroup:
    g_pw = Adw.PreferencesGroup(title="Password")
    cur = Adw.PasswordEntryRow(title="Current password")
    new = Adw.PasswordEntryRow(title="New password")
    confirm_row = Adw.PasswordEntryRow(title="Confirm new password")
    g_pw.add(cur)
    g_pw.add(new)
    g_pw.add(confirm_row)

    btn = Gtk.Button(label="Change password")
    btn.add_css_class("suggested-action")
    btn.add_css_class("pill")
    btn.set_halign(Gtk.Align.START)
    status = Gtk.Label(xalign=0.0, wrap=True)
    status.add_css_class("dim-label")

    def on_click(_b) -> None:
        c, n, cf = cur.get_text(), new.get_text(), confirm_row.get_text()
        if not c or not n:
            status.set_label("Enter your current and a new password.")
            return
        if n != cf:
            status.set_label("New passwords don't match.")
            return
        btn.set_sensitive(False)
        status.set_label("Changing…")

        def worker() -> None:
            ok, msg = account.change_password(c, n)

            def done() -> bool:
                status.set_label(("✔ " if ok else "✘ ") + msg)
                btn.set_sensitive(True)
                if ok:
                    for e in (cur, new, confirm_row):
                        e.set_text("")
                return False

            GLib.idle_add(done)

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

    btn.connect("clicked", on_click)
    box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8)
    box.set_margin_top(6)
    box.append(btn)
    box.append(status)
    g_pw.add(box)
    return g_pw


def _load_avatar(av: Adw.Avatar) -> None:
    path = avatar.current_path()
    if path:
        try:
            av.set_custom_image(Gdk.Texture.new_from_filename(path))
        except GLib.Error:
            pass


def _pick_avatar(parent: Gtk.Widget, av: Adw.Avatar) -> None:
    dialog = Gtk.FileDialog(title="Choose a picture")
    filt = Gtk.FileFilter()
    filt.set_name("Images")
    for p in ("*.png", "*.jpg", "*.jpeg", "*.webp", "*.gif"):
        filt.add_pattern(p)
    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
        try:
            pb = GdkPixbuf.Pixbuf.new_from_file_at_scale(f.get_path(), 256, 256, True)
            out = Path.home() / ".cache" / "taninux"
            out.mkdir(parents=True, exist_ok=True)
            dst = out / "avatar.png"
            pb.savev(str(dst), "png", [], [])
        except (GLib.Error, OSError):
            return
        avatar.set_own(str(dst))
        _load_avatar(av)

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


def _row(title: str, value: str) -> Adw.ActionRow:
    row = Adw.ActionRow(title=title)
    lbl = Gtk.Label(label=value, xalign=1.0)
    lbl.add_css_class("dim-label")
    lbl.set_selectable(True)
    row.add_suffix(lbl)
    return row
