# #region Test.Assistant.ToolMaintenance [C:2] [TYPE Module] [SEMANTICS test,assistant,maintenance,events]
# @BRIEF Tests for _tool_maintenance.py — maintenance handlers.
# @RELATION BINDS_TO -> [AssistantToolMaintenance]
# @TEST_EDGE: list_maintenance_events -> Lists active and completed events
# @TEST_EDGE: list_maintenance_no_events -> Empty maintenance response
# @TEST_EDGE: start_maintenance -> Start with tables and start_time
# @TEST_EDGE: start_maintenance_missing_params -> 422 on missing params
# @TEST_EDGE: start_maintenance_invalid_time -> 422 on bad datetime
# @TEST_EDGE: start_maintenance_too_many_tables -> 422 on >100 tables
# @TEST_EDGE: end_maintenance_by_id -> End specific event
# @TEST_EDGE: end_maintenance_not_found -> 404 on missing event
# @TEST_EDGE: end_maintenance_all_active -> End all active events

from pathlib import Path
import sys

sys.path.insert(0, str(Path(__file__).parent.parent / "src"))

from datetime import UTC, datetime, timedelta
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from fastapi import HTTPException


def _make_maintenance_event(event_id="evt-1", status="ACTIVE", tables=None, environment_id="env-1"):
    ev = MagicMock()
    ev.id = event_id
    ev.status = status
    ev.tables = tables or ["users"]
    ev.environment_id = environment_id
    ev.created_at = datetime.now(UTC)
    ev.end_time = None
    return ev


class TestHandleListMaintenanceEvents:
    """handle_list_maintenance_events — listing events."""

    @pytest.fixture(autouse=True)
    def _patch_permissions(self):
        with patch("src.api.routes.assistant._tool_maintenance._check_any_permission", return_value=None):
            yield

    @pytest.fixture
    def intent(self):
        return {"entities": {}}

    @pytest.fixture
    def current_user(self):
        from src.schemas.auth import User
        return User(id="user-1", username="test", email="test@x.com", is_active=True,
                    auth_source="internal", created_at=datetime.now(UTC), last_login=datetime.now(UTC), roles=[])

    @pytest.mark.asyncio
    async def test_lists_active_and_completed(self, intent, current_user):
        from src.api.routes.assistant._tool_maintenance import handle_list_maintenance_events
        from src.models.maintenance import MaintenanceEventStatus

        task_manager = MagicMock()
        task_manager.create_task = AsyncMock(return_value=MagicMock(id="task-99"))
        config_manager = MagicMock()
        db = MagicMock()

        active_events = [_make_maintenance_event("evt-1", MaintenanceEventStatus.ACTIVE)]
        completed_events = [_make_maintenance_event("evt-2", MaintenanceEventStatus.COMPLETED)]

        # Mock the queries
        db.query.return_value.filter.return_value = MagicMock()
        db.query.return_value.filter.return_value.filter.return_value.all.return_value = []

        # Separate mocks for each query
        def query_side_effect(model):
            q = MagicMock()
            if model.__name__ == "MaintenanceEvent":
                # Active events
                q.filter.return_value = MagicMock()
                q.filter.return_value.filter.return_value.all.side_effect = [
                    [],  # expired
                    active_events,  # active
                ]
                q.filter.return_value.filter.return_value.order_by.return_value.limit.return_value.all.return_value = completed_events
                q.filter.return_value.filter.return_value.order_by.return_value.all.return_value = active_events
                q.filter.return_value.in_.return_value = q.filter.return_value
                q.filter.return_value.isnot.return_value = q.filter.return_value
            return q

        db.query.side_effect = query_side_effect

        # Mock dashboard state count
        db.query.return_value.filter.return_value.filter.return_value = MagicMock()
        db.query.return_value.filter.return_value.filter.return_value.count.return_value = 3

        text, task_id, actions = await handle_list_maintenance_events(
            intent, current_user, task_manager, config_manager, db
        )
        assert "Активные" in text or "Завершённые" in text
        assert actions is not None

    @pytest.mark.asyncio
    async def test_no_events(self, intent, current_user):
        from src.api.routes.assistant._tool_maintenance import handle_list_maintenance_events

        task_manager = MagicMock()
        task_manager.create_task = AsyncMock(return_value=MagicMock(id="task-99"))
        config_manager = MagicMock()
        db = MagicMock()

        def query_side_effect(model):
            q = MagicMock()
            if model.__name__ == "MaintenanceEvent":
                # Expired: query().filter(a, b, c).all()
                q.filter.return_value.all.return_value = []
                # Active: query().filter(...).order_by().all()
                q.filter.return_value.order_by.return_value.all.return_value = []
                # Completed: query().filter(...).order_by().limit().all()
                q.filter.return_value.order_by.return_value.limit.return_value.all.return_value = []
            else:
                # MaintenanceDashboardState: query().filter(...).count() — won't be reached
                q.filter.return_value.count.return_value = 0
            return q

        db.query.side_effect = query_side_effect

        text, task_id, actions = await handle_list_maintenance_events(
            intent, current_user, task_manager, config_manager, db
        )
        assert "Нет событий" in text

    @pytest.mark.asyncio
    async def test_with_expired_events(self, intent, current_user):
        """Expired events trigger end-task creation."""
        from src.api.routes.assistant._tool_maintenance import handle_list_maintenance_events

        task_manager = MagicMock()
        task_manager.create_task = AsyncMock(return_value=MagicMock(id="task-exp-1"))
        config_manager = MagicMock()
        db = MagicMock()

        expired_events = [
            _make_maintenance_event("evt-exp-1", "ACTIVE"),
            _make_maintenance_event("evt-exp-2", "PARTIAL"),
        ]
        active_events = [_make_maintenance_event("evt-act-1", "ACTIVE")]
        completed_events = [_make_maintenance_event("evt-cmp-1", "COMPLETED")]

        class CountMock:
            """Mock that returns a specific count value."""
            def __init__(self, val):
                self._val = val
            def count(self):
                return self._val

        def query_side_effect(model):
            q = MagicMock()
            if model.__name__ == "MaintenanceEvent":
                # First .filter(...).all() → expired events
                # Then .filter(...).order_by().all() → active
                # Then .filter(...).order_by().limit().all() → completed
                f1 = MagicMock()
                f1.all.side_effect = [expired_events, active_events, completed_events]
                q.filter.return_value = f1
                # For the order_by path used in active/completed:
                f1.order_by.return_value.all.return_value = active_events
                f1.order_by.return_value.limit.return_value.all.return_value = completed_events
            elif model.__name__ == "MaintenanceDashboardState":
                q.filter.return_value.filter.return_value.count.return_value = 2
            return q

        db.query.side_effect = query_side_effect

        text, task_id, actions = await handle_list_maintenance_events(
            intent, current_user, task_manager, config_manager, db
        )
        assert "Активные" in text
        assert "Завершённые" in text
        # Should have created tasks for expired events
        assert task_manager.create_task.call_count >= 2


class TestHandleStartMaintenance:
    """handle_start_maintenance — starting maintenance events."""

    @pytest.fixture(autouse=True)
    def _patch_permissions(self):
        with patch("src.api.routes.assistant._tool_maintenance._check_any_permission", return_value=None):
            yield

    @pytest.fixture
    def intent(self):
        return {"entities": {}}

    @pytest.fixture
    def current_user(self):
        from src.schemas.auth import User
        return User(id="user-1", username="test", email="test@x.com", is_active=True,
                    auth_source="internal", created_at=datetime.now(UTC), last_login=datetime.now(UTC), roles=[])

    @pytest.mark.asyncio
    async def test_start_success(self, intent, current_user):
        from src.api.routes.assistant._tool_maintenance import handle_start_maintenance

        task_manager = MagicMock()
        task_manager.create_task = AsyncMock(return_value=MagicMock(id="task-42"))
        config_manager = MagicMock()
        db = MagicMock()

        intent["entities"] = {
            "tables": ["users", "orders"],
            "start_time": "2025-06-15T10:00",
            "end_time": "2025-06-15T12:00",
            "message": "Scheduled maintenance",
        }

        text, task_id, actions = await handle_start_maintenance(
            intent, current_user, task_manager, config_manager, db
        )
        assert "Обслуживание запущено" in text
        assert task_id == "task-42"

    @pytest.mark.asyncio
    async def test_start_without_end_time(self, intent, current_user):
        from src.api.routes.assistant._tool_maintenance import handle_start_maintenance

        task_manager = MagicMock()
        task_manager.create_task = AsyncMock(return_value=MagicMock(id="task-43"))
        config_manager = MagicMock()
        db = MagicMock()

        intent["entities"] = {
            "tables": ["users"],
            "start_time": "2025-06-15T10:00",
        }

        text, task_id, actions = await handle_start_maintenance(
            intent, current_user, task_manager, config_manager, db
        )
        assert "Обслуживание запущено" in text

    @pytest.mark.asyncio
    async def test_start_missing_params_raises_422(self, intent, current_user):
        from src.api.routes.assistant._tool_maintenance import handle_start_maintenance

        task_manager = MagicMock()
        config_manager = MagicMock()
        db = MagicMock()

        intent["entities"] = {}

        with pytest.raises(HTTPException) as exc:
            await handle_start_maintenance(
                intent, current_user, task_manager, config_manager, db
            )
        assert exc.value.status_code == 422

    @pytest.mark.asyncio
    async def test_start_missing_tables_raises_422(self, intent, current_user):
        from src.api.routes.assistant._tool_maintenance import handle_start_maintenance

        task_manager = MagicMock()
        config_manager = MagicMock()
        db = MagicMock()

        intent["entities"] = {"start_time": "2025-06-15T10:00"}

        with pytest.raises(HTTPException) as exc:
            await handle_start_maintenance(
                intent, current_user, task_manager, config_manager, db
            )
        assert exc.value.status_code == 422

    @pytest.mark.asyncio
    async def test_start_invalid_time_raises_422(self, intent, current_user):
        from src.api.routes.assistant._tool_maintenance import handle_start_maintenance

        task_manager = MagicMock()
        config_manager = MagicMock()
        db = MagicMock()

        intent["entities"] = {
            "tables": ["users"],
            "start_time": "not-a-date",
        }

        with pytest.raises(HTTPException) as exc:
            await handle_start_maintenance(
                intent, current_user, task_manager, config_manager, db
            )
        assert exc.value.status_code == 422
        assert "start_time" in exc.value.detail.lower()

    @pytest.mark.asyncio
    async def test_start_invalid_end_time_raises_422(self, intent, current_user):
        from src.api.routes.assistant._tool_maintenance import handle_start_maintenance

        task_manager = MagicMock()
        config_manager = MagicMock()
        db = MagicMock()

        intent["entities"] = {
            "tables": ["users"],
            "start_time": "2025-06-15T10:00",
            "end_time": "not-a-date",
        }

        with pytest.raises(HTTPException) as exc:
            await handle_start_maintenance(
                intent, current_user, task_manager, config_manager, db
            )
        assert exc.value.status_code == 422
        assert "end_time" in exc.value.detail.lower()

    @pytest.mark.asyncio
    async def test_start_too_many_tables_raises_422(self, intent, current_user):
        from src.api.routes.assistant._tool_maintenance import handle_start_maintenance

        task_manager = MagicMock()
        config_manager = MagicMock()
        db = MagicMock()

        intent["entities"] = {
            "tables": [f"table_{i}" for i in range(101)],
            "start_time": "2025-06-15T10:00",
        }

        with pytest.raises(HTTPException) as exc:
            await handle_start_maintenance(
                intent, current_user, task_manager, config_manager, db
            )
        assert exc.value.status_code == 422
        assert "100" in exc.value.detail

    @pytest.mark.asyncio
    async def test_start_with_tables_as_string(self, intent, current_user):
        from src.api.routes.assistant._tool_maintenance import handle_start_maintenance

        task_manager = MagicMock()
        task_manager.create_task = AsyncMock(return_value=MagicMock(id="task-44"))
        config_manager = MagicMock()
        db = MagicMock()

        intent["entities"] = {
            "tables": "users,orders,products",
            "start_time": "2025-06-15T10:00",
        }

        text, task_id, actions = await handle_start_maintenance(
            intent, current_user, task_manager, config_manager, db
        )
        assert "Обслуживание запущено" in text
        # Should have split the string
        task_params = task_manager.create_task.call_args[1]["params"]
        assert task_params["tables"] == ["users", "orders", "products"]


class TestHandleEndMaintenance:
    """handle_end_maintenance — ending maintenance events."""

    @pytest.fixture(autouse=True)
    def _patch_permissions(self):
        with patch("src.api.routes.assistant._tool_maintenance._check_any_permission", return_value=None):
            yield

    @pytest.fixture
    def intent(self):
        return {"entities": {}}

    @pytest.fixture
    def current_user(self):
        from src.schemas.auth import User
        return User(id="user-1", username="test", email="test@x.com", is_active=True,
                    auth_source="internal", created_at=datetime.now(UTC), last_login=datetime.now(UTC), roles=[])

    @pytest.mark.asyncio
    async def test_end_by_id(self, intent, current_user):
        from src.api.routes.assistant._tool_maintenance import handle_end_maintenance

        task_manager = MagicMock()
        task_manager.create_task = AsyncMock(return_value=MagicMock(id="task-45"))
        config_manager = MagicMock()
        db = MagicMock()

        event = _make_maintenance_event("evt-1")
        db.query.return_value.filter.return_value.first.return_value = event

        intent["entities"] = {"event_id": "evt-1"}

        text, task_id, actions = await handle_end_maintenance(
            intent, current_user, task_manager, config_manager, db
        )
        assert "Завершение" in text
        assert task_id == "task-45"

    @pytest.mark.asyncio
    async def test_end_not_found_raises_404(self, intent, current_user):
        from src.api.routes.assistant._tool_maintenance import handle_end_maintenance

        task_manager = MagicMock()
        config_manager = MagicMock()
        db = MagicMock()

        db.query.return_value.filter.return_value.first.return_value = None

        intent["entities"] = {"event_id": "evt-nonexistent"}

        with pytest.raises(HTTPException) as exc:
            await handle_end_maintenance(
                intent, current_user, task_manager, config_manager, db
            )
        assert exc.value.status_code == 404

    @pytest.mark.asyncio
    async def test_end_all_active_success(self, intent, current_user):
        from src.api.routes.assistant._tool_maintenance import handle_end_maintenance
        from src.models.maintenance import MaintenanceEventStatus

        task_manager = MagicMock()
        task_manager.create_task = AsyncMock(return_value=MagicMock(id="task-46"))
        config_manager = MagicMock()
        db = MagicMock()

        active_events = [
            _make_maintenance_event("evt-1", MaintenanceEventStatus.ACTIVE),
            _make_maintenance_event("evt-2", MaintenanceEventStatus.PARTIAL),
        ]
        # Code: db.query(MaintenanceEvent).filter(...).all()
        db.query.return_value.filter.return_value.all.return_value = active_events

        intent["entities"] = {}  # No event_id -> end all

        text, task_id, actions = await handle_end_maintenance(
            intent, current_user, task_manager, config_manager, db
        )
        assert "Завершение" in text
        assert "2" in text  # Should mention 2 events

    @pytest.mark.asyncio
    async def test_end_all_no_active(self, intent, current_user):
        from src.api.routes.assistant._tool_maintenance import handle_end_maintenance

        task_manager = MagicMock()
        config_manager = MagicMock()
        db = MagicMock()

        # Code: db.query(MaintenanceEvent).filter(...).all()
        db.query.return_value.filter.return_value.all.return_value = []

        intent["entities"] = {}  # No event_id

        text, task_id, actions = await handle_end_maintenance(
            intent, current_user, task_manager, config_manager, db
        )
        assert "Нет активных" in text
        assert task_id is None
# #endregion Test.Assistant.ToolMaintenance
