"""
Tests for PluginActivatorService.
"""

import pytest
import asyncio
from unittest.mock import AsyncMock, MagicMock, Mock

from elizaos_plugin_secrets_manager.types import (
    SecretLevel,
    SecretContext,
    PluginSecretRequirement,
    PluginRequirementStatus,
    SecretType,
)
from elizaos_plugin_secrets_manager.activator import (
    PluginActivatorService,
    PluginWithSecrets,
    PendingPluginActivation,
)


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


def make_requirement(key, required=True):
    return PluginSecretRequirement(
        description=f"Requirement: {key}",
        type=SecretType.API_KEY,
        required=required,
    )


def make_mock_secrets_service(existing_keys=None):
    existing_keys = existing_keys or {}
    svc = MagicMock()
    svc.service_type = "SECRETS"

    async def check_reqs(plugin_id, requirements):
        missing_required = []
        missing_optional = []
        for key, req in requirements.items():
            if key not in existing_keys:
                if req.required:
                    missing_required.append(key)
                else:
                    missing_optional.append(key)
        ready = len(missing_required) == 0
        message = "Ready" if ready else f"Missing: {', '.join(missing_required)}"
        return PluginRequirementStatus(
            plugin_id=plugin_id,
            ready=ready,
            missing_required=missing_required,
            missing_optional=missing_optional,
            invalid=[],
            message=message,
        )

    svc.check_plugin_requirements = AsyncMock(side_effect=check_reqs)
    svc.on_any_secret_changed = MagicMock(return_value=lambda: None)
    return svc


class TestPluginRegistration:
    @pytest.fixture
    def setup(self):
        runtime = make_mock_runtime()
        secrets_svc = make_mock_secrets_service(existing_keys={"OPENAI_API_KEY": "sk-test"})

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

        runtime.get_service = MagicMock(side_effect=get_svc)
        activator = PluginActivatorService(runtime)
        return activator, secrets_svc

    @pytest.mark.asyncio
    async def test_immediate_activation_when_ready(self, setup):
        activator, _ = setup
        await activator.start()

        plugin = PluginWithSecrets(
            plugin=MagicMock(name="ready-plugin"),
            secret_requirements={"OPENAI_API_KEY": make_requirement("OPENAI_API_KEY")},
        )

        result = await activator.register_plugin(plugin)
        assert result is True
        assert "ready-plugin" in activator.get_activated_plugins()

    @pytest.mark.asyncio
    async def test_queued_when_not_ready(self, setup):
        activator, _ = setup
        await activator.start()

        plugin = PluginWithSecrets(
            plugin=MagicMock(name="pending-plugin"),
            secret_requirements={"MISSING_KEY": make_requirement("MISSING_KEY")},
        )

        result = await activator.register_plugin(plugin)
        assert result is False
        pending = activator.get_pending_plugins()
        assert len(pending) > 0

    @pytest.mark.asyncio
    async def test_already_activated_returns_true(self, setup):
        activator, _ = setup
        await activator.start()

        plugin = PluginWithSecrets(
            plugin=MagicMock(name="ready-plugin"),
            secret_requirements={"OPENAI_API_KEY": make_requirement("OPENAI_API_KEY")},
        )

        await activator.register_plugin(plugin)
        result = await activator.register_plugin(plugin)
        assert result is True


class TestPluginUnregistration:
    @pytest.mark.asyncio
    async def test_unregister_pending_plugin(self):
        runtime = make_mock_runtime()
        secrets_svc = make_mock_secrets_service()

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

        runtime.get_service = MagicMock(side_effect=get_svc)
        activator = PluginActivatorService(runtime)
        await activator.start()

        plugin = PluginWithSecrets(
            plugin=MagicMock(name="pending-plugin"),
            secret_requirements={"MISSING_KEY": make_requirement("MISSING_KEY")},
        )

        await activator.register_plugin(plugin)
        assert len(activator.get_pending_plugins()) > 0

        result = await activator.unregister_plugin("pending-plugin")
        assert result is True
        assert len(activator.get_pending_plugins()) == 0


class TestForceCheck:
    @pytest.mark.asyncio
    async def test_force_check_activates_ready_plugins(self):
        runtime = make_mock_runtime()
        secrets_svc = make_mock_secrets_service()

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

        runtime.get_service = MagicMock(side_effect=get_svc)
        activator = PluginActivatorService(runtime)
        await activator.start()

        plugin = PluginWithSecrets(
            plugin=MagicMock(name="late-plugin"),
            secret_requirements={"LATE_KEY": make_requirement("LATE_KEY")},
        )

        await activator.register_plugin(plugin)
        assert len(activator.get_pending_plugins()) > 0

        # Simulate key becoming available
        updated_svc = make_mock_secrets_service(existing_keys={"LATE_KEY": "value"})
        secrets_svc.check_plugin_requirements = updated_svc.check_plugin_requirements

        activated = await activator.force_check()
        assert activated == 1
        assert "late-plugin" in activator.get_activated_plugins()


class TestPluginStatus:
    @pytest.mark.asyncio
    async def test_check_activated_plugin_status(self):
        runtime = make_mock_runtime()
        secrets_svc = make_mock_secrets_service(existing_keys={"KEY": "val"})

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

        runtime.get_service = MagicMock(side_effect=get_svc)
        activator = PluginActivatorService(runtime)
        await activator.start()

        plugin = PluginWithSecrets(
            plugin=MagicMock(name="status-plugin"),
            secret_requirements={"KEY": make_requirement("KEY")},
        )

        await activator.register_plugin(plugin)
        status = await activator.check_plugin_status("status-plugin")
        assert status is not None
        assert status.ready is True


class TestServiceLifecycle:
    @pytest.mark.asyncio
    async def test_start_and_stop(self):
        runtime = make_mock_runtime()
        secrets_svc = make_mock_secrets_service()

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

        runtime.get_service = MagicMock(side_effect=get_svc)
        activator = PluginActivatorService(runtime)

        await activator.start()
        await activator.stop()

        assert len(activator.get_pending_plugins()) == 0
        assert len(activator.get_activated_plugins()) == 0
