"""
Tests for providers:
- SecretsStatusProvider
- SecretsInfoProvider
- OnboardingSettingsProvider
- MissingSecretsProvider
"""

import pytest
from unittest.mock import AsyncMock, MagicMock

from elizaos_plugin_secrets_manager.types import (
    SecretContext,
    SecretLevel,
    SecretConfig,
    SecretStatus,
    SecretType,
)


def make_mock_runtime(agent_id="test-agent"):
    runtime = MagicMock()
    runtime.agent_id = agent_id
    runtime.character = MagicMock()
    runtime.character.name = "TestBot"
    runtime.get_setting = MagicMock(return_value=None)
    return runtime


def make_mock_message(room_id="room-1", text="", channel_type=""):
    msg = MagicMock()
    msg.room_id = room_id
    msg.content = {"text": text, "channelType": channel_type}
    return msg


def make_mock_room(world_id=None, room_type="CHANNEL"):
    room = MagicMock()
    room.world_id = world_id
    room.type = room_type
    return room


def make_mock_world(world_id="world-1", settings=None):
    world = MagicMock()
    world.id = world_id
    world.metadata = {}
    if settings is not None:
        world.metadata["settings"] = settings
    return world


def make_secret_config(required=False, description="Test", status="valid"):
    return SecretConfig(
        type=SecretType.API_KEY,
        required=required,
        description=description,
        can_generate=False,
        status=SecretStatus(status),
        plugin="test",
        level=SecretLevel.GLOBAL,
    )


class TestSecretsStatusProvider:
    @pytest.mark.asyncio
    async def test_returns_none_when_no_service(self):
        from elizaos_plugin_secrets_manager.providers.secrets_status import (
            SecretsStatusProvider,
        )

        provider = SecretsStatusProvider()
        runtime = make_mock_runtime()
        runtime.get_service = MagicMock(return_value=None)
        msg = make_mock_message()

        result = await provider.get(runtime, msg, {})
        assert result is None

    @pytest.mark.asyncio
    async def test_returns_no_secrets_message(self):
        from elizaos_plugin_secrets_manager.providers.secrets_status import (
            SecretsStatusProvider,
        )

        provider = SecretsStatusProvider()
        runtime = make_mock_runtime()

        mock_service = MagicMock()
        mock_service.service_type = "SECRETS"
        mock_service.list = AsyncMock(return_value={})

        def get_svc(stype):
            if stype == "SECRETS":
                return mock_service
            return None

        runtime.get_service = MagicMock(side_effect=get_svc)
        msg = make_mock_message()

        result = await provider.get(runtime, msg, {})
        assert result is not None
        assert "No secrets" in result

    @pytest.mark.asyncio
    async def test_returns_configured_secrets(self):
        from elizaos_plugin_secrets_manager.providers.secrets_status import (
            SecretsStatusProvider,
        )

        provider = SecretsStatusProvider()
        runtime = make_mock_runtime()

        mock_service = MagicMock()
        mock_service.service_type = "SECRETS"
        mock_service.list = AsyncMock(
            return_value={
                "OPENAI_API_KEY": make_secret_config(),
                "ANTHROPIC_API_KEY": make_secret_config(),
            }
        )

        def get_svc(stype):
            if stype == "SECRETS":
                return mock_service
            return None

        runtime.get_service = MagicMock(side_effect=get_svc)
        msg = make_mock_message()

        result = await provider.get(runtime, msg, {})
        assert result is not None
        assert "OPENAI_API_KEY" in result
        assert "ANTHROPIC_API_KEY" in result

    def test_provider_name(self):
        from elizaos_plugin_secrets_manager.providers.secrets_status import (
            SecretsStatusProvider,
        )

        provider = SecretsStatusProvider()
        assert provider.name == "SECRETS_STATUS"


class TestSecretsInfoProvider:
    @pytest.mark.asyncio
    async def test_returns_none_for_unrelated_message(self):
        from elizaos_plugin_secrets_manager.providers.secrets_status import (
            SecretsInfoProvider,
        )

        provider = SecretsInfoProvider()
        runtime = make_mock_runtime()
        runtime.get_service = MagicMock(return_value=None)
        msg = make_mock_message(text="Hello how are you?")

        result = await provider.get(runtime, msg, {})
        assert result is None

    @pytest.mark.asyncio
    async def test_returns_info_for_secret_question(self):
        from elizaos_plugin_secrets_manager.providers.secrets_status import (
            SecretsInfoProvider,
        )

        provider = SecretsInfoProvider()
        runtime = make_mock_runtime()

        mock_service = MagicMock()
        mock_service.service_type = "SECRETS"
        mock_service.list = AsyncMock(return_value={})

        def get_svc(stype):
            if stype == "SECRETS":
                return mock_service
            return None

        runtime.get_service = MagicMock(side_effect=get_svc)
        msg = make_mock_message(text="What API keys do I have configured?")

        result = await provider.get(runtime, msg, {})
        assert result is not None
        assert "No secrets configured" in result

    @pytest.mark.asyncio
    async def test_returns_detailed_info_with_secrets(self):
        from elizaos_plugin_secrets_manager.providers.secrets_status import (
            SecretsInfoProvider,
        )

        provider = SecretsInfoProvider()
        runtime = make_mock_runtime()

        mock_service = MagicMock()
        mock_service.service_type = "SECRETS"
        mock_service.list = AsyncMock(
            return_value={
                "OPENAI_API_KEY": make_secret_config(),
            }
        )

        def get_svc(stype):
            if stype == "SECRETS":
                return mock_service
            return None

        runtime.get_service = MagicMock(side_effect=get_svc)
        msg = make_mock_message(text="Show me my secret configuration")

        result = await provider.get(runtime, msg, {})
        assert result is not None
        assert "OPENAI_API_KEY" in result
        assert "Total configured secrets: 1" in result


class TestOnboardingSettingsProvider:
    @pytest.mark.asyncio
    async def test_returns_none_for_no_room(self):
        from elizaos_plugin_secrets_manager.providers.onboarding_settings import (
            OnboardingSettingsProvider,
        )

        provider = OnboardingSettingsProvider()
        runtime = make_mock_runtime()
        runtime.get_room = AsyncMock(return_value=None)
        msg = make_mock_message()

        result = await provider.get(runtime, msg, {})
        assert result is None

    @pytest.mark.asyncio
    async def test_returns_none_for_no_world_id(self):
        from elizaos_plugin_secrets_manager.providers.onboarding_settings import (
            OnboardingSettingsProvider,
        )

        provider = OnboardingSettingsProvider()
        runtime = make_mock_runtime()
        room = make_mock_room(world_id=None)
        runtime.get_room = AsyncMock(return_value=room)
        msg = make_mock_message()

        result = await provider.get(runtime, msg, {})
        assert result is None

    @pytest.mark.asyncio
    async def test_dm_with_unconfigured_returns_priority(self):
        from elizaos_plugin_secrets_manager.providers.onboarding_settings import (
            OnboardingSettingsProvider,
        )

        provider = OnboardingSettingsProvider()
        runtime = make_mock_runtime()

        room = make_mock_room(world_id="world-1", room_type="DM")
        world = make_mock_world(
            settings={
                "OPENAI_API_KEY": {
                    "name": "OpenAI API Key",
                    "description": "API key for OpenAI",
                    "usageDescription": "Your OpenAI key",
                    "required": True,
                    "secret": True,
                    "value": None,
                },
            }
        )

        runtime.get_room = AsyncMock(return_value=room)
        runtime.get_world = AsyncMock(return_value=world)
        msg = make_mock_message()

        result = await provider.get(runtime, msg, {"senderName": "Alice"})
        assert result is not None
        assert "PRIORITY TASK" in result
        assert "OPENAI_API_KEY" in result

    @pytest.mark.asyncio
    async def test_non_dm_with_all_configured(self):
        from elizaos_plugin_secrets_manager.providers.onboarding_settings import (
            OnboardingSettingsProvider,
        )

        provider = OnboardingSettingsProvider()
        runtime = make_mock_runtime()

        room = make_mock_room(world_id="world-1", room_type="CHANNEL")
        world = make_mock_world(
            settings={
                "OPENAI_API_KEY": {
                    "name": "OpenAI API Key",
                    "description": "API key",
                    "required": True,
                    "secret": True,
                    "value": "sk-set",
                },
            }
        )

        runtime.get_room = AsyncMock(return_value=room)
        runtime.get_world = AsyncMock(return_value=world)
        msg = make_mock_message()

        result = await provider.get(runtime, msg, {})
        assert result is not None
        assert "All required settings are configured" in result

    def test_provider_name(self):
        from elizaos_plugin_secrets_manager.providers.onboarding_settings import (
            OnboardingSettingsProvider,
        )

        provider = OnboardingSettingsProvider()
        assert provider.name == "ONBOARDING_SETTINGS"


class TestMissingSecretsProvider:
    @pytest.mark.asyncio
    async def test_returns_none_for_no_room(self):
        from elizaos_plugin_secrets_manager.providers.missing_secrets import (
            MissingSecretsProvider,
        )

        provider = MissingSecretsProvider()
        runtime = make_mock_runtime()
        runtime.get_room = AsyncMock(return_value=None)
        msg = make_mock_message()

        result = await provider.get(runtime, msg, {})
        assert result is None

    @pytest.mark.asyncio
    async def test_returns_all_configured(self):
        from elizaos_plugin_secrets_manager.providers.missing_secrets import (
            MissingSecretsProvider,
        )

        provider = MissingSecretsProvider()
        runtime = make_mock_runtime()

        room = make_mock_room(world_id="world-1")
        world = make_mock_world(
            settings={
                "KEY1": {"name": "Key1", "required": True, "value": "set"},
            }
        )

        runtime.get_room = AsyncMock(return_value=room)
        runtime.get_world = AsyncMock(return_value=world)
        msg = make_mock_message()

        result = await provider.get(runtime, msg, {})
        assert result is not None
        assert "All secrets are configured" in result

    @pytest.mark.asyncio
    async def test_returns_missing_required_and_optional(self):
        from elizaos_plugin_secrets_manager.providers.missing_secrets import (
            MissingSecretsProvider,
        )

        provider = MissingSecretsProvider()
        runtime = make_mock_runtime()

        room = make_mock_room(world_id="world-1")
        world = make_mock_world(
            settings={
                "OPENAI_API_KEY": {
                    "name": "OpenAI Key",
                    "description": "OpenAI API key",
                    "required": True,
                    "value": None,
                },
                "GROQ_API_KEY": {
                    "name": "Groq Key",
                    "description": "Groq API key",
                    "required": False,
                    "value": None,
                },
            }
        )

        runtime.get_room = AsyncMock(return_value=room)
        runtime.get_world = AsyncMock(return_value=world)
        msg = make_mock_message()

        result = await provider.get(runtime, msg, {})
        assert result is not None
        assert "Missing required secrets" in result
        assert "OPENAI_API_KEY" in result
        assert "Missing optional secrets" in result
        assert "GROQ_API_KEY" in result

    def test_provider_name(self):
        from elizaos_plugin_secrets_manager.providers.missing_secrets import (
            MissingSecretsProvider,
        )

        provider = MissingSecretsProvider()
        assert provider.name == "MISSING_SECRETS"
