"""Bundles-Pillar — deklarative Paket-Sets installieren/checken."""
from __future__ import annotations

from dataclasses import dataclass

from textual.app import ComposeResult
from textual.binding import Binding
from textual.containers import VerticalScroll
from textual.widgets import DataTable, Static

from taninux.bundles.builtins import Bundle
from taninux.bundles.loader import all_bundles
from taninux.core import pacman as pac
from taninux.core import sysinfo
from taninux.core import theme as T
from taninux.core.confirm import ConfirmModal
from taninux.core.runner import Command, run_interactive


@dataclass
class BundleStatus:
    bundle: Bundle
    installed: list[str]
    missing: list[str]

    @property
    def state(self) -> str:
        if not self.installed:
            return "none"
        if not self.missing:
            return "satisfied"
        return "partial"


STATE_GLYPH = {
    "satisfied": ("●", T.TAKE),
    "partial":   ("◐", T.CHIKYU),
    "none":      ("○", T.BASE4),
}
STATE_LABEL = {
    "satisfied": "vollständig",
    "partial":   "teilweise",
    "none":      "fehlt",
}


def status_for(bundle: Bundle, installed: frozenset[str]) -> BundleStatus:
    inst = [p for p in bundle.packages if p in installed]
    miss = [p for p in bundle.packages if p not in installed]
    return BundleStatus(bundle=bundle, installed=inst, missing=miss)


class BundlesView(VerticalScroll):
    DEFAULT_CSS = f"""
    BundlesView {{
        background: {T.YORU};
        padding: 1 2;
        scrollbar-color: {T.BASE2} {T.YORU};
        scrollbar-background: {T.YORU};
        scrollbar-corner-color: {T.YORU};
        scrollbar-size-vertical: 1;
    }}
    BundlesView Static {{
        background: {T.YORU};
        margin-bottom: 1;
    }}
    BundlesView #b-table {{
        height: auto;
        background: {T.YORU};
        color: {T.FG_ALT};
        border: none;
        overflow-x: hidden;
    }}
    BundlesView #b-table:focus {{
        border: none;
    }}
    BundlesView DataTable > .datatable--header {{
        background: {T.YORU};
        color: {T.SHINKAI};
    }}
    BundlesView DataTable > .datatable--cursor {{
        background: {T.BASE1};
        color: {T.YUKI};
    }}
    BundlesView DataTable > .datatable--even-row {{
        background: {T.YORU};
    }}
    BundlesView DataTable > .datatable--odd-row {{
        background: {T.YORU};
    }}
    """

    BINDINGS = [
        Binding("r", "rescan", "rescan"),
        Binding("i", "install", "fehlende installieren"),
        Binding("j", "cursor_down", "down", show=False),
        Binding("k", "cursor_up", "up", show=False),
    ]

    def __init__(self, **kwargs) -> None:
        super().__init__(**kwargs)
        self.statuses: list[BundleStatus] = []

    def compose(self) -> ComposeResult:
        yield Static("", id="b-intro", markup=True)
        yield DataTable(id="b-table", cursor_type="row", zebra_stripes=False)
        yield Static("", id="b-detail", markup=True)
        yield Static("", id="b-flash", markup=True)

    def on_mount(self) -> None:
        t = self.query_one("#b-table", DataTable)
        t.add_column("", width=2, key="glyph")
        t.add_column("bundle", width=22, key="name")
        t.add_column("status", width=14, key="status")
        t.add_column("fortschritt", width=18, key="progress")
        t.add_column("beschreibung", key="desc")
        self.action_rescan()

    def action_rescan(self) -> None:
        pac.invalidate()
        installed = pac.installed_packages()
        self.statuses = [status_for(b, installed) for b in all_bundles()]
        # Sortierung: partial first (in-progress), dann none (zum vorschlagen), dann satisfied
        order = {"partial": 0, "none": 1, "satisfied": 2}
        self.statuses.sort(key=lambda s: (order[s.state], s.bundle.label))
        self._refresh_view()
        self._flash(f"{len(self.statuses)} bundles geprüft")

    def action_install(self) -> None:
        cur = self._current()
        if cur is None:
            return
        if not cur.missing:
            self._flash(f"{cur.bundle.label} bereits vollständig", color=T.TAKE)
            return
        body = (
            f"bundle [{cur.bundle.label}] vervollständigen?\n\n"
            f"fehlende pakete ({len(cur.missing)}):\n"
            f"  {', '.join(cur.missing)}"
        )
        self.app.push_screen(
            ConfirmModal(f"install {cur.bundle.id}?", body, danger=False),
            lambda ok: self._after_install(ok, cur),
        )

    def _after_install(self, ok: bool | None, cur: BundleStatus) -> None:
        if not ok:
            self._flash("abgebrochen")
            return
        helper = "paru" if sysinfo.has_binary("paru") else (
            "yay" if sysinfo.has_binary("yay") else "sudo pacman"
        )
        argv = helper.split() + ["-S", "--needed", *cur.missing]
        codes = run_interactive(
            self.app,
            [Command(
                label=f"install {len(cur.missing)} pakete für [{cur.bundle.label}]",
                argv=argv,
            )],
        )
        success = codes and codes[0] == 0
        self._flash(
            f"{cur.bundle.label} installiert" if success else "install fehlgeschlagen",
            color=T.TAKE if success else T.RED,
        )
        self.action_rescan()

    def action_cursor_down(self) -> None:
        self.query_one("#b-table", DataTable).action_cursor_down()

    def action_cursor_up(self) -> None:
        self.query_one("#b-table", DataTable).action_cursor_up()

    def _current(self) -> BundleStatus | None:
        t = self.query_one("#b-table", DataTable)
        r = t.cursor_row
        if r is None or r < 0 or r >= len(self.statuses):
            return None
        return self.statuses[r]

    def _flash(self, msg: str, color: str | None = None) -> None:
        color = color or T.BASE4
        self.query_one("#b-flash", Static).update(f"[{color}]{msg}[/]")

    def _refresh_view(self) -> None:
        total = len(self.statuses)
        sat = sum(1 for s in self.statuses if s.state == "satisfied")
        par = sum(1 for s in self.statuses if s.state == "partial")
        non = sum(1 for s in self.statuses if s.state == "none")
        intro = (
            f"[{T.UME}]◆ bundles[/]\n"
            f"[{T.BASE5}]deklarative paket-sets · "
            f"[{T.TAKE}]●[/] {sat} vollständig  "
            f"[{T.CHIKYU}]◐[/] {par} teilweise  "
            f"[{T.BASE4}]○[/] {non} fehlend[/]\n"
            f"[{T.BASE5}]`i` installiert die fehlenden pakete des ausgewählten bundles[/]"
        )
        self.query_one("#b-intro", Static).update(intro)

        t = self.query_one("#b-table", DataTable)
        t.clear()
        for s in self.statuses:
            glyph, color = STATE_GLYPH[s.state]
            label = STATE_LABEL[s.state]
            total_p = len(s.bundle.packages)
            done = len(s.installed)
            # Progress bar als unicode block-chars
            ratio = done / total_p if total_p else 0
            blocks = int(ratio * 10)
            bar = "█" * blocks + "░" * (10 - blocks)
            t.add_row(
                f"[{color}]{glyph}[/]",
                f"[{T.YUKI}]{s.bundle.label}[/]",
                f"[{color}]{label}[/]",
                f"[{color}]{bar}[/] [{T.BASE5}]{done}/{total_p}[/]",
                f"[{T.BASE5}]{s.bundle.tagline}[/]",
            )

        # Detail: zeige fehlende Pakete des aktuellen Bundles
        if self.statuses:
            self._render_detail(self.statuses[0])

    def _render_detail(self, s: BundleStatus) -> None:
        lines = [
            f"[{T.SHINKAI}]{s.bundle.label}[/]   [{T.BASE5}]{s.bundle.tagline}[/]",
        ]
        if s.bundle.tags:
            lines.append(f"[{T.BASE4}]tags:[/] {', '.join(s.bundle.tags)}")
        lines += [
            "",
            f"[{T.SHINKAI}]installiert ({len(s.installed)})[/]",
        ]
        for p in s.installed:
            lines.append(f"  [{T.TAKE}]●[/] [{T.YUKI}]{p}[/]")
        if s.missing:
            lines += [
                "",
                f"[{T.SHINKAI}]fehlt ({len(s.missing)})[/]",
            ]
            for p in s.missing:
                lines.append(f"  [{T.BASE4}]○[/] [{T.BASE5}]{p}[/]")
        self.query_one("#b-detail", Static).update("\n".join(lines))

    def on_data_table_row_highlighted(self, event: DataTable.RowHighlighted) -> None:
        if event.cursor_row is None or event.cursor_row >= len(self.statuses):
            return
        self._render_detail(self.statuses[event.cursor_row])
