"""
Tests for the OnboardingService and related utilities.
"""

import pytest
from unittest.mock import AsyncMock, MagicMock

from elizaos_plugin_secrets_manager.onboarding import (
    OnboardingService,
    OnboardingSetting,
    OnboardingConfig,
    OnboardingFlowStep,
    create_onboarding_config,
    get_unconfigured_required,
    get_unconfigured_optional,
    get_next_setting,
    is_onboarding_complete,
    DEFAULT_ONBOARDING_MESSAGES,
    COMMON_API_KEY_SETTINGS,
)


# ============================================================================
# Configuration Utilities Tests
# ============================================================================

class TestOnboardingConfigUtils:
    def test_create_onboarding_config_required(self):
        config = create_onboarding_config(["OPENAI_API_KEY", "ANTHROPIC_API_KEY"])
        assert "OPENAI_API_KEY" in config.settings
        assert "ANTHROPIC_API_KEY" in config.settings
        assert config.settings["OPENAI_API_KEY"].required is True

    def test_create_onboarding_config_optional(self):
        config = create_onboarding_config([], ["GROQ_API_KEY"])
        assert "GROQ_API_KEY" in config.settings
        assert config.settings["GROQ_API_KEY"].required is False

    def test_create_onboarding_config_custom(self):
        config = create_onboarding_config(
            ["MY_KEY"],
            custom_settings={"MY_KEY": {"name": "My Custom Key", "description": "A custom key"}},
        )
        assert config.settings["MY_KEY"].name == "My Custom Key"

    def test_get_unconfigured_required(self):
        config = OnboardingConfig(settings={
            "KEY1": OnboardingSetting(name="K1", description="D1", secret=True, public=False, required=True, value=None),
            "KEY2": OnboardingSetting(name="K2", description="D2", secret=True, public=False, required=True, value="set"),
            "KEY3": OnboardingSetting(name="K3", description="D3", secret=True, public=False, required=False, value=None),
        })
        unconfigured = get_unconfigured_required(config)
        assert len(unconfigured) == 1
        assert unconfigured[0][0] == "KEY1"

    def test_get_unconfigured_optional(self):
        config = OnboardingConfig(settings={
            "KEY1": OnboardingSetting(name="K1", description="D1", secret=True, public=False, required=True, value=None),
            "KEY2": OnboardingSetting(name="K2", description="D2", secret=True, public=False, required=False, value=None),
        })
        optional = get_unconfigured_optional(config)
        assert len(optional) == 1
        assert optional[0][0] == "KEY2"

    def test_is_onboarding_complete_false(self):
        config = create_onboarding_config(["OPENAI_API_KEY"])
        assert is_onboarding_complete(config) is False

    def test_is_onboarding_complete_true(self):
        config = create_onboarding_config(["OPENAI_API_KEY"])
        config.settings["OPENAI_API_KEY"].value = "sk-test"
        assert is_onboarding_complete(config) is True

    def test_get_next_setting(self):
        config = create_onboarding_config(["OPENAI_API_KEY", "ANTHROPIC_API_KEY"])
        result = get_next_setting(config)
        assert result is not None
        assert result[0] == "OPENAI_API_KEY"

    def test_get_next_setting_respects_dependencies(self):
        config = OnboardingConfig(settings={
            "TWITTER_USERNAME": OnboardingSetting(
                name="Twitter User", description="Username", secret=False,
                public=True, required=True, value=None, depends_on=[],
            ),
            "TWITTER_PASSWORD": OnboardingSetting(
                name="Twitter Pass", description="Password", secret=True,
                public=False, required=True, value=None, depends_on=["TWITTER_USERNAME"],
            ),
        })
        result = get_next_setting(config)
        assert result is not None
        assert result[0] == "TWITTER_USERNAME"

    def test_get_next_setting_none_when_complete(self):
        config = create_onboarding_config(["OPENAI_API_KEY"])
        config.settings["OPENAI_API_KEY"].value = "set"
        result = get_next_setting(config)
        assert result is None


# ============================================================================
# OnboardingService Tests
# ============================================================================

class TestOnboardingService:
    @pytest.fixture
    def service(self):
        runtime = MagicMock()
        runtime.agent_id = "test-agent"
        runtime.get_service = MagicMock(return_value=None)
        runtime.character = MagicMock()
        runtime.character.settings = {"secrets": {}}
        return OnboardingService(runtime)

    def test_detect_missing_secrets_all_missing(self, service):
        runtime = MagicMock()
        runtime.character = MagicMock()
        runtime.character.settings = {}
        runtime.get_setting = MagicMock(return_value=None)

        missing = service.detect_missing_secrets(runtime, ["KEY_A", "KEY_B"])
        assert missing == ["KEY_A", "KEY_B"]

    def test_detect_missing_secrets_some_present(self, service):
        runtime = MagicMock()
        runtime.character = MagicMock()
        runtime.character.settings = {"secrets": {"KEY_A": "value"}}
        runtime.get_setting = MagicMock(return_value=None)

        missing = service.detect_missing_secrets(runtime, ["KEY_A", "KEY_B"])
        assert missing == ["KEY_B"]

    def test_create_onboarding_flow(self, service):
        runtime = MagicMock()
        steps = service.create_onboarding_flow(runtime, ["OPENAI_API_KEY", "ANTHROPIC_API_KEY"])
        assert len(steps) == 2
        assert steps[0].key == "OPENAI_API_KEY"
        assert steps[0].step_number == 1
        assert steps[0].total_steps == 2
        assert steps[1].key == "ANTHROPIC_API_KEY"
        assert steps[1].step_number == 2

    @pytest.mark.asyncio
    async def test_process_step_empty_input(self, service):
        step = OnboardingFlowStep(
            key="KEY", setting=OnboardingSetting(
                name="Test", description="Desc", secret=True, public=False, required=True,
            ),
            prompt="Enter key", step_number=1, total_steps=1, is_required=True,
        )
        result = await service.process_onboarding_step(MagicMock(), step, "")
        assert result.success is False
        assert result.error is not None

    @pytest.mark.asyncio
    async def test_process_step_last_step(self, service):
        step = OnboardingFlowStep(
            key="API_KEY", setting=OnboardingSetting(
                name="API Key", description="Desc", secret=True, public=False, required=True,
            ),
            prompt="Enter key", step_number=1, total_steps=1, is_required=True,
        )
        result = await service.process_onboarding_step(MagicMock(), step, "sk-valid-key-value")
        assert result.success is True
        assert result.complete is True
        assert result.updated_key == "API_KEY"

    @pytest.mark.asyncio
    async def test_process_step_not_last(self, service):
        step = OnboardingFlowStep(
            key="API_KEY", setting=OnboardingSetting(
                name="API Key", description="Desc", secret=True, public=False, required=True,
            ),
            prompt="Enter key", step_number=1, total_steps=3, is_required=True,
        )
        result = await service.process_onboarding_step(MagicMock(), step, "test-value")
        assert result.success is True
        assert result.complete is False

    def test_generate_settings_context_onboarding(self, service):
        config = create_onboarding_config(["OPENAI_API_KEY"])
        context = service.generate_settings_context(config, is_onboarding=True, agent_name="Bot")
        assert "PRIORITY TASK" in context
        assert "OPENAI_API_KEY" in context

    def test_generate_settings_context_complete(self, service):
        config = create_onboarding_config(["OPENAI_API_KEY"])
        config.settings["OPENAI_API_KEY"].value = "set"
        context = service.generate_settings_context(config, is_onboarding=False, agent_name="Bot")
        assert "All required settings are configured" in context
