from pathlib import Path

from watchfiles import Change

import asyncio
import pytest

from src.config.config import ConfigManager
from src.config.file_watcher import FileChange, FileWatcherStats


@pytest.mark.asyncio
async def test_handle_file_changes_throttles_reload():
    manager = ConfigManager()
    manager._hot_reload_min_interval_s = 100.0

    called = 0

    async def reload_stub(changed_scopes=None) -> bool:
        nonlocal called
        called += 1
        return True

    manager.reload_config = reload_stub  # type: ignore[method-assign]
    changes = [FileChange(change_type=Change.modified, path=Path("/tmp/bot_config.toml"))]

    await manager._handle_file_changes(changes)
    await manager._handle_file_changes(changes)

    assert called == 1


@pytest.mark.asyncio
async def test_handle_file_changes_timeout_logged(caplog):
    manager = ConfigManager()
    manager._hot_reload_min_interval_s = 0.0
    manager._hot_reload_timeout_s = 0.01

    async def reload_stub(changed_scopes=None) -> bool:
        await asyncio.sleep(0.05)
        return True

    manager.reload_config = reload_stub  # type: ignore[method-assign]
    changes = [FileChange(change_type=Change.modified, path=Path("/tmp/model_config.toml"))]

    with caplog.at_level("ERROR"):
        await manager._handle_file_changes(changes)

    assert "配置热重载超时" in caplog.text


@pytest.mark.asyncio
async def test_handle_file_changes_empty_skips_reload():
    manager = ConfigManager()

    called = 0

    async def reload_stub(changed_scopes=None) -> bool:
        nonlocal called
        called += 1
        return True

    manager.reload_config = reload_stub  # type: ignore[method-assign]

    await manager._handle_file_changes([])

    assert called == 0


class _FakeWatcher:
    def __init__(self):
        self.unsubscribe_called_with: str | None = None
        self.stop_called = False
        self.stats = FileWatcherStats(
            batches_seen=1,
            changes_seen=2,
            callbacks_succeeded=3,
            callbacks_failed=4,
            callbacks_timed_out=5,
            callbacks_skipped_cooldown=6,
            restart_count=7,
        )

    def unsubscribe(self, subscription_id: str) -> bool:
        self.unsubscribe_called_with = subscription_id
        return True

    async def stop(self) -> None:
        self.stop_called = True


@pytest.mark.asyncio
async def test_stop_file_watcher_cleans_state():
    manager = ConfigManager()
    fake_watcher = _FakeWatcher()
    manager._file_watcher = fake_watcher  # type: ignore[assignment]
    manager._file_watcher_subscription_id = "sub-1"

    await manager.stop_file_watcher()

    assert fake_watcher.unsubscribe_called_with == "sub-1"
    assert fake_watcher.stop_called is True
    assert manager._file_watcher is None
    assert manager._file_watcher_subscription_id is None
