"""Panel, Table, and Status handle implementations."""

from __future__ import annotations

from typing import TYPE_CHECKING, Any

from cleveragents.cli.output.handles._base import ElementHandle
from cleveragents.cli.output.handles._models import (
    MAX_TABLE_ROWS,
    Panel,
    PanelEntry,
    StatusMessage,
    Table,
)

if TYPE_CHECKING:
    from cleveragents.cli.output.session import OutputSession


class PanelHandle(ElementHandle[Panel]):
    """Handle for building a Panel element incrementally."""

    def __init__(
        self,
        handle_id: str,
        declaration_index: int,
        session: OutputSession,
        element: Panel,
    ) -> None:
        super().__init__(handle_id, "panel", declaration_index, session, element)

    def set_entry(
        self,
        key: str,
        value: str,
        *,
        style_hint: str | None = None,
        icon: str | None = None,
    ) -> None:
        """Set or update a key-value entry in the panel."""
        if not key:
            raise ValueError("key cannot be empty")
        with self._lock:
            self._require_open()
            for entry in self._element.entries:
                if entry.key == key:
                    entry.value = value
                    entry.style_hint = style_hint
                    entry.icon = icon
                    break
            else:
                self._element.entries.append(
                    PanelEntry(key=key, value=value, style_hint=style_hint, icon=icon)
                )
            self._emit_update("entry_set", {"key": key, "value": value})

    def set_entries(
        self,
        entries: dict[str, str],
        *,
        style_hints: dict[str, str] | None = None,
    ) -> None:
        """Set multiple entries at once (batch update)."""
        if entries is None:
            raise ValueError("entries cannot be None")
        with self._lock:
            self._require_open()
            hints = style_hints or {}
            for key, value in entries.items():
                hint = hints.get(key)
                found = False
                for entry in self._element.entries:
                    if entry.key == key:
                        entry.value = value
                        entry.style_hint = hint
                        found = True
                        break
                if not found:
                    self._element.entries.append(
                        PanelEntry(key=key, value=value, style_hint=hint)
                    )
            self._emit_update("entries_set", {"entries": entries})

    def remove_entry(self, key: str) -> None:
        """Remove an entry by key."""
        if not key:
            raise ValueError("key cannot be empty")
        with self._lock:
            self._require_open()
            self._element.entries = [e for e in self._element.entries if e.key != key]
            self._emit_update("entry_removed", {"key": key})

    def add_line(self, key: str, value: str) -> None:
        """Convenience alias for ``set_entry``."""
        self.set_entry(key, value)


class TableHandle(ElementHandle[Table]):
    """Handle for building a Table element incrementally."""

    def __init__(
        self,
        handle_id: str,
        declaration_index: int,
        session: OutputSession,
        element: Table,
    ) -> None:
        super().__init__(handle_id, "table", declaration_index, session, element)

    def add_row(self, row: dict[str, Any] | list[Any]) -> None:
        """Append a single row to the table."""
        if row is None:
            raise ValueError("row cannot be None")
        with self._lock:
            self._require_open()
            if len(self._element.rows) >= MAX_TABLE_ROWS:
                raise ValueError(f"Maximum table rows ({MAX_TABLE_ROWS}) exceeded")
            if isinstance(row, list):
                row_dict: dict[str, Any] = {}
                for idx, val in enumerate(row):
                    if idx < len(self._element.columns):
                        row_dict[self._element.columns[idx].name] = val
                self._element.rows.append(row_dict)
            else:
                self._element.rows.append(dict(row))
            self._emit_update("row_added", {"row": row})

    def add_rows(self, rows: list[dict[str, Any] | list[Any]]) -> None:
        """Append multiple rows in a batch."""
        if rows is None:
            raise ValueError("rows cannot be None")
        with self._lock:
            self._require_open()
            if len(self._element.rows) + len(rows) > MAX_TABLE_ROWS:
                raise ValueError(
                    f"Maximum table rows ({MAX_TABLE_ROWS}) would be exceeded"
                )
            for row in rows:
                if isinstance(row, list):
                    row_dict: dict[str, Any] = {}
                    for idx, val in enumerate(row):
                        if idx < len(self._element.columns):
                            row_dict[self._element.columns[idx].name] = val
                    self._element.rows.append(row_dict)
                else:
                    self._element.rows.append(dict(row))
            self._emit_update("rows_added", {"count": len(rows)})

    def set_summary(self, summary: dict[str, Any]) -> None:
        """Set or update the summary/aggregation row."""
        if summary is None:
            raise ValueError("summary cannot be None")
        with self._lock:
            self._require_open()
            self._element.summary = summary
            self._emit_update("summary_set", {"summary": summary})

    def set_sort_key(self, column: str, *, descending: bool = False) -> None:
        """Change the sort key and direction."""
        if not column:
            raise ValueError("column cannot be empty")
        with self._lock:
            self._require_open()
            self._element.sort_key = column
            self._element.sort_descending = descending
            self._emit_update(
                "sort_key_changed",
                {"column": column, "descending": descending},
            )


class StatusHandle(ElementHandle[StatusMessage]):
    """Handle for a status message."""

    def __init__(
        self,
        handle_id: str,
        declaration_index: int,
        session: OutputSession,
        element: StatusMessage,
    ) -> None:
        super().__init__(handle_id, "status", declaration_index, session, element)

    def set_message(self, message: str) -> None:
        """Update the status message text."""
        if not message:
            raise ValueError("message cannot be empty")
        with self._lock:
            self._require_open()
            self._element.message = message
            self._emit_update("message_changed", {"message": message})

    def set_level(self, level: str) -> None:
        """Change the status level."""
        if not level:
            raise ValueError("level cannot be empty")
        valid_levels = {"ok", "warn", "error", "info"}
        if level not in valid_levels:
            raise ValueError(f"level must be one of {valid_levels!r}, got {level!r}")
        with self._lock:
            self._require_open()
            self._element.level = level
            self._emit_update("level_changed", {"level": level})

    def set_detail(self, detail: str | None) -> None:
        """Set or clear the detail text."""
        with self._lock:
            self._require_open()
            self._element.detail = detail
            self._emit_update("detail_changed", {"detail": detail})
