"""Tests for textual_mcp.serializers.widget_state — TDD Red phase."""

import pytest
from textual.app import App, ComposeResult
from textual.widgets import (
    Input,
    Button,
    Static,
    Checkbox,
    Switch,
    Select,
    TextArea,
    DataTable,
    Tree,
    ListView,
    ListItem,
    OptionList,
    TabbedContent,
    TabPane,
    ProgressBar,
    RadioSet,
    RadioButton,
    ContentSwitcher,
    Label,
)

from textual_mcp.serializers.widget_state import WidgetStateExtractor


class TestExtractInput:
    """Test 4.1: extract(Input) returns value, placeholder, password, cursor_position, is_valid."""

    @pytest.mark.asyncio
    async def test_extract_input_basic(self):
        class TestApp(App):
            def compose(self) -> ComposeResult:
                yield Input(value="hello", placeholder="Type here...")

        app = TestApp()
        async with app.run_test() as pilot:
            widget = app.query_one(Input)
            result = WidgetStateExtractor.extract(widget)
            assert result["value"] == "hello"
            assert result["placeholder"] == "Type here..."
            assert result["password"] is False
            assert result["cursor_position"] == 5  # end of "hello"
            assert result["is_valid"] is True

    @pytest.mark.asyncio
    async def test_extract_input_password(self):
        class TestApp(App):
            def compose(self) -> ComposeResult:
                yield Input(value="secret", password=True)

        app = TestApp()
        async with app.run_test() as pilot:
            widget = app.query_one(Input)
            result = WidgetStateExtractor.extract(widget)
            assert result["password"] is True


class TestExtractButton:
    """Test 4.2: extract(Button) returns label, variant."""

    @pytest.mark.asyncio
    async def test_extract_button(self):
        class TestApp(App):
            def compose(self) -> ComposeResult:
                yield Button("Click Me", variant="primary")

        app = TestApp()
        async with app.run_test() as pilot:
            widget = app.query_one(Button)
            result = WidgetStateExtractor.extract(widget)
            assert result["label"] == "Click Me"
            assert result["variant"] == "primary"

    @pytest.mark.asyncio
    async def test_extract_button_default_variant(self):
        class TestApp(App):
            def compose(self) -> ComposeResult:
                yield Button("OK")

        app = TestApp()
        async with app.run_test() as pilot:
            widget = app.query_one(Button)
            result = WidgetStateExtractor.extract(widget)
            assert result["label"] == "OK"
            assert result["variant"] == "default"


class TestExtractStatic:
    """Test 4.3: extract(Static) returns content, truncated to max_length."""

    @pytest.mark.asyncio
    async def test_extract_static(self):
        class TestApp(App):
            def compose(self) -> ComposeResult:
                yield Static("Hello World")

        app = TestApp()
        async with app.run_test() as pilot:
            widget = app.query_one(Static)
            result = WidgetStateExtractor.extract(widget)
            assert result["content"] == "Hello World"

    @pytest.mark.asyncio
    async def test_extract_static_truncation(self):
        long_text = "A" * 300
        class TestApp(App):
            def compose(self) -> ComposeResult:
                yield Static(long_text)

        app = TestApp()
        async with app.run_test() as pilot:
            widget = app.query_one(Static)
            result = WidgetStateExtractor.extract(widget)
            assert len(result["content"]) <= WidgetStateExtractor.MAX_TEXT_LENGTH + 3  # +3 for "..."
            assert result["content"].endswith("...")


class TestExtractCheckbox:
    """Test 4.4: extract(Checkbox) returns value (bool), label (str)."""

    @pytest.mark.asyncio
    async def test_extract_checkbox(self):
        class TestApp(App):
            def compose(self) -> ComposeResult:
                yield Checkbox("Accept terms", value=True)

        app = TestApp()
        async with app.run_test() as pilot:
            widget = app.query_one(Checkbox)
            result = WidgetStateExtractor.extract(widget)
            assert result["value"] is True
            assert result["label"] == "Accept terms"

    @pytest.mark.asyncio
    async def test_extract_checkbox_unchecked(self):
        class TestApp(App):
            def compose(self) -> ComposeResult:
                yield Checkbox("Option", value=False)

        app = TestApp()
        async with app.run_test() as pilot:
            widget = app.query_one(Checkbox)
            result = WidgetStateExtractor.extract(widget)
            assert result["value"] is False


class TestExtractSwitch:
    """Test 4.5: extract(Switch) returns value (bool)."""

    @pytest.mark.asyncio
    async def test_extract_switch(self):
        class TestApp(App):
            def compose(self) -> ComposeResult:
                yield Switch(value=True)

        app = TestApp()
        async with app.run_test() as pilot:
            widget = app.query_one(Switch)
            result = WidgetStateExtractor.extract(widget)
            assert result["value"] is True

    @pytest.mark.asyncio
    async def test_extract_switch_off(self):
        class TestApp(App):
            def compose(self) -> ComposeResult:
                yield Switch(value=False)

        app = TestApp()
        async with app.run_test() as pilot:
            widget = app.query_one(Switch)
            result = WidgetStateExtractor.extract(widget)
            assert result["value"] is False


class TestExtractSelect:
    """Test 4.6: extract(Select) returns value, expanded, is_blank, prompt."""

    @pytest.mark.asyncio
    async def test_extract_select_with_value(self):
        class TestApp(App):
            def compose(self) -> ComposeResult:
                yield Select(
                    [("Option A", "a"), ("Option B", "b")],
                    value="a",
                    prompt="Choose...",
                )

        app = TestApp()
        async with app.run_test() as pilot:
            widget = app.query_one(Select)
            result = WidgetStateExtractor.extract(widget)
            assert result["value"] == "a"
            assert result["expanded"] is False
            assert result["is_blank"] is False
            assert result["prompt"] == "Choose..."

    @pytest.mark.asyncio
    async def test_extract_select_blank(self):
        class TestApp(App):
            def compose(self) -> ComposeResult:
                yield Select(
                    [("Option A", "a"), ("Option B", "b")],
                    prompt="Pick one",
                )

        app = TestApp()
        async with app.run_test() as pilot:
            widget = app.query_one(Select)
            result = WidgetStateExtractor.extract(widget)
            assert result["is_blank"] is True
            assert result["prompt"] == "Pick one"


class TestExtractTextArea:
    """Test 4.7: extract(TextArea) returns text (truncated), cursor_location, line_count, language, read_only."""

    @pytest.mark.asyncio
    async def test_extract_text_area(self):
        class TestApp(App):
            def compose(self) -> ComposeResult:
                yield TextArea("line1\nline2\nline3", language="python")

        app = TestApp()
        async with app.run_test() as pilot:
            widget = app.query_one(TextArea)
            result = WidgetStateExtractor.extract(widget)
            assert result["text"] == "line1\nline2\nline3"
            assert result["line_count"] == 3
            assert result["language"] == "python"
            assert result["read_only"] is False
            assert "cursor_location" in result

    @pytest.mark.asyncio
    async def test_extract_text_area_truncation(self):
        long_text = "x" * 300
        class TestApp(App):
            def compose(self) -> ComposeResult:
                yield TextArea(long_text)

        app = TestApp()
        async with app.run_test() as pilot:
            widget = app.query_one(TextArea)
            result = WidgetStateExtractor.extract(widget)
            assert len(result["text"]) <= WidgetStateExtractor.MAX_TEXT_LENGTH + 3


class TestExtractDataTable:
    """Test 4.8: extract(DataTable) returns row_count, column_count, cursor_row, cursor_column, cursor_type."""

    @pytest.mark.asyncio
    async def test_extract_data_table(self):
        class TestApp(App):
            def compose(self) -> ComposeResult:
                yield DataTable()

            def on_mount(self) -> None:
                table = self.query_one(DataTable)
                table.add_columns("Name", "Age")
                table.add_rows([("Alice", 30), ("Bob", 25)])

        app = TestApp()
        async with app.run_test() as pilot:
            widget = app.query_one(DataTable)
            result = WidgetStateExtractor.extract(widget)
            assert result["row_count"] == 2
            assert result["column_count"] == 2
            assert "cursor_row" in result
            assert "cursor_column" in result
            assert result["cursor_type"] == "cell"


class TestExtractTree:
    """Test 4.9: extract(Tree) returns cursor_line, cursor_node_label."""

    @pytest.mark.asyncio
    async def test_extract_tree(self):
        class TestApp(App):
            def compose(self) -> ComposeResult:
                tree = Tree("Root")
                tree.root.add_leaf("Child 1")
                tree.root.add_leaf("Child 2")
                yield tree

        app = TestApp()
        async with app.run_test() as pilot:
            widget = app.query_one(Tree)
            result = WidgetStateExtractor.extract(widget)
            assert "cursor_line" in result
            assert "cursor_node_label" in result


class TestExtractListView:
    """Test 4.10: extract(ListView) returns index, count."""

    @pytest.mark.asyncio
    async def test_extract_list_view(self):
        class TestApp(App):
            def compose(self) -> ComposeResult:
                yield ListView(
                    ListItem(Label("Item 1")),
                    ListItem(Label("Item 2")),
                    ListItem(Label("Item 3")),
                )

        app = TestApp()
        async with app.run_test() as pilot:
            widget = app.query_one(ListView)
            result = WidgetStateExtractor.extract(widget)
            assert result["index"] == 0
            assert result["count"] == 3


class TestExtractOptionList:
    """Test 4.11: extract(OptionList) returns highlighted, option_count."""

    @pytest.mark.asyncio
    async def test_extract_option_list(self):
        class TestApp(App):
            def compose(self) -> ComposeResult:
                yield OptionList("One", "Two", "Three")

        app = TestApp()
        async with app.run_test() as pilot:
            widget = app.query_one(OptionList)
            result = WidgetStateExtractor.extract(widget)
            assert result["highlighted"] == 0
            assert result["option_count"] == 3


class TestExtractTabbedContent:
    """Test 4.12: extract(TabbedContent) returns active, tab_count."""

    @pytest.mark.asyncio
    async def test_extract_tabbed_content(self):
        class TestApp(App):
            def compose(self) -> ComposeResult:
                with TabbedContent():
                    with TabPane("Tab 1", id="tab1"):
                        yield Static("Content 1")
                    with TabPane("Tab 2", id="tab2"):
                        yield Static("Content 2")

        app = TestApp()
        async with app.run_test() as pilot:
            widget = app.query_one(TabbedContent)
            result = WidgetStateExtractor.extract(widget)
            assert result["active"] == "tab1"
            assert result["tab_count"] == 2


class TestExtractProgressBar:
    """Test 4.13: extract(ProgressBar) returns progress, total, percentage."""

    @pytest.mark.asyncio
    async def test_extract_progress_bar(self):
        class TestApp(App):
            def compose(self) -> ComposeResult:
                yield ProgressBar(total=100)

            def on_mount(self) -> None:
                self.query_one(ProgressBar).progress = 50

        app = TestApp()
        async with app.run_test() as pilot:
            widget = app.query_one(ProgressBar)
            result = WidgetStateExtractor.extract(widget)
            assert result["progress"] == 50
            assert result["total"] == 100
            assert result["percentage"] == 0.5


class TestExtractRadioSet:
    """Test 4.14: extract(RadioSet) returns pressed_index."""

    @pytest.mark.asyncio
    async def test_extract_radio_set(self):
        class TestApp(App):
            def compose(self) -> ComposeResult:
                yield RadioSet(
                    RadioButton("Option A"),
                    RadioButton("Option B", value=True),
                    RadioButton("Option C"),
                )

        app = TestApp()
        async with app.run_test() as pilot:
            widget = app.query_one(RadioSet)
            result = WidgetStateExtractor.extract(widget)
            assert result["pressed_index"] == 1


class TestExtractContentSwitcher:
    """Test 4.15: extract(ContentSwitcher) returns current."""

    @pytest.mark.asyncio
    async def test_extract_content_switcher(self):
        class TestApp(App):
            def compose(self) -> ComposeResult:
                with ContentSwitcher(initial="panel-a"):
                    yield Static("Panel A", id="panel-a")
                    yield Static("Panel B", id="panel-b")

        app = TestApp()
        async with app.run_test() as pilot:
            widget = app.query_one(ContentSwitcher)
            result = WidgetStateExtractor.extract(widget)
            assert result["current"] == "panel-a"


class TestExtractUnknown:
    """Test 4.16: extract(unknown_widget) returns empty dict."""

    @pytest.mark.asyncio
    async def test_extract_unknown_widget(self):
        from textual.widget import Widget

        class CustomWidget(Widget):
            pass

        class TestApp(App):
            def compose(self) -> ComposeResult:
                yield CustomWidget()

        app = TestApp()
        async with app.run_test() as pilot:
            widget = app.query_one(CustomWidget)
            result = WidgetStateExtractor.extract(widget)
            assert result == {}


class TestExtractLabel:
    """Test 4.17: extract(Label) returns content (inherits Static behavior)."""

    @pytest.mark.asyncio
    async def test_extract_label(self):
        class TestApp(App):
            def compose(self) -> ComposeResult:
                yield Label("Hello Label")

        app = TestApp()
        async with app.run_test() as pilot:
            widget = app.query_one(Label)
            result = WidgetStateExtractor.extract(widget)
            assert result["content"] == "Hello Label"
