"""Tests for plugin architecture."""

import pytest

from bilgeai.plugins.base import BilgePlugin, HookType
from bilgeai.plugins.manager import PluginManager


class DummyPlugin(BilgePlugin):
    """Test plugin for unit tests."""

    name = "test-plugin"
    version = "1.0.0"
    description = "A test plugin"
    activated = False
    deactivated = False
    hook_calls: list = []  # noqa: RUF012

    def activate(self):
        self.activated = True

    def deactivate(self):
        self.deactivated = True

    def get_hooks(self):
        return {
            HookType.POST_TRAIN: self.on_post_train,
            HookType.PRE_ANALYZE: self.on_pre_analyze,
        }

    def on_post_train(self, **kwargs):
        self.hook_calls.append(("post_train", kwargs))
        return "post_train_result"

    def on_pre_analyze(self, **kwargs):
        self.hook_calls.append(("pre_analyze", kwargs))
        return "pre_analyze_result"


class AnotherPlugin(BilgePlugin):
    name = "another-plugin"
    version = "0.1.0"


class TestBilgePlugin:
    def test_abstract_requires_name_version(self):
        with pytest.raises(TypeError):
            BilgePlugin()

    def test_dummy_plugin_properties(self):
        p = DummyPlugin()
        assert p.name == "test-plugin"
        assert p.version == "1.0.0"
        assert p.description == "A test plugin"

    def test_default_description(self):
        p = AnotherPlugin()
        assert p.description == ""

    def test_default_hooks_empty(self):
        p = AnotherPlugin()
        assert p.get_hooks() == {}


class TestHookType:
    def test_all_hooks_defined(self):
        expected = {
            "pre_profile",
            "post_profile",
            "pre_analyze",
            "post_analyze",
            "pre_optimize",
            "post_optimize",
            "pre_train",
            "post_train",
            "pre_generate",
            "post_generate",
        }
        assert {h.value for h in HookType} == expected


class TestPluginManager:
    def test_load_plugin(self):
        mgr = PluginManager()
        plugin = DummyPlugin()
        mgr.load(plugin)

        assert len(mgr.plugins) == 1
        assert mgr.plugins[0].name == "test-plugin"
        assert plugin.activated

    def test_duplicate_load_skipped(self):
        mgr = PluginManager()
        mgr.load(DummyPlugin())
        mgr.load(DummyPlugin())
        assert len(mgr.plugins) == 1

    def test_unload_plugin(self):
        mgr = PluginManager()
        plugin = DummyPlugin()
        mgr.load(plugin)
        assert mgr.unload("test-plugin")
        assert len(mgr.plugins) == 0
        assert plugin.deactivated

    def test_unload_nonexistent(self):
        mgr = PluginManager()
        assert not mgr.unload("does-not-exist")

    def test_get_plugin(self):
        mgr = PluginManager()
        plugin = DummyPlugin()
        mgr.load(plugin)
        assert mgr.get_plugin("test-plugin") is plugin
        assert mgr.get_plugin("nope") is None

    def test_run_hook(self):
        mgr = PluginManager()
        plugin = DummyPlugin()
        plugin.hook_calls = []
        mgr.load(plugin)

        results = mgr.run_hook(HookType.POST_TRAIN, loss=0.5)
        assert results == ["post_train_result"]
        assert len(plugin.hook_calls) == 1
        assert plugin.hook_calls[0] == ("post_train", {"loss": 0.5})

    def test_run_hook_no_handlers(self):
        mgr = PluginManager()
        results = mgr.run_hook(HookType.PRE_PROFILE)
        assert results == []

    def test_discover_returns_list(self):
        mgr = PluginManager()
        discovered = mgr.discover()
        assert isinstance(discovered, list)

    def test_load_all(self):
        mgr = PluginManager()
        count = mgr.load_all()
        assert isinstance(count, int)
