from dataclasses import dataclass
from pathlib import Path
from typing import Any

from textual import events
from textual.app import ComposeResult
from textual.css.query import NoMatches
from textual.message import Message
from textual.reactive import reactive
from textual.widget import Widget
from textual.widgets import DataTable, Label


@dataclass
class Column:
    label: str
    key: str
    width: int | None = None


class ResourceTable(Widget):
    """Generic reusable DataTable for displaying Juju resources."""

    collapsed: reactive[bool] = reactive(False, init=False)

    class TableFocused(Message):
        """Posted (bubbling) when the internal DataTable gains focus."""

        def __init__(self, resource_table: "ResourceTable") -> None:
            super().__init__()
            self.resource_table = resource_table

    DEFAULT_CSS = (Path(__file__).parent / "resource_table.tcss").read_text()

    def __init__(
        self, columns: list[Column], *args: Any, cursor: bool = True, **kwargs: Any
    ) -> None:
        super().__init__(*args, **kwargs)
        self._columns = columns
        self._cursor = cursor
        self._is_loading = False

    def compose(self) -> ComposeResult:
        cursor_type = "row" if self._cursor else "none"
        resource_table = self

        class _FocusableTable(DataTable):
            def _on_focus(self, event: events.Focus) -> None:
                super()._on_focus(event)
                resource_table.post_message(ResourceTable.TableFocused(resource_table))

        table = _FocusableTable(
            cursor_type=cursor_type,
            zebra_stripes=False,
            cursor_background_priority="css",
            cursor_foreground_priority="renderable",
        )
        for col in self._columns:
            table.add_column(col.label, key=col.key, width=col.width)
        yield table
        yield Label("No data available.", id="empty-label")

    def on_mount(self) -> None:
        self.query_one("#empty-label").display = False

    def reset_columns(self, columns: list[Column]) -> None:
        """Replace all columns (and clear rows) with a new column set."""
        self._columns = columns
        table = self.query_one(DataTable)
        table.clear(columns=True)
        for col in self._columns:
            table.add_column(col.label, key=col.key, width=col.width)

    def update_rows(
        self,
        rows: list[tuple],
        keys: list[str] | None = None,
        heights: list[int] | None = None,
    ) -> None:
        """Replace all table rows. Each tuple must match the column order."""
        table = self.query_one(DataTable)
        table.clear()
        if rows:
            for i, row in enumerate(rows):
                key = keys[i] if keys else None
                height = heights[i] if heights else 1
                table.add_row(*row, key=key, height=height)
            self.query_one("#empty-label").display = False
        else:
            self.query_one("#empty-label").display = True

    def set_loading(self, loading: bool) -> None:
        """Show or hide a loading indicator."""
        self._is_loading = loading
        self.loading = loading

    def _watch_collapsed(self, value: bool) -> None:
        try:
            dt = self.query_one(DataTable)
            label = self.query_one("#empty-label")
        except NoMatches:
            return
        if value:
            dt.display = False
            label.display = False
            self.can_focus = True
            self.add_class("collapsed")
            self.focus()
        else:
            self.remove_class("collapsed")
            self.can_focus = False
            dt.display = True
            self.query_one(DataTable).focus()

    def _on_focus(self, event: events.Focus) -> None:
        if self.collapsed:
            self.post_message(ResourceTable.TableFocused(self))
