"""Tests for hot-reload functionality."""
import time
import pytest
import yaml

from logos_adk.agent import Agent
from logos_adk.errors import ConfigError
from logos_adk.providers.mock import MockProvider


class TestConfigPath:
    def test_from_config_stores_path(self, tmp_path):
        config_file = tmp_path / "agent.yaml"
        config_file.write_text(yaml.dump({"name": "bot", "model": "claude-sonnet"}))
        agent = Agent.from_config(str(config_file))
        assert agent._config_path == str(config_file)

    def test_builder_agent_has_no_path(self):
        provider = MockProvider()
        agent = Agent("bot", model=provider)
        assert agent._config_path is None


class TestReload:
    def test_reload_updates_system_prompt(self, tmp_path):
        config_file = tmp_path / "agent.yaml"
        config_file.write_text(yaml.dump({
            "name": "bot",
            "model": "claude-sonnet",
            "system_prompt": "You are v1",
        }))
        agent = Agent.from_config(str(config_file))
        assert agent._config.system_prompt == "You are v1"

        config_file.write_text(yaml.dump({
            "name": "bot",
            "model": "claude-sonnet",
            "system_prompt": "You are v2",
        }))
        agent.reload()
        assert agent._config.system_prompt == "You are v2"

    @pytest.mark.asyncio
    async def test_reload_preserves_sessions(self, tmp_path):
        config_file = tmp_path / "agent.yaml"
        config_file.write_text(yaml.dump({"name": "bot", "model": "claude-sonnet"}))
        agent = Agent.from_config(str(config_file))

        provider = MockProvider(responses=["hello"])
        agent._config.model = provider
        await agent.run("hi")
        assert len(agent._sessions) > 0

        config_file.write_text(yaml.dump({
            "name": "bot",
            "model": "claude-sonnet",
            "system_prompt": "Updated",
        }))
        agent.reload()
        assert len(agent._sessions) > 0

    def test_reload_resets_provider(self, tmp_path):
        config_file = tmp_path / "agent.yaml"
        config_file.write_text(yaml.dump({"name": "bot", "model": "claude-sonnet"}))
        agent = Agent.from_config(str(config_file))
        agent._provider = MockProvider()

        agent.reload()
        assert agent._provider is None

    def test_reload_resets_mcp_connected(self, tmp_path):
        config_file = tmp_path / "agent.yaml"
        config_file.write_text(yaml.dump({"name": "bot", "model": "claude-sonnet"}))
        agent = Agent.from_config(str(config_file))
        agent._mcp_connected = True

        agent.reload()
        assert agent._mcp_connected is False

    def test_reload_without_path_raises(self):
        provider = MockProvider()
        agent = Agent("bot", model=provider)
        with pytest.raises(ConfigError, match="no config path"):
            agent.reload()

    def test_reload_invalid_yaml_keeps_old_config(self, tmp_path):
        config_file = tmp_path / "agent.yaml"
        config_file.write_text(yaml.dump({
            "name": "bot",
            "model": "claude-sonnet",
            "system_prompt": "original",
        }))
        agent = Agent.from_config(str(config_file))
        assert agent._config.system_prompt == "original"

        config_file.write_text("name: [invalid yaml structure")
        with pytest.raises((yaml.YAMLError, ConfigError)):
            agent.reload()
        assert agent._config.system_prompt == "original"

    def test_reload_with_explicit_path(self, tmp_path):
        config1 = tmp_path / "agent1.yaml"
        config2 = tmp_path / "agent2.yaml"
        config1.write_text(yaml.dump({"name": "bot", "model": "claude-sonnet", "system_prompt": "v1"}))
        config2.write_text(yaml.dump({"name": "bot", "model": "claude-sonnet", "system_prompt": "v2"}))

        agent = Agent.from_config(str(config1))
        assert agent._config.system_prompt == "v1"

        agent.reload(str(config2))
        assert agent._config.system_prompt == "v2"
        assert agent._config_path == str(config2)


# ---------------------------------------------------------------------------
# ConfigWatcher
# ---------------------------------------------------------------------------

from logos_adk.hot_reload import ConfigWatcher


class TestConfigWatcher:
    def test_register_stores_agent(self, tmp_path):
        config_file = tmp_path / "agent.yaml"
        config_file.write_text(yaml.dump({"name": "bot", "model": "claude-sonnet"}))
        agent = Agent.from_config(str(config_file))

        watcher = ConfigWatcher(str(tmp_path))
        watcher.register(agent)
        assert str(config_file) in watcher._watched

    def test_register_no_path_raises(self):
        provider = MockProvider()
        agent = Agent("bot", model=provider)
        watcher = ConfigWatcher(".")
        with pytest.raises(ConfigError, match="no config path"):
            watcher.register(agent)

    def test_detects_change(self, tmp_path):
        config_file = tmp_path / "agent.yaml"
        config_file.write_text(yaml.dump({
            "name": "bot", "model": "claude-sonnet", "system_prompt": "v1",
        }))
        agent = Agent.from_config(str(config_file))

        reloaded = []
        watcher = ConfigWatcher(str(tmp_path), interval=0.1, on_reload=lambda a, p: reloaded.append(p))
        watcher.register(agent)
        watcher.start()

        try:
            time.sleep(0.15)
            config_file.write_text(yaml.dump({
                "name": "bot", "model": "claude-sonnet", "system_prompt": "v2",
            }))
            time.sleep(0.3)
        finally:
            watcher.stop()

        assert len(reloaded) >= 1
        assert agent._config.system_prompt == "v2"

    def test_error_callback(self, tmp_path):
        config_file = tmp_path / "agent.yaml"
        config_file.write_text(yaml.dump({"name": "bot", "model": "claude-sonnet"}))
        agent = Agent.from_config(str(config_file))

        errors = []
        watcher = ConfigWatcher(
            str(tmp_path), interval=0.1,
            on_error=lambda a, p, e: errors.append(str(e)),
        )
        watcher.register(agent)
        watcher.start()

        try:
            time.sleep(0.15)
            config_file.write_text("name: [broken yaml")
            time.sleep(0.3)
        finally:
            watcher.stop()

        assert len(errors) >= 1

    def test_start_stop_lifecycle(self, tmp_path):
        watcher = ConfigWatcher(str(tmp_path), interval=0.1)
        watcher.start()
        assert watcher._running
        watcher.stop()
        assert not watcher._running
