"""Scrollbare Monospace-Log-Konsole + Run-Button — wiederverwendbar.

Kapselt das Muster „Button drücken → privilegierten Befehl async ausführen →
Output live mitlesen". Genutzt von der Update- und Kernel-Seite.
"""
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.gui import confirm as confirm_mod
from taninux.gui.process import ProcessRunner


class LogConsole(Gtk.Box):
    """TextView in einem ScrolledWindow, das automatisch nach unten scrollt."""

    def __init__(self) -> None:
        super().__init__(orientation=Gtk.Orientation.VERTICAL)
        self.add_css_class("card")

        self._view = Gtk.TextView(editable=False, cursor_visible=False, monospace=True)
        self._view.set_wrap_mode(Gtk.WrapMode.WORD_CHAR)
        self._view.set_left_margin(10)
        self._view.set_right_margin(10)
        self._view.set_top_margin(8)
        self._view.set_bottom_margin(8)
        self._buffer = self._view.get_buffer()

        # Höhe auf ~5 Zeilen begrenzt (vorher 220px = riesiges Feld), scrollbar.
        scroller = Gtk.ScrolledWindow(hexpand=True)
        scroller.set_min_content_height(112)
        scroller.set_max_content_height(112)
        scroller.set_propagate_natural_height(False)
        scroller.set_child(self._view)
        self.set_vexpand(False)
        self.append(scroller)

    def clear(self) -> None:
        self._buffer.set_text("")

    def append_line(self, text: str) -> None:
        end = self._buffer.get_end_iter()
        self._buffer.insert(end, text + "\n")
        # ans Ende scrollen
        mark = self._buffer.create_mark(None, self._buffer.get_end_iter(), False)
        self._view.scroll_mark_onscreen(mark)
        self._buffer.delete_mark(mark)


def collapsible_log(console: LogConsole, *, title: str = "Log") -> Gtk.Expander:
    """Konsole in einem zugeklappten, dezenten „Log"-Disclosure."""
    exp = Gtk.Expander(label=title)
    exp.set_margin_top(6)
    exp.set_child(console)
    return exp


class RunnerCard(Gtk.Box):
    """Konsole + Action-Button, verdrahtet mit einem ProcessRunner."""

    def __init__(
        self,
        button_label: str,
        argv: list[str],
        *,
        privileged: bool = True,
        confirm: tuple[str, str] | None = None,
    ) -> None:
        super().__init__(orientation=Gtk.Orientation.VERTICAL, spacing=12)
        self._argv = argv
        self._privileged = privileged
        self._confirm = confirm
        self._runner = ProcessRunner()
        self._runner.connect("line", lambda _r, line: self._console.append_line(line))
        self._runner.connect("done", self._on_done)

        self._button = Gtk.Button(label=button_label)
        self._button.add_css_class("suggested-action")
        self._button.add_css_class("pill")
        self._button.set_halign(Gtk.Align.START)
        self._button.connect("clicked", self._on_click)
        self.append(self._button)

        self._console = LogConsole()
        self.append(collapsible_log(self._console))

    def _run(self) -> None:
        self._button.set_sensitive(False)
        self._console.clear()
        self._runner.run(self._argv, privileged=self._privileged)

    def _on_click(self, _button: Gtk.Button) -> None:
        if self._confirm is not None:
            heading, body = self._confirm
            confirm_mod.confirm(self._button, heading, body, self._run)
        else:
            self._run()

    def _on_done(self, _runner: ProcessRunner, code: int) -> None:
        self._console.append_line("")
        self._console.append_line(
            f"✔ done (exit {code})" if code == 0 else f"✘ exit {code}"
        )
        self._button.set_sensitive(True)


def log_expander(console: LogConsole, *, title: str = "Log",
                 expanded: bool = False) -> Adw.PreferencesGroup:
    """Verpackt eine LogConsole in eine einklappbare „Log"-Zeile (Pfeil).

    Standardmäßig zugeklappt; aufgeklappt zeigt sie die auf ~5 Zeilen
    begrenzte, scrollbare Konsole. Rückgabe = PreferencesGroup (für page.add()).
    Ersetzt das frühere „große schwarze Log-Feld" auf jeder Seite.
    """
    group = Adw.PreferencesGroup()
    exp = collapsible_log(console, title=title)
    exp.set_expanded(expanded)
    group.add(exp)
    return group
