"""Tests for the Signals screen: table rendering, filter, refresh, empty state."""

import pytest
from unittest.mock import MagicMock

from textual.widgets import DataTable, Input, Static

from skilark_cli.tui.app import SkilarkApp
from skilark_cli.tui.screens.signals import SignalsPane


SAMPLE_SIGNALS = {
    "week_of": "2026-03-02",
    "signals": [
        {
            "signal_type": "company_surge",
            "title": "Stripe hiring surge",
            "body": "Stripe posted 15 new ML roles this week, 3x previous week.",
        },
        {
            "signal_type": "company_surge",
            "title": "Anthropic hiring surge",
            "body": "Anthropic posted 23 new backend roles, 2x previous week.",
        },
        {
            "signal_type": "salary_insight",
            "title": "Kubernetes salary premium",
            "body": "Kubernetes roles pay $185K median, 15% above market.",
        },
    ],
}


def _mock_client(data=None, error=None):
    client = MagicMock()
    if error:
        client.get_signals.side_effect = error
    else:
        client.get_signals.return_value = data or SAMPLE_SIGNALS
    # Drill tab needs these on app launch (default tab is now Drill)
    client.get_next_challenge.return_value = None
    client.get_stats.return_value = {"total_completed": 0}
    return client


@pytest.fixture
def app_with_signals():
    client = _mock_client()
    # Start on Signals tab (index 1) so signals widgets are mounted
    app = SkilarkApp(client=client)
    app._active_tab = 1
    return app


@pytest.fixture
def app_empty():
    client = _mock_client(data={"week_of": "2026-03-02", "signals": []})
    app = SkilarkApp(client=client)
    app._active_tab = 1
    return app


@pytest.fixture
def app_error():
    client = _mock_client(error=Exception("Connection refused"))
    app = SkilarkApp(client=client)
    app._active_tab = 1
    return app


class TestSignalsRendering:
    async def test_signals_table_has_rows(self, app_with_signals):
        async with app_with_signals.run_test() as pilot:
            await pilot.pause(delay=0.2)
            table = app_with_signals.query_one("#signals-table", DataTable)
            assert table.row_count == 3

    async def test_header_shows_week(self, app_with_signals):
        async with app_with_signals.run_test() as pilot:
            await pilot.pause(delay=0.2)
            header = app_with_signals.query_one("#signals-header", Static)
            assert "Mar 2" in header.content

    async def test_header_shows_count(self, app_with_signals):
        async with app_with_signals.run_test() as pilot:
            await pilot.pause(delay=0.2)
            header = app_with_signals.query_one("#signals-header", Static)
            assert "3 signals" in header.content

    async def test_empty_signals(self, app_empty):
        async with app_empty.run_test() as pilot:
            await pilot.pause(delay=0.2)
            table = app_empty.query_one("#signals-table", DataTable)
            assert table.row_count == 0

    async def test_api_error(self, app_error):
        async with app_error.run_test() as pilot:
            await pilot.pause(delay=0.2)
            header = app_error.query_one("#signals-header", Static)
            assert "Connection refused" in header.content


class TestSignalsFilter:
    async def test_filter_input_hidden_by_default(self, app_with_signals):
        async with app_with_signals.run_test() as pilot:
            await pilot.pause(delay=0.2)
            filter_input = app_with_signals.query_one("#filter-input", Input)
            assert not filter_input.display

    async def test_slash_shows_filter(self, app_with_signals):
        async with app_with_signals.run_test() as pilot:
            await pilot.pause(delay=0.2)
            await pilot.press("slash")
            filter_input = app_with_signals.query_one("#filter-input", Input)
            assert filter_input.display
            assert filter_input.has_focus

    async def test_filter_reduces_rows(self, app_with_signals):
        async with app_with_signals.run_test() as pilot:
            await pilot.pause(delay=0.2)
            await pilot.press("slash")
            await pilot.pause()
            # Type filter text via keystrokes
            for char in "stripe":
                await pilot.press(char)
            await pilot.pause()
            table = app_with_signals.query_one("#signals-table", DataTable)
            assert table.row_count == 1

    async def test_escape_clears_filter(self, app_with_signals):
        async with app_with_signals.run_test() as pilot:
            await pilot.pause(delay=0.2)
            await pilot.press("slash")
            await pilot.pause()
            for char in "stripe":
                await pilot.press(char)
            await pilot.pause()
            table = app_with_signals.query_one("#signals-table", DataTable)
            assert table.row_count == 1
            await pilot.press("escape")
            await pilot.pause()
            assert table.row_count == 3


class TestSignalsRefresh:
    async def test_refresh_refetches(self, app_with_signals):
        async with app_with_signals.run_test() as pilot:
            await pilot.pause(delay=0.2)
            await pilot.press("r")
            await pilot.pause(delay=0.2)
            assert app_with_signals.client.get_signals.call_count == 2

    async def test_refresh_shows_loading(self, app_with_signals):
        async with app_with_signals.run_test() as pilot:
            await pilot.pause(delay=0.2)
            # Make the next call slow so we can see "Loading..."
            import threading

            barrier = threading.Event()
            original_fn = app_with_signals.client.get_signals.return_value

            def slow_fetch(*args, **kwargs):
                barrier.wait(timeout=1)
                return SAMPLE_SIGNALS

            app_with_signals.client.get_signals.side_effect = slow_fetch
            await pilot.press("r")
            await pilot.pause()
            header = app_with_signals.query_one("#signals-header", Static)
            assert "Loading" in header.content
            barrier.set()
            await pilot.pause(delay=0.2)
