"""Tests for supporting tools (watcher, web, dataset)."""

import pytest
import respx
from httpx import Response

from crustdata_mcp.client import CrustdataClient


class TestWatcherTools:
    """Tests for watcher tools."""

    @pytest.mark.asyncio
    @respx.mock
    async def test_watcher_create_success(self):
        """Test successful watcher creation."""
        expected_response = {"message": "Watch created successfully", "id": 123}
        respx.post("https://api.crustdata.com/watcher/watches/").mock(
            return_value=Response(201, json=expected_response)
        )

        async with CrustdataClient("test-token") as client:
            result = await client.post(
                "/watcher/watches/",
                data={
                    "event_type_slug": "job-posting-with-keyword-and-location",
                    "event_filters": [{"filter_type": "TITLE", "type": "in", "value": "Engineer"}],
                    "notification_endpoint": "https://example.com/webhook",
                    "frequency": 1,
                },
            )

        assert result["id"] == 123
        assert "Watch created" in result["message"]

    @pytest.mark.asyncio
    @respx.mock
    async def test_watcher_update_success(self):
        """Test successful watcher update."""
        expected_response = {"message": "Watch updated successfully"}
        respx.post("https://api.crustdata.com/watcher/watches/123/update/").mock(
            return_value=Response(200, json=expected_response)
        )

        async with CrustdataClient("test-token") as client:
            result = await client.post(
                "/watcher/watches/123/update/",
                data={"status": "paused"},
            )

        assert "Watch updated" in result["message"]

    @pytest.mark.asyncio
    @respx.mock
    async def test_watcher_list_success(self):
        """Test listing watcher subscriptions."""
        expected_response = [
            {"id": 1, "event_type_slug": "job-posting", "status": "active"},
            {"id": 2, "event_type_slug": "linkedin-posts", "status": "paused"},
        ]
        respx.get("https://api.crustdata.com/watcher/watches/").mock(
            return_value=Response(200, json=expected_response)
        )

        async with CrustdataClient("test-token") as client:
            result = await client.get("/watcher/watches/")

        assert len(result) == 2

    def test_watcher_tools_registered(self):
        """Test that watcher tools are registered."""
        from crustdata_mcp.server import mcp

        tools = list(mcp._tool_manager._tools.keys())
        assert "crustdata_watcher_create" in tools
        assert "crustdata_watcher_update" in tools
        assert "crustdata_watcher_simulate" in tools
        assert "crustdata_watcher_list" in tools


class TestWatcherInputValidation:
    """Tests for watcher input validation with enums."""

    def test_valid_event_type_slug_create(self):
        """Test that valid event type slugs are accepted for watcher create."""
        from crustdata_mcp.tools.watcher import WatcherCreateInput

        # All valid event types should work
        valid_types = [
            "job-posting-with-keyword-and-location",
            "company-watch-linkedin-posts",
            "linkedin-person-post-updates",
        ]
        for event_type in valid_types:
            input_data = WatcherCreateInput(
                event_type_slug=event_type,
                event_filters=[{"filter_type": "TITLE", "type": "in", "value": "Engineer"}],
                notification_endpoint="https://example.com/webhook",
            )
            assert input_data.event_type_slug == event_type

    def test_invalid_event_type_slug_create(self):
        """Test that invalid event type slugs are rejected for watcher create."""
        from pydantic import ValidationError

        from crustdata_mcp.tools.watcher import WatcherCreateInput

        with pytest.raises(ValidationError) as exc_info:
            WatcherCreateInput(
                event_type_slug="invalid-event-type",
                event_filters=[{"filter_type": "TITLE", "type": "in", "value": "Engineer"}],
                notification_endpoint="https://example.com/webhook",
            )
        assert "Invalid event_type_slug" in str(exc_info.value)
        assert "job-posting-with-keyword-and-location" in str(exc_info.value)

    def test_valid_event_type_slug_simulate(self):
        """Test that valid event type slugs are accepted for watcher simulate."""
        from crustdata_mcp.tools.watcher import WatcherSimulateInput

        input_data = WatcherSimulateInput(
            event_type_slug="company-watch-linkedin-posts",
            event_filters=[{"filter_type": "REGION", "type": "in", "value": "SF"}],
            notification_endpoint="https://example.com/webhook",
        )
        assert input_data.event_type_slug == "company-watch-linkedin-posts"

    def test_invalid_event_type_slug_simulate(self):
        """Test that invalid event type slugs are rejected for watcher simulate."""
        from pydantic import ValidationError

        from crustdata_mcp.tools.watcher import WatcherSimulateInput

        with pytest.raises(ValidationError) as exc_info:
            WatcherSimulateInput(
                event_type_slug="not-a-valid-type",
                event_filters=[{"filter_type": "REGION", "type": "in", "value": "SF"}],
                notification_endpoint="https://example.com/webhook",
            )
        assert "Invalid event_type_slug" in str(exc_info.value)

    def test_valid_status_update(self):
        """Test that valid status values are accepted for watcher update."""
        from crustdata_mcp.tools.watcher import WatcherUpdateInput

        valid_statuses = ["active", "paused", "cancelled"]
        for status in valid_statuses:
            input_data = WatcherUpdateInput(
                subscription_id=123,
                status=status,
            )
            assert input_data.status == status

    def test_invalid_status_update(self):
        """Test that invalid status values are rejected for watcher update."""
        from pydantic import ValidationError

        from crustdata_mcp.tools.watcher import WatcherUpdateInput

        with pytest.raises(ValidationError) as exc_info:
            WatcherUpdateInput(
                subscription_id=123,
                status="invalid_status",
            )
        assert "Invalid status" in str(exc_info.value)
        assert "active" in str(exc_info.value)

    def test_status_none_is_valid(self):
        """Test that None status is accepted (optional field)."""
        from crustdata_mcp.tools.watcher import WatcherUpdateInput

        input_data = WatcherUpdateInput(
            subscription_id=123,
            notification_endpoint="https://example.com/webhook",
        )
        assert input_data.status is None


class TestWatcherFilterValidation:
    """Tests for WatcherFilter operator validation."""

    def test_valid_filter_operators(self):
        """Test that valid filter operators are accepted."""
        from crustdata_mcp.tools.watcher import WatcherFilter

        valid_operators = ["in", "=", "!=", ">", "<", ">=", "<=", "not_in", "not in", "(.)", "[.]", "between", "contains"]
        for op in valid_operators:
            filter_obj = WatcherFilter(
                filter_type="TITLE",
                type=op,
                value="Engineer",
            )
            assert filter_obj.type == op

    def test_invalid_filter_operator(self):
        """Test that invalid filter operator is rejected."""
        from pydantic import ValidationError

        from crustdata_mcp.tools.watcher import WatcherFilter

        with pytest.raises(ValidationError) as exc_info:
            WatcherFilter(
                filter_type="TITLE",
                type="invalid_operator",
                value="Engineer",
            )
        assert "Invalid filter operator" in str(exc_info.value)

    def test_filter_with_list_value(self):
        """Test filter with list value."""
        from crustdata_mcp.tools.watcher import WatcherFilter

        filter_obj = WatcherFilter(
            filter_type="REGION",
            type="in",
            value=["San Francisco", "New York"],
        )
        assert filter_obj.value == ["San Francisco", "New York"]


class TestWebTools:
    """Tests for web tools."""

    @pytest.mark.asyncio
    @respx.mock
    async def test_web_search_success(self):
        """Test successful web search."""
        expected_response = {
            "success": True,
            "query": "Anthropic AI",
            "results": [
                {"title": "Anthropic", "url": "https://anthropic.com", "snippet": "AI safety company"},
                {"title": "About Anthropic", "url": "https://anthropic.com/about", "snippet": "About us"},
            ],
            "metadata": {},
        }
        respx.post("https://api.crustdata.com/screener/web-search").mock(
            return_value=Response(200, json=expected_response)
        )

        async with CrustdataClient("test-token") as client:
            result = await client.post(
                "/screener/web-search",
                data={"query": "Anthropic AI", "limit": 10},
            )

        assert result["success"] is True
        assert len(result["results"]) == 2
        assert result["results"][0]["title"] == "Anthropic"

    @pytest.mark.asyncio
    @respx.mock
    async def test_web_fetch_success(self):
        """Test successful web fetch."""
        expected_response = {
            "content": "Anthropic is an AI safety company...",
            "metadata": {"title": "Anthropic", "description": "AI Safety"},
        }
        respx.post("https://api.crustdata.com/screener/web-fetch").mock(
            return_value=Response(200, json=expected_response)
        )

        async with CrustdataClient("test-token") as client:
            result = await client.post(
                "/screener/web-fetch",
                data={"urls": ["https://anthropic.com"], "extract_text": True},
            )

        assert "Anthropic" in result["content"]
        assert result["metadata"]["title"] == "Anthropic"

    def test_web_tools_registered(self):
        """Test that web tools are registered."""
        from crustdata_mcp.server import mcp

        tools = list(mcp._tool_manager._tools.keys())
        assert "crustdata_web_search" in tools
        assert "crustdata_web_fetch" in tools


class TestDatasetTools:
    """Tests for dataset tools."""

    @pytest.mark.asyncio
    @respx.mock
    async def test_job_listings_success(self):
        """Test successful job listings query."""
        expected_response = {
            "job_listings": [
                {"title": "Software Engineer", "company_name": "Google", "location": "Mountain View"},
                {"title": "Data Scientist", "company_name": "Meta", "location": "Menlo Park"},
            ],
            "total_count": 2,
        }
        respx.post("https://api.crustdata.com/data_lab/job_listings/Table/").mock(
            return_value=Response(200, json=expected_response)
        )

        async with CrustdataClient("test-token") as client:
            result = await client.post(
                "/data_lab/job_listings/Table/",
                data={
                    "filters": {"filter_type": "title", "type": "(.)", "value": "Engineer"},
                    "limit": 10,
                },
            )

        assert len(result["job_listings"]) == 2
        assert result["total_count"] == 2

    def test_dataset_tools_registered(self):
        """Test that dataset tools are registered."""
        from crustdata_mcp.server import mcp

        tools = list(mcp._tool_manager._tools.keys())
        assert "crustdata_dataset_job_listings" in tools
