"""Per-type widget state extraction for LLM-friendly serialization."""

from __future__ import annotations

from typing import Any

from textual.widget import Widget


class WidgetStateExtractor:
    """Extracts type-specific properties from Textual widgets."""

    MAX_TEXT_LENGTH = 200

    @classmethod
    def _truncate(cls, text: str) -> str:
        """Truncate text to MAX_TEXT_LENGTH, adding '...' if truncated."""
        if len(text) <= cls.MAX_TEXT_LENGTH:
            return text
        return text[: cls.MAX_TEXT_LENGTH] + "..."

    def _extract_input(self, w: Any) -> dict[str, Any]:
        return {
            "value": w.value,
            "placeholder": w.placeholder,
            "password": w.password,
            "cursor_position": w.cursor_position,
            "is_valid": w.is_valid,
        }

    def _extract_button(self, w: Any) -> dict[str, Any]:
        return {
            "label": w.label.plain,
            "variant": w.variant,
        }

    def _extract_static(self, w: Any) -> dict[str, Any]:
        content = str(w.renderable) if hasattr(w, "renderable") else str(w.render())
        return {
            "content": self._truncate(content),
        }

    def _extract_checkbox(self, w: Any) -> dict[str, Any]:
        return {
            "value": w.value,
            "label": w.label.plain,
        }

    def _extract_switch(self, w: Any) -> dict[str, Any]:
        return {
            "value": w.value,
        }

    def _extract_select(self, w: Any) -> dict[str, Any]:
        from textual.widgets._select import NoSelection

        value = w.value
        is_blank = isinstance(value, NoSelection)
        return {
            "value": None if is_blank else value,
            "expanded": w.expanded,
            "is_blank": is_blank,
            "prompt": w.prompt,
        }

    def _extract_text_area(self, w: Any) -> dict[str, Any]:
        return {
            "text": self._truncate(w.text),
            "cursor_location": list(w.cursor_location),
            "line_count": w.document.line_count,
            "language": w.language,
            "read_only": w.read_only,
        }

    def _extract_data_table(self, w: Any) -> dict[str, Any]:
        return {
            "row_count": w.row_count,
            "column_count": len(w.columns),
            "cursor_row": w.cursor_row,
            "cursor_column": w.cursor_column,
            "cursor_type": w.cursor_type,
        }

    def _extract_tree(self, w: Any) -> dict[str, Any]:
        cursor_node = w.cursor_node
        label = ""
        if cursor_node is not None:
            raw_label = cursor_node.label
            label = raw_label.plain if hasattr(raw_label, "plain") else str(raw_label)
        return {
            "cursor_line": w.cursor_line,
            "cursor_node_label": label,
        }

    def _extract_list_view(self, w: Any) -> dict[str, Any]:
        return {
            "index": w.index,
            "count": len(w),
        }

    def _extract_option_list(self, w: Any) -> dict[str, Any]:
        return {
            "highlighted": w.highlighted,
            "option_count": w.option_count,
        }

    def _extract_tabbed_content(self, w: Any) -> dict[str, Any]:
        return {
            "active": w.active,
            "tab_count": w.tab_count,
        }

    def _extract_progress_bar(self, w: Any) -> dict[str, Any]:
        return {
            "progress": w.progress,
            "total": w.total,
            "percentage": w.percentage,
        }

    def _extract_radio_set(self, w: Any) -> dict[str, Any]:
        return {
            "pressed_index": w.pressed_index,
        }

    def _extract_content_switcher(self, w: Any) -> dict[str, Any]:
        return {
            "current": w.current,
        }

    # Build extractor registry - order matters for isinstance checks.
    # More specific types (Label, Checkbox) must come before their parents (Static, ToggleButton).
    _EXTRACTORS: list[tuple[type, Any]] = []

    @classmethod
    def _init_extractors(cls) -> None:
        """Lazily initialize extractors to avoid import-time widget imports."""
        if cls._EXTRACTORS:
            return

        from textual.widgets import (
            Button,
            Checkbox,
            ContentSwitcher,
            DataTable,
            Input,
            Label,
            ListView,
            OptionList,
            ProgressBar,
            RadioSet,
            Select,
            Static,
            Switch,
            TabbedContent,
            TextArea,
            Tree,
        )

        cls._EXTRACTORS = [
            # Label before Static (Label inherits Static)
            (Label, cls._extract_static),
            # Checkbox before Static (Checkbox inherits ToggleButton inherits Static)
            (Checkbox, cls._extract_checkbox),
            (Input, cls._extract_input),
            (Button, cls._extract_button),
            (Static, cls._extract_static),
            (Switch, cls._extract_switch),
            (Select, cls._extract_select),
            (TextArea, cls._extract_text_area),
            (DataTable, cls._extract_data_table),
            (Tree, cls._extract_tree),
            (ListView, cls._extract_list_view),
            (OptionList, cls._extract_option_list),
            (TabbedContent, cls._extract_tabbed_content),
            (ProgressBar, cls._extract_progress_bar),
            (RadioSet, cls._extract_radio_set),
            (ContentSwitcher, cls._extract_content_switcher),
        ]

    @classmethod
    def extract(cls, widget: Widget) -> dict[str, Any]:
        """Extract type-specific properties from a widget."""
        cls._init_extractors()
        for widget_type, extractor in cls._EXTRACTORS:
            if isinstance(widget, widget_type):
                return extractor(cls, widget)
        return {}
