import pytest
import json
import asyncio
from unittest.mock import patch, MagicMock, AsyncMock
import os
import sys
import uuid

# Add the app directory to sys.path
current_dir = os.path.dirname(os.path.abspath(__file__))
parent_dir = os.path.dirname(current_dir)
app_dir = os.path.join(parent_dir, "app")
if app_dir not in sys.path:
    sys.path.insert(0, app_dir)

class TestMCPServer:
    """Test the MCP server functionality."""
    
    def test_server_version(self):
        """Test that the server has a version attribute."""
        # Import the server module directly without mocking
        # This ensures we're testing the actual code
        from app.server import mcp
        
        # All MCP servers should have a name, and it should be "Hass-MCP"
        assert hasattr(mcp, "name")
        assert mcp.name == "Hass-MCP"

    def test_async_handler_decorator(self):
        """Test the async_handler decorator."""
        # Import the decorator
        from app.server import async_handler
        
        # Create a test async function
        async def test_func(arg1, arg2=None):
            return f"{arg1}_{arg2}"
        
        # Apply the decorator
        decorated_func = async_handler("test_command")(test_func)
        
        # Run the decorated function
        result = asyncio.run(decorated_func("val1", arg2="val2"))
        
        # Verify the result
        assert result == "val1_val2"
    
    def test_tool_functions_exist(self):
        """Test that tool functions exist in the server module."""
        # Import the server module directly
        import app.server
        
        # List of expected tool functions
        expected_tools = [
            "get_version",
            "get_entity",
            "list_entities",
            "entity_action",
            "search_entities",
            "domain_summary",
            "system_overview",
            "call_service",
            "restart_ha",
            "list_automations",
            "list_automation_traces",
            "get_automation_trace",
            "get_error_log",
            "get_core_logs",
            "set_log_level",
            "get_history",
            "get_statistics",
            "query_entities",
            "remove_entity",
            "update_entity",
            "get_entity_registry",
            "list_entity_registry",
        ]
        
        # Check that each expected tool function exists
        for tool_name in expected_tools:
            assert hasattr(app.server, tool_name)
            assert callable(getattr(app.server, tool_name))
    
    @pytest.mark.asyncio
    async def test_list_automations_error_handling(self):
        """Test that list_automations handles errors properly."""
        from app.server import list_automations

        # Mock the get_automations function with different scenarios
        with patch("app.server.get_automations") as mock_get_automations:
            # Case 1: Test with dict error response
            mock_get_automations.return_value = {"error": "HTTP error: 404 - Not Found"}

            # Should return a dict with empty automations list and error
            result = await list_automations()
            assert isinstance(result, dict)
            assert result["automations"] == []
            assert "error" in result

            # Case 2: Test with unexpected error (exception)
            mock_get_automations.side_effect = Exception("Unexpected error")

            # Should return a dict with empty automations list and error
            result = await list_automations()
            assert isinstance(result, dict)
            assert result["automations"] == []
            assert "error" in result
            assert result["error"] == "Error listing automations"

            # Case 3: Test with successful response (new dict format)
            mock_automations = {
                "automations": [
                    {
                        "id": "morning_lights",
                        "entity_id": "automation.morning_lights",
                        "state": "on",
                        "alias": "Turn on lights in the morning"
                    }
                ],
                "count": 1,
                "total_available": 1,
                "truncated": False
            }
            mock_get_automations.side_effect = None
            mock_get_automations.return_value = mock_automations

            # Should return the automations dict
            result = await list_automations()
            assert isinstance(result, dict)
            assert len(result["automations"]) == 1
            assert result["automations"][0]["id"] == "morning_lights"
            assert result["count"] == 1
            
    def test_tools_have_proper_docstrings(self):
        """Test that tool functions have proper docstrings"""
        # Import the server module directly
        import app.server
        
        # List of expected tool functions
        tool_functions = [
            "get_version",
            "get_entity",
            "list_entities",
            "entity_action",
            "search_entities",
            "domain_summary",
            "system_overview",
            "call_service",
            "restart_ha",
            "list_automations",
            "list_automation_traces",
            "get_automation_trace",
            "get_error_log",
            "get_core_logs",
            "set_log_level",
            "get_history",
            "get_statistics",
            "query_entities",
            "remove_entity",
            "update_entity",
            "get_entity_registry",
            "list_entity_registry",
        ]
        
        # Check that each tool function has a proper docstring and exists
        for tool_name in tool_functions:
            assert hasattr(app.server, tool_name), f"{tool_name} function missing"
            tool_function = getattr(app.server, tool_name)
            assert tool_function.__doc__ is not None, f"{tool_name} missing docstring"
            assert len(tool_function.__doc__.strip()) > 10, f"{tool_name} has insufficient docstring"
    
    @pytest.mark.asyncio
    async def test_search_entities_resource(self):
        """Test the search_entities function"""
        from app.server import search_entities
        
        # Mock the get_entities function with test data
        mock_entities = [
            {"entity_id": "light.living_room", "state": "on", "attributes": {"friendly_name": "Living Room Light", "brightness": 255}},
            {"entity_id": "light.kitchen", "state": "off", "attributes": {"friendly_name": "Kitchen Light"}}
        ]
        
        with patch("app.server.get_entities", return_value=mock_entities) as mock_get:
            # Test search with a valid query
            result = await search_entities(query="living")
            
            # Verify the function was called with the right parameters including lean format
            mock_get.assert_called_once_with(search_query="living", limit=20, lean=True)
            
            # Check that the result contains the expected entity data
            assert result["count"] == 2
            assert any(e["entity_id"] == "light.living_room" for e in result["results"])
            assert result["query"] == "living"
            
            # Check that domain counts are included
            assert "domains" in result
            assert "light" in result["domains"]
            
            # Test with empty query (returns all entities instead of error)
            result = await search_entities(query="")
            assert "error" not in result
            assert result["count"] > 0
            assert "all entities (no filtering)" in result["query"]
            
            # Test that simplified representation includes domain-specific attributes
            result = await search_entities(query="living")
            assert any("brightness" in e for e in result["results"])
            
            # Test with custom limit as an integer
            mock_get.reset_mock()
            result = await search_entities(query="light", limit=5)
            mock_get.assert_called_once_with(search_query="light", limit=5, lean=True)
            
            # Test with a different limit to ensure it's respected
            mock_get.reset_mock()
            result = await search_entities(query="light", limit=10)
            mock_get.assert_called_once_with(search_query="light", limit=10, lean=True)
            
    @pytest.mark.asyncio
    async def test_domain_summary(self):
        """Test the domain_summary function"""
        from app.server import domain_summary
        
        # Mock the summarize_domain function
        mock_summary = {
            "domain": "light",
            "total_count": 2,
            "state_distribution": {"on": 1, "off": 1},
            "examples": {
                "on": [{"entity_id": "light.living_room", "friendly_name": "Living Room Light"}],
                "off": [{"entity_id": "light.kitchen", "friendly_name": "Kitchen Light"}]
            },
            "common_attributes": [("friendly_name", 2), ("brightness", 1)]
        }
        
        with patch("app.server.summarize_domain", return_value=mock_summary) as mock_summarize:
            # Test the function
            result = await domain_summary(domain="light", example_limit=3)
            
            # Verify the function was called with the right parameters
            mock_summarize.assert_called_once_with("light", 3)
            
            # Check that the result matches the mock data
            assert result == mock_summary
            
    @pytest.mark.asyncio        
    async def test_get_entity_with_field_filtering(self):
        """Test the get_entity function with field filtering"""
        from app.server import get_entity
        
        # Mock entity data
        mock_entity = {
            "entity_id": "light.living_room",
            "state": "on",
            "attributes": {
                "friendly_name": "Living Room Light",
                "brightness": 255,
                "color_temp": 370
            }
        }
        
        # Mock filtered entity data
        mock_filtered = {
            "entity_id": "light.living_room",
            "state": "on"
        }
        
        # Set up mock for get_entity_state to handle different calls
        with patch("app.server.get_entity_state") as mock_get_state:
            # Configure mock to return different responses based on parameters
            mock_get_state.return_value = mock_filtered
            
            # Test with field filtering
            result = await get_entity(entity_id="light.living_room", fields=["state"])
            
            # Verify the function call with fields parameter
            mock_get_state.assert_called_with("light.living_room", fields=["state"])
            assert result == mock_filtered
            
            # Test with detailed=True
            mock_get_state.reset_mock()
            mock_get_state.return_value = mock_entity
            result = await get_entity(entity_id="light.living_room", detailed=True)
            
            # Verify the function call with detailed parameter
            mock_get_state.assert_called_with("light.living_room", lean=False)
            assert result == mock_entity
            
            # Test default lean mode
            mock_get_state.reset_mock()
            mock_get_state.return_value = mock_filtered
            result = await get_entity(entity_id="light.living_room")
            
            # Verify the function call with lean=True parameter
            mock_get_state.assert_called_with("light.living_room", lean=True)
            assert result == mock_filtered

    @pytest.mark.asyncio
    async def test_query_entities_domain_only(self):
        """Test query_entities with domain filter only (no expression)."""
        from app.server import query_entities

        mock_all_states = {
            "light.living_room": {"entity_id": "light.living_room", "state": "on", "attributes": {"friendly_name": "Living Room", "brightness": 255}},
            "sensor.temp": {"entity_id": "sensor.temp", "state": "72", "attributes": {"friendly_name": "Temperature"}},
            "light.kitchen": {"entity_id": "light.kitchen", "state": "off", "attributes": {"friendly_name": "Kitchen", "brightness": 0}},
        }

        with patch("app.server.get_all_entity_states", new_callable=AsyncMock, return_value=mock_all_states):
            result = await query_entities(domain="light")

            assert isinstance(result, dict)
            assert result["count"] == 2
            assert result["total_matched"] == 2
            assert result["truncated"] is False
            assert len(result["entities"]) == 2
            assert all("light." in e["entity_id"] for e in result["entities"])

    @pytest.mark.asyncio
    async def test_query_entities_with_cel_expression(self):
        """Test query_entities with CEL expression filtering."""
        from app.server import query_entities

        mock_all_states = {
            "light.living_room": {"entity_id": "light.living_room", "state": "on", "attributes": {"friendly_name": "Living Room", "brightness": 255}},
            "light.kitchen": {"entity_id": "light.kitchen", "state": "off", "attributes": {"friendly_name": "Kitchen", "brightness": 0}},
        }

        with patch("app.server.get_all_entity_states", new_callable=AsyncMock, return_value=mock_all_states):
            result = await query_entities(expression='state == "on"')

            assert result["count"] == 1
            assert result["entities"][0]["entity_id"] == "light.living_room"

    @pytest.mark.asyncio
    async def test_query_entities_numeric_comparison(self):
        """Test query_entities with numeric CEL expression."""
        from app.server import query_entities

        mock_all_states = {
            "sensor.battery_a": {"entity_id": "sensor.battery_a", "state": "25", "attributes": {"friendly_name": "Battery A", "device_class": "battery"}},
            "sensor.battery_b": {"entity_id": "sensor.battery_b", "state": "80", "attributes": {"friendly_name": "Battery B", "device_class": "battery"}},
            "sensor.battery_c": {"entity_id": "sensor.battery_c", "state": "10", "attributes": {"friendly_name": "Battery C", "device_class": "battery"}},
        }

        with patch("app.server.get_all_entity_states", new_callable=AsyncMock, return_value=mock_all_states):
            result = await query_entities(
                domain="sensor",
                expression="state < 30"
            )

            assert result["count"] == 2
            entity_ids = [e["entity_id"] for e in result["entities"]]
            assert "sensor.battery_a" in entity_ids
            assert "sensor.battery_c" in entity_ids

    @pytest.mark.asyncio
    async def test_query_entities_or_logic(self):
        """Test query_entities with CEL OR logic."""
        from app.server import query_entities

        mock_all_states = {
            "sensor.a": {"entity_id": "sensor.a", "state": "unavailable", "attributes": {"friendly_name": "A"}},
            "sensor.b": {"entity_id": "sensor.b", "state": "unknown", "attributes": {"friendly_name": "B"}},
            "sensor.c": {"entity_id": "sensor.c", "state": "42", "attributes": {"friendly_name": "C"}},
        }

        with patch("app.server.get_all_entity_states", new_callable=AsyncMock, return_value=mock_all_states):
            result = await query_entities(
                expression='state == "unavailable" || state == "unknown"'
            )

            assert result["count"] == 2
            entity_ids = [e["entity_id"] for e in result["entities"]]
            assert "sensor.a" in entity_ids
            assert "sensor.b" in entity_ids

    @pytest.mark.asyncio
    async def test_query_entities_with_limit(self):
        """Test query_entities respects limit and reports total_matched."""
        from app.server import query_entities

        mock_all_states = {
            f"sensor.test_{i}": {"entity_id": f"sensor.test_{i}", "state": str(i), "attributes": {"friendly_name": f"Test {i}"}}
            for i in range(100)
        }

        with patch("app.server.get_all_entity_states", new_callable=AsyncMock, return_value=mock_all_states):
            result = await query_entities(domain="sensor", limit=10)

            assert result["count"] == 10
            assert result["total_matched"] == 100
            assert result["truncated"] is True
            assert len(result["entities"]) == 10

    @pytest.mark.asyncio
    async def test_query_entities_lean_format(self):
        """Test lean output format includes domain-specific attributes."""
        from app.server import query_entities

        mock_all_states = {
            "light.test": {
                "entity_id": "light.test",
                "state": "on",
                "attributes": {
                    "friendly_name": "Test Light",
                    "brightness": 255,
                    "color_temp": 370,
                    "supported_features": 63,
                    "extra_attr": "should_not_appear"
                }
            }
        }

        with patch("app.server.get_all_entity_states", new_callable=AsyncMock, return_value=mock_all_states):
            result = await query_entities(domain="light", lean=True, compact=False)

            entity = result["entities"][0]
            assert entity["entity_id"] == "light.test"
            assert entity["state"] == "on"
            assert entity["friendly_name"] == "Test Light"
            assert entity["brightness"] == 255
            assert "extra_attr" not in entity

    @pytest.mark.asyncio
    async def test_query_entities_compact_format(self):
        """Test compact output returns only entity_id, state, friendly_name."""
        from app.server import query_entities

        mock_all_states = {
            "light.test": {
                "entity_id": "light.test",
                "state": "on",
                "attributes": {
                    "friendly_name": "Test Light",
                    "brightness": 255,
                    "color_temp": 370
                }
            }
        }

        with patch("app.server.get_all_entity_states", new_callable=AsyncMock, return_value=mock_all_states):
            result = await query_entities(domain="light", compact=True)

            entity = result["entities"][0]
            assert entity["entity_id"] == "light.test"
            assert entity["state"] == "on"
            assert entity["friendly_name"] == "Test Light"
            assert "brightness" not in entity
            assert "color_temp" not in entity

    @pytest.mark.asyncio
    async def test_query_entities_empty_result(self):
        """Test query_entities with no matches returns count=0, empty entities."""
        from app.server import query_entities

        mock_all_states = {
            "light.a": {"entity_id": "light.a", "state": "off", "attributes": {"friendly_name": "A"}},
        }

        with patch("app.server.get_all_entity_states", new_callable=AsyncMock, return_value=mock_all_states):
            result = await query_entities(expression='state == "on"')

            assert result["count"] == 0
            assert result["total_matched"] == 0
            assert result["truncated"] is False
            assert result["entities"] == []
            assert "error" not in result

    @pytest.mark.asyncio
    async def test_query_entities_invalid_expression(self):
        """Test query_entities with invalid CEL expression returns error."""
        from app.server import query_entities

        mock_all_states = {
            "light.a": {"entity_id": "light.a", "state": "on", "attributes": {}},
        }

        with patch("app.server.get_all_entity_states", new_callable=AsyncMock, return_value=mock_all_states):
            result = await query_entities(expression="invalid %%% expression")

            assert result["count"] == 0
            assert result["total_matched"] == 0
            assert result["entities"] == []
            assert "error" in result

    @pytest.mark.asyncio
    async def test_query_entities_api_error(self):
        """Test query_entities handles API failure gracefully."""
        from app.server import query_entities

        with patch("app.server.get_all_entity_states", new_callable=AsyncMock, return_value={"error": "Connection refused"}):
            result = await query_entities(domain="light")

            assert isinstance(result, dict)
            assert "error" in result
            assert result["count"] == 0
            assert result["entities"] == []

    @pytest.mark.asyncio
    async def test_query_entities_no_params(self):
        """Test query_entities with no domain and no expression returns all entities up to limit."""
        from app.server import query_entities

        mock_all_states = {
            "light.a": {"entity_id": "light.a", "state": "on", "attributes": {"friendly_name": "A"}},
            "sensor.b": {"entity_id": "sensor.b", "state": "42", "attributes": {"friendly_name": "B"}},
        }

        with patch("app.server.get_all_entity_states", new_callable=AsyncMock, return_value=mock_all_states):
            result = await query_entities()

            assert result["count"] == 2
            assert result["total_matched"] == 2
            assert result["truncated"] is False
            assert len(result["entities"]) == 2

    @pytest.mark.asyncio
    async def test_update_entity_enable_via_none_string(self):
        """Test that update_entity converts 'none' string to None for enable/unhide."""
        from app.server import update_entity

        mock_result = {"success": True, "entity_id": "light.test", "result": {}}
        with patch("app.server.update_registry_entity", new_callable=AsyncMock, return_value=mock_result) as mock_fn:
            # Test enabling a disabled entity
            result = await update_entity(entity_id="light.test", disabled_by="none")
            assert result["success"] is True
            mock_fn.assert_called_once_with("light.test", disabled_by=None)

            # Test unhiding an entity
            mock_fn.reset_mock()
            result = await update_entity(entity_id="light.test", hidden_by="none")
            mock_fn.assert_called_once_with("light.test", hidden_by=None)

            # Test clearing a custom name
            mock_fn.reset_mock()
            result = await update_entity(entity_id="light.test", name="none")
            mock_fn.assert_called_once_with("light.test", name=None)

    @pytest.mark.asyncio
    async def test_remove_entity_preview_and_confirm(self):
        """Test that remove_entity passes confirm parameter through correctly."""
        from app.server import remove_entity

        mock_preview = {
            "action": "preview",
            "entity_id": "light.test",
            "entity_details": {"entity_id": "light.test", "platform": "mqtt"},
            "warning": "This will permanently remove...",
            "suggestion": "Consider using update_entity...",
            "confirm_required": True,
        }
        mock_confirmed = {
            "success": True,
            "entity_id": "light.test",
            "removed_entity": {"entity_id": "light.test", "platform": "mqtt"},
        }

        with patch("app.server.remove_registry_entity", new_callable=AsyncMock) as mock_fn:
            # Default (preview)
            mock_fn.return_value = mock_preview
            result = await remove_entity(entity_id="light.test")
            assert result["action"] == "preview"
            assert result["confirm_required"] is True
            mock_fn.assert_called_once_with("light.test", confirm=False)

            # Confirmed deletion
            mock_fn.reset_mock()
            mock_fn.return_value = mock_confirmed
            result = await remove_entity(entity_id="light.test", confirm=True)
            assert result["success"] is True
            assert "removed_entity" in result
            mock_fn.assert_called_once_with("light.test", confirm=True)

    @pytest.mark.asyncio
    async def test_query_entities_domain_and_expression(self):
        """Test query_entities with both domain and expression combined."""
        from app.server import query_entities

        mock_all_states = {
            "light.a": {"entity_id": "light.a", "state": "on", "attributes": {"friendly_name": "A"}},
            "light.b": {"entity_id": "light.b", "state": "off", "attributes": {"friendly_name": "B"}},
            "sensor.c": {"entity_id": "sensor.c", "state": "on", "attributes": {"friendly_name": "C"}},
        }

        with patch("app.server.get_all_entity_states", new_callable=AsyncMock, return_value=mock_all_states):
            result = await query_entities(
                domain="light",
                expression='state == "on"'
            )

            assert result["count"] == 1
            assert result["entities"][0]["entity_id"] == "light.a"

    # --- Unified get_history tests ---

    @pytest.mark.asyncio
    async def test_get_history_hours_mode(self):
        """Test get_history with hours param (default mode, no start_time)."""
        from app.server import get_history

        mock_result = {
            "states": [
                {"state": "on", "last_changed": "2025-01-01T00:00:00Z"},
                {"state": "off", "last_changed": "2025-01-01T01:00:00Z"},
            ],
            "count": 2,
            "total_available": 2,
            "truncated": False,
            "sample_strategy": "recent",
        }

        with patch("app.server.get_entity_history", new_callable=AsyncMock, return_value=mock_result) as mock_fn:
            result = await get_history(entity_id="sensor.temp", hours=12, limit=50)

            mock_fn.assert_called_once_with("sensor.temp", 12, 50, "recent")
            assert result["count"] == 2
            assert result["first_changed"] == "2025-01-01T00:00:00Z"
            assert result["last_changed"] == "2025-01-01T01:00:00Z"

    @pytest.mark.asyncio
    async def test_get_history_range_mode(self):
        """Test get_history with start_time triggers range mode."""
        from app.server import get_history

        mock_result = {
            "states": [
                {"state": "22.5", "last_changed": "2025-01-01T10:00:00Z"},
            ],
            "count": 1,
            "total_available": 1,
            "truncated": False,
            "sample_strategy": "recent",
            "start_time": "2025-01-01T10:00:00Z",
            "end_time": "2025-01-01T11:00:00Z",
        }

        with patch("app.server.get_entity_history_range", new_callable=AsyncMock, return_value=mock_result) as mock_fn:
            result = await get_history(
                entity_id="sensor.temp",
                start_time="2025-01-01T10:00:00Z",
                end_time="2025-01-01T11:00:00Z",
                limit=50,
                minimal_response=False,
            )

            mock_fn.assert_called_once_with(
                "sensor.temp", "2025-01-01T10:00:00Z", "2025-01-01T11:00:00Z", False, 50, "recent"
            )
            assert result["count"] == 1
            assert result["start_time"] == "2025-01-01T10:00:00Z"
            assert result["end_time"] == "2025-01-01T11:00:00Z"

    @pytest.mark.asyncio
    async def test_get_history_range_mode_no_end_time(self):
        """Test get_history with start_time but no end_time (defaults to None -> now)."""
        from app.server import get_history

        mock_result = {
            "states": [{"state": "on", "last_changed": "2025-01-01T00:00:00Z"}],
            "count": 1,
            "total_available": 1,
            "truncated": False,
            "sample_strategy": "recent",
            "start_time": "yesterday",
            "end_time": "now",
        }

        with patch("app.server.get_entity_history_range", new_callable=AsyncMock, return_value=mock_result) as mock_fn:
            result = await get_history(entity_id="light.test", start_time="yesterday")

            mock_fn.assert_called_once_with(
                "light.test", "yesterday", None, True, 100, "recent"
            )
            assert result["count"] == 1

    @pytest.mark.asyncio
    async def test_get_history_range_mode_invalid_date(self):
        """Test get_history with invalid start_time returns date error."""
        from app.server import get_history

        with patch("app.server.get_entity_history_range", new_callable=AsyncMock, side_effect=ValueError("Invalid date")):
            result = await get_history(entity_id="sensor.temp", start_time="not-a-date")

            assert "error" in result
            assert "date" in result["error"].lower() or "time" in result["error"].lower()
            assert result["states"] == []

    # --- Unified get_statistics tests ---

    @pytest.mark.asyncio
    async def test_get_statistics_hours_mode(self):
        """Test get_statistics with hours param (default mode)."""
        from app.server import get_statistics

        mock_result = {
            "statistics": [
                {"start": "2025-01-01T00:00:00Z", "mean": 22.5, "min": 20.0, "max": 25.0},
            ],
        }

        with patch("app.server.get_entity_statistics", new_callable=AsyncMock, return_value=mock_result) as mock_fn:
            result = await get_statistics(entity_id="sensor.temp", hours=48, period="day")

            mock_fn.assert_called_once_with("sensor.temp", 48, "day")
            assert result["count"] == 1
            assert result["hours_requested"] == 48
            assert result["period"] == "day"

    @pytest.mark.asyncio
    async def test_get_statistics_range_mode(self):
        """Test get_statistics with start_time triggers range mode."""
        from app.server import get_statistics

        mock_result = {
            "statistics": [
                {"start": "2024-10-01T00:00:00Z", "mean": 21.0, "min": 18.0, "max": 24.0},
                {"start": "2024-10-02T00:00:00Z", "mean": 22.0, "min": 19.0, "max": 25.0},
            ],
            "start_time": "2024-10-01",
            "end_time": "2024-10-31",
        }

        with patch("app.server.get_entity_statistics_range", new_callable=AsyncMock, return_value=mock_result) as mock_fn:
            result = await get_statistics(
                entity_id="sensor.temp",
                start_time="2024-10-01",
                end_time="2024-10-31",
                period="day",
            )

            mock_fn.assert_called_once_with("sensor.temp", "2024-10-01", "2024-10-31", "day")
            assert result["count"] == 2
            assert result["start_time"] == "2024-10-01"
            assert result["end_time"] == "2024-10-31"
            assert result["period"] == "day"

    @pytest.mark.asyncio
    async def test_get_statistics_range_mode_no_end_time(self):
        """Test get_statistics with start_time but no end_time."""
        from app.server import get_statistics

        mock_result = {
            "statistics": [{"start": "2025-01-01T00:00:00Z", "mean": 50.0}],
            "start_time": "yesterday",
            "end_time": "now",
        }

        with patch("app.server.get_entity_statistics_range", new_callable=AsyncMock, return_value=mock_result) as mock_fn:
            result = await get_statistics(entity_id="sensor.humidity", start_time="yesterday", period="5minute")

            mock_fn.assert_called_once_with("sensor.humidity", "yesterday", None, "5minute")
            assert result["count"] == 1

    @pytest.mark.asyncio
    async def test_get_statistics_range_mode_invalid_date(self):
        """Test get_statistics with invalid start_time returns date error."""
        from app.server import get_statistics

        with patch("app.server.get_entity_statistics_range", new_callable=AsyncMock, side_effect=ValueError("Invalid date")):
            result = await get_statistics(entity_id="sensor.temp", start_time="garbage-date")

            assert "error" in result
            assert "date" in result["error"].lower() or "time" in result["error"].lower()
            assert result["statistics"] == []