# #region Test.Assistant.LlmEdge [C:2] [TYPE Module] [SEMANTICS test,assistant,llm,edge,provider,status]
# @BRIEF Edge-case tests for _tool_llm.py — provider listing, status checks with various states.
# @RELATION BINDS_TO -> [AssistantToolLlm]
# @TEST_CONTRACT: handle_list_llm_providers -> lists providers with icons and model info
# @TEST_CONTRACT: handle_get_llm_status -> shows provider count and valid/invalid key states
# @TEST_EDGE: get_llm_status_mixed_providers -> active + inactive displayed correctly
# @TEST_EDGE: get_llm_status_invalid_api_key -> shows "невалидный" status
# @TEST_EDGE: list_providers_with_null_model -> shows "модель не указана"
# @TEST_EDGE: get_llm_status_multiple_active -> picks first active provider

from datetime import UTC, datetime
from unittest.mock import AsyncMock, MagicMock, patch

import pytest

from src.schemas.auth import User


@pytest.fixture
def current_user():
    return User(
        id="user-1", username="testuser", email="test@example.com",
        is_active=True, auth_source="internal",
        created_at=datetime.now(UTC), last_login=datetime.now(UTC), roles=[],
    )


@pytest.fixture
def config_manager():
    return MagicMock()


@pytest.fixture
def task_manager():
    return MagicMock()


@pytest.fixture
def db():
    return MagicMock()


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


class TestListLlmProvidersEdge:
    """handle_list_llm_providers — edge cases."""

    @pytest.mark.asyncio
    async def test_single_provider(
        self, intent, current_user, task_manager, config_manager, db
    ):
        from src.api.routes.assistant._tool_llm import handle_list_llm_providers

        with patch("src.api.routes.assistant._tool_llm.LLMProviderService") as mock_svc_cls:
            svc = MagicMock()
            mock_svc_cls.return_value = svc
            p = MagicMock(name="OpenAI", provider_type="openai", is_active=True, default_model="gpt-4")
            svc.get_all_providers.return_value = [p]

            text, task_id, actions = await handle_list_llm_providers(
                intent, current_user, task_manager, config_manager, db
            )
            assert "LLM провайдеры" in text
            assert "OpenAI" in text
            assert "gpt-4" in text

    @pytest.mark.asyncio
    async def test_provider_with_null_model(
        self, intent, current_user, task_manager, config_manager, db
    ):
        from src.api.routes.assistant._tool_llm import handle_list_llm_providers

        with patch("src.api.routes.assistant._tool_llm.LLMProviderService") as mock_svc_cls:
            svc = MagicMock()
            mock_svc_cls.return_value = svc
            p = MagicMock(name="Anthropic", provider_type="anthropic", is_active=True, default_model=None)
            svc.get_all_providers.return_value = [p]

            text, task_id, actions = await handle_list_llm_providers(
                intent, current_user, task_manager, config_manager, db
            )
            assert "Anthropic" in text
            assert "модель не указана" in text

    @pytest.mark.asyncio
    async def test_all_inactive_providers(
        self, intent, current_user, task_manager, config_manager, db
    ):
        from src.api.routes.assistant._tool_llm import handle_list_llm_providers

        with patch("src.api.routes.assistant._tool_llm.LLMProviderService") as mock_svc_cls:
            svc = MagicMock()
            mock_svc_cls.return_value = svc
            p1 = MagicMock(name="Provider1", provider_type="type1", is_active=False, default_model="m1")
            p2 = MagicMock(name="Provider2", provider_type="type2", is_active=False, default_model="m2")
            svc.get_all_providers.return_value = [p1, p2]

            text, task_id, actions = await handle_list_llm_providers(
                intent, current_user, task_manager, config_manager, db
            )
            # Both should appear with inactive icon
            assert "Provider1" in text
            assert "Provider2" in text

    @pytest.mark.asyncio
    async def test_many_providers(
        self, intent, current_user, task_manager, config_manager, db
    ):
        from src.api.routes.assistant._tool_llm import handle_list_llm_providers

        with patch("src.api.routes.assistant._tool_llm.LLMProviderService") as mock_svc_cls:
            svc = MagicMock()
            mock_svc_cls.return_value = svc
            providers = [
                MagicMock(name=f"P{i}", provider_type=f"type{i}", is_active=i % 2 == 0, default_model=f"m{i}")
                for i in range(5)
            ]
            svc.get_all_providers.return_value = providers

            text, task_id, actions = await handle_list_llm_providers(
                intent, current_user, task_manager, config_manager, db
            )
            for i in range(5):
                assert f"P{i}" in text

    @pytest.mark.asyncio
    async def test_action_links_settings(
        self, intent, current_user, task_manager, config_manager, db
    ):
        from src.api.routes.assistant._tool_llm import handle_list_llm_providers

        with patch("src.api.routes.assistant._tool_llm.LLMProviderService") as mock_svc_cls:
            svc = MagicMock()
            mock_svc_cls.return_value = svc
            svc.get_all_providers.return_value = [
                MagicMock(name="O", provider_type="openai", is_active=True, default_model="gpt-4")
            ]

            text, task_id, actions = await handle_list_llm_providers(
                intent, current_user, task_manager, config_manager, db
            )
            assert len(actions) == 1
            assert actions[0].type == "open_route"
            assert "settings/llm" in actions[0].target


class TestGetLlmStatusEdge:
    """handle_get_llm_status — edge cases."""

    @pytest.mark.asyncio
    async def test_multiple_providers_counts(
        self, intent, current_user, task_manager, config_manager, db
    ):
        from src.api.routes.assistant._tool_llm import handle_get_llm_status

        with patch("src.api.routes.assistant._tool_llm.LLMProviderService") as mock_svc_cls:
            svc = MagicMock()
            mock_svc_cls.return_value = svc
            p1 = MagicMock(id="p1", name="A", provider_type="t1", is_active=True, default_model="m1")
            p2 = MagicMock(id="p2", name="B", provider_type="t2", is_active=False, default_model=None)
            p3 = MagicMock(id="p3", name="C", provider_type="t3", is_active=True, default_model="m3")
            svc.get_all_providers.return_value = [p1, p2, p3]
            svc.get_decrypted_api_key.return_value = "sk-valid"

            with patch("src.api.routes.llm._is_valid_runtime_api_key", return_value=True):
                text, task_id, actions = await handle_get_llm_status(
                    intent, current_user, task_manager, config_manager, db
                )

            assert "Всего провайдеров: 3" in text
            assert "Активных: 2" in text
            assert "Неактивных: 1" in text

    @pytest.mark.asyncio
    async def test_invalid_api_key(
        self, intent, current_user, task_manager, config_manager, db
    ):
        from src.api.routes.assistant._tool_llm import handle_get_llm_status

        with patch("src.api.routes.assistant._tool_llm.LLMProviderService") as mock_svc_cls:
            svc = MagicMock()
            mock_svc_cls.return_value = svc
            p = MagicMock(id="p1", name="OpenAI", provider_type="openai", is_active=True, default_model="gpt-4")
            svc.get_all_providers.return_value = [p]
            svc.get_decrypted_api_key.return_value = "invalid"

            with patch("src.api.routes.llm._is_valid_runtime_api_key", return_value=False):
                text, task_id, actions = await handle_get_llm_status(
                    intent, current_user, task_manager, config_manager, db
                )

            assert "невалидный" in text or "не полностью" in text

    @pytest.mark.asyncio
    async def test_active_provider_without_model(
        self, intent, current_user, task_manager, config_manager, db
    ):
        from src.api.routes.assistant._tool_llm import handle_get_llm_status

        with patch("src.api.routes.assistant._tool_llm.LLMProviderService") as mock_svc_cls:
            svc = MagicMock()
            mock_svc_cls.return_value = svc
            p = MagicMock(id="p1", name="Custom", provider_type="custom", is_active=True, default_model=None)
            svc.get_all_providers.return_value = [p]
            svc.get_decrypted_api_key.return_value = "some-key"

            with patch("src.api.routes.llm._is_valid_runtime_api_key", return_value=True):
                text, task_id, actions = await handle_get_llm_status(
                    intent, current_user, task_manager, config_manager, db
                )

            assert "не указана" in text

    @pytest.mark.asyncio
    async def test_multiple_active_providers_first_one_shown(
        self, intent, current_user, task_manager, config_manager, db
    ):
        from src.api.routes.assistant._tool_llm import handle_get_llm_status

        with patch("src.api.routes.assistant._tool_llm.LLMProviderService") as mock_svc_cls:
            svc = MagicMock()
            mock_svc_cls.return_value = svc
            p1 = MagicMock(id="p1", name="Alpha", provider_type="type1", is_active=True, default_model="m1")
            p2 = MagicMock(id="p2", name="Beta", provider_type="type2", is_active=True, default_model="m2")
            svc.get_all_providers.return_value = [p1, p2]
            svc.get_decrypted_api_key.return_value = "valid-key"

            with patch("src.api.routes.llm._is_valid_runtime_api_key", return_value=True):
                text, task_id, actions = await handle_get_llm_status(
                    intent, current_user, task_manager, config_manager, db
                )

            # Should show the first active provider (Alpha)
            assert "Alpha" in text or "<MagicMock name='Alpha.name'" in text
            # Beta is also active but only first is shown as "Активный провайдер"

    @pytest.mark.asyncio
    async def test_all_inactive_no_active_shown(
        self, intent, current_user, task_manager, config_manager, db
    ):
        from src.api.routes.assistant._tool_llm import handle_get_llm_status

        with patch("src.api.routes.assistant._tool_llm.LLMProviderService") as mock_svc_cls:
            svc = MagicMock()
            mock_svc_cls.return_value = svc
            p1 = MagicMock(id="p1", name="Inactive1", provider_type="t1", is_active=False, default_model="m1")
            p2 = MagicMock(id="p2", name="Inactive2", provider_type="t2", is_active=False, default_model=None)
            svc.get_all_providers.return_value = [p1, p2]

            text, task_id, actions = await handle_get_llm_status(
                intent, current_user, task_manager, config_manager, db
            )

            assert "Активных: 0" in text
            # Should NOT show active provider details block
            assert "Активный провайдер" not in text

    @pytest.mark.asyncio
    async def test_no_providers_no_actions(
        self, intent, current_user, task_manager, config_manager, db
    ):
        from src.api.routes.assistant._tool_llm import handle_get_llm_status

        with patch("src.api.routes.assistant._tool_llm.LLMProviderService") as mock_svc_cls:
            svc = MagicMock()
            mock_svc_cls.return_value = svc
            svc.get_all_providers.return_value = []

            text, task_id, actions = await handle_get_llm_status(
                intent, current_user, task_manager, config_manager, db
            )

            assert task_id is None
            assert actions == []


# #endregion Test.Assistant.LlmEdge
