"""Tests for the Watches pane: table rendering, navigation, actions."""

import pytest
from unittest.mock import MagicMock, patch

from textual.app import App, ComposeResult
from textual.widgets import DataTable, Static

from skilark_cli.tui.screens.watches import WatchesPane


SAMPLE_WATCHES = [
    {
        "id": "w1",
        "label": "ML Roles",
        "skills": ["python", "pytorch"],
        "seniority": "senior",
        "location": "San Francisco, CA",
        "is_active": True,
        "pending_alerts": 3,
    },
    {
        "id": "w2",
        "label": "Go Backend",
        "skills": ["go"],
        "seniority": "",
        "location": "Remote",
        "is_active": False,
        "pending_alerts": 0,
    },
]

SAMPLE_ALERTS = [
    {
        "id": "a1",
        "listing_title": "Senior ML Engineer",
        "listing_company": "Anthropic",
        "listing_location": "San Francisco, CA",
        "listing_url": "https://example.com/job/1",
        "created_at": "2026-03-28T10:00:00Z",
    },
    {
        "id": "a2",
        "listing_title": "ML Platform Lead",
        "listing_company": "Stripe",
        "listing_location": "",
        "listing_url": "https://example.com/job/2",
        "created_at": "2026-03-27T08:00:00Z",
    },
]


class WatchesTestApp(App):
    """Minimal test app that hosts a WatchesPane."""

    def __init__(self, client):
        super().__init__()
        self.client = client

    def compose(self) -> ComposeResult:
        yield WatchesPane(self.client, id="watches-pane")


def _mock_client(watches=None, alerts=None):
    client = MagicMock()
    client.list_watches.return_value = (
        watches if watches is not None else SAMPLE_WATCHES
    )
    client.get_watch_alerts.return_value = {
        "count": len(alerts) if alerts is not None else 2,
        "alerts": alerts if alerts is not None else SAMPLE_ALERTS,
    }
    client.delete_watch.return_value = None
    client.update_watch.return_value = {"id": "w1", "is_active": False}
    return client


class TestWatchesRendering:
    async def test_table_has_rows(self):
        """Mock client returns 2 watches, verify DataTable has 2 rows."""
        client = _mock_client()
        app = WatchesTestApp(client)
        async with app.run_test() as pilot:
            await pilot.pause(delay=0.3)
            table = app.query_one("#watches-table", DataTable)
            assert table.row_count == 2

    async def test_empty_shows_message(self):
        """Mock returns [], verify header shows empty message."""
        client = _mock_client(watches=[])
        app = WatchesTestApp(client)
        async with app.run_test() as pilot:
            await pilot.pause(delay=0.3)
            header = app.query_one("#watches-header", Static)
            assert "No watches" in header.content

    async def test_watches_table_columns(self):
        """Verify table has expected columns."""
        client = _mock_client()
        app = WatchesTestApp(client)
        async with app.run_test() as pilot:
            await pilot.pause(delay=0.3)
            table = app.query_one("#watches-table", DataTable)
            col_labels = [str(c.label) for c in table.columns.values()]
            assert col_labels == ["Label", "Filters", "Status", "Alerts"]

    async def test_alerts_table_hidden_initially(self):
        """Alerts table should be hidden on mount."""
        client = _mock_client()
        app = WatchesTestApp(client)
        async with app.run_test() as pilot:
            await pilot.pause(delay=0.3)
            alerts_table = app.query_one("#alerts-table", DataTable)
            assert not alerts_table.display


class TestWatchesActions:
    async def test_pause_toggles_watch(self):
        """Press p, verify client.update_watch called."""
        client = _mock_client()
        app = WatchesTestApp(client)
        async with app.run_test() as pilot:
            await pilot.pause(delay=0.3)
            # Focus the watches table and select first row
            table = app.query_one("#watches-table", DataTable)
            table.focus()
            await pilot.pause(delay=0.1)
            await pilot.press("p")
            await pilot.pause(delay=0.3)
            client.update_watch.assert_called_once_with(
                "w1", is_active=False
            )

    async def test_delete_requires_confirmation(self):
        """Press d once shows confirmation, d again actually deletes."""
        client = _mock_client()
        app = WatchesTestApp(client)
        async with app.run_test() as pilot:
            await pilot.pause(delay=0.3)
            table = app.query_one("#watches-table", DataTable)
            table.focus()
            await pilot.pause(delay=0.1)
            # First press: confirmation prompt, no API call
            await pilot.press("d")
            await pilot.pause(delay=0.1)
            client.delete_watch.assert_not_called()
            header = app.query_one("#watches-header", Static)
            assert "again" in str(header.content).lower()
            # Second press: actual delete
            await pilot.press("d")
            await pilot.pause(delay=0.3)
            client.delete_watch.assert_called_once_with("w1")

    async def test_refresh_reloads(self):
        """Press r, verify client.list_watches called again."""
        client = _mock_client()
        app = WatchesTestApp(client)
        async with app.run_test() as pilot:
            await pilot.pause(delay=0.3)
            assert client.list_watches.call_count == 1
            await pilot.press("r")
            await pilot.pause(delay=0.3)
            assert client.list_watches.call_count == 2


class TestWatchesNavigation:
    async def test_enter_shows_alerts(self):
        """Press enter on watch, verify alerts table visible with data."""
        client = _mock_client()
        app = WatchesTestApp(client)
        async with app.run_test() as pilot:
            await pilot.pause(delay=0.3)
            table = app.query_one("#watches-table", DataTable)
            table.focus()
            await pilot.pause(delay=0.1)
            await pilot.press("enter")
            await pilot.pause(delay=0.3)
            alerts_table = app.query_one("#alerts-table", DataTable)
            watches_table = app.query_one("#watches-table", DataTable)
            assert alerts_table.display
            assert not watches_table.display
            assert alerts_table.row_count == 2

    async def test_back_returns_to_watches(self):
        """Enter then b, verify watches table visible again."""
        client = _mock_client()
        app = WatchesTestApp(client)
        async with app.run_test() as pilot:
            await pilot.pause(delay=0.3)
            table = app.query_one("#watches-table", DataTable)
            table.focus()
            await pilot.pause(delay=0.1)
            # Navigate into alerts
            await pilot.press("enter")
            await pilot.pause(delay=0.3)
            # Navigate back
            await pilot.press("b")
            await pilot.pause(delay=0.1)
            watches_table = app.query_one("#watches-table", DataTable)
            alerts_table = app.query_one("#alerts-table", DataTable)
            assert watches_table.display
            assert not alerts_table.display

    async def test_escape_returns_to_watches(self):
        """Enter then escape, verify watches table visible again."""
        client = _mock_client()
        app = WatchesTestApp(client)
        async with app.run_test() as pilot:
            await pilot.pause(delay=0.3)
            table = app.query_one("#watches-table", DataTable)
            table.focus()
            await pilot.pause(delay=0.1)
            await pilot.press("enter")
            await pilot.pause(delay=0.3)
            await pilot.press("escape")
            await pilot.pause(delay=0.1)
            watches_table = app.query_one("#watches-table", DataTable)
            assert watches_table.display

    async def test_open_link_calls_webbrowser(self):
        """In alerts view, press o to open listing URL."""
        client = _mock_client()
        app = WatchesTestApp(client)
        async with app.run_test() as pilot:
            await pilot.pause(delay=0.3)
            table = app.query_one("#watches-table", DataTable)
            table.focus()
            await pilot.pause(delay=0.1)
            # Navigate into alerts
            await pilot.press("enter")
            await pilot.pause(delay=0.3)
            with patch(
                "skilark_cli.tui.screens.watches.webbrowser"
            ) as mock_wb:
                await pilot.press("o")
                await pilot.pause(delay=0.1)
                mock_wb.open.assert_called_once_with(
                    "https://example.com/job/1"
                )


class TestWatchesActivate:
    async def test_activate_always_reloads(self):
        """Calling activate() when watches exist still reloads from API."""
        client = _mock_client()
        app = WatchesTestApp(client)
        async with app.run_test() as pilot:
            await pilot.pause(delay=0.3)
            # Initial mount loaded watches
            assert client.list_watches.call_count >= 1
            initial_count = client.list_watches.call_count
            # Simulate tab switch by calling activate again
            pane = app.query_one("#watches-pane", WatchesPane)
            pane.activate()
            await pilot.pause(delay=0.3)
            assert client.list_watches.call_count > initial_count
