"""Tests for the Telegram bot integration."""

import json
from unittest.mock import AsyncMock, MagicMock, patch

import pytest

# ── TelegramBot unit tests ────────────────────────────────────────────────────


class TestTelegramBotTokenHelpers:
    def test_save_and_load_token(self, tmp_path, monkeypatch):
        """save_token persists to disk; load_token reads it back."""
        state_file = tmp_path / "telegram_state.json"
        monkeypatch.setattr("integrations.telegram_bot.bot._STATE_FILE", state_file)

        from integrations.telegram_bot.bot import TelegramBot

        bot = TelegramBot()

        bot.save_token("test-token-123")
        assert state_file.exists()
        data = json.loads(state_file.read_text())
        assert data["token"] == "test-token-123"

        loaded = bot.load_token()
        assert loaded == "test-token-123"

    def test_load_token_missing_file(self, tmp_path, monkeypatch):
        """load_token returns None when no state file and no env var."""
        state_file = tmp_path / "missing_telegram_state.json"
        monkeypatch.setattr("integrations.telegram_bot.bot._STATE_FILE", state_file)

        # Ensure settings.telegram_token is empty
        mock_settings = MagicMock()
        mock_settings.telegram_token = ""
        with patch("integrations.telegram_bot.bot._load_token_from_file", return_value=None):
            with patch("config.settings", mock_settings):
                from integrations.telegram_bot.bot import TelegramBot

                bot = TelegramBot()
                result = bot.load_token()
        assert result is None

    def test_token_set_property(self):
        """token_set reflects whether a token has been applied."""
        from integrations.telegram_bot.bot import TelegramBot

        bot = TelegramBot()
        assert not bot.token_set
        bot._token = "tok"
        assert bot.token_set

    def test_is_running_property(self):
        from integrations.telegram_bot.bot import TelegramBot

        bot = TelegramBot()
        assert not bot.is_running
        bot._running = True
        assert bot.is_running


class TestTelegramBotLifecycle:
    @pytest.mark.asyncio
    async def test_start_no_token_returns_false(self, tmp_path, monkeypatch):
        """start() returns False and stays idle when no token is available."""
        state_file = tmp_path / "telegram_state.json"
        monkeypatch.setattr("integrations.telegram_bot.bot._STATE_FILE", state_file)

        from integrations.telegram_bot.bot import TelegramBot

        bot = TelegramBot()
        with patch.object(bot, "load_token", return_value=None):
            result = await bot.start()
        assert result is False
        assert not bot.is_running

    @pytest.mark.asyncio
    async def test_start_already_running_returns_true(self):
        from integrations.telegram_bot.bot import TelegramBot

        bot = TelegramBot()
        bot._running = True
        result = await bot.start(token="any")
        assert result is True

    @pytest.mark.asyncio
    async def test_start_import_error_returns_false(self):
        """start() returns False gracefully when python-telegram-bot absent."""
        from integrations.telegram_bot.bot import TelegramBot

        bot = TelegramBot()

        with (
            patch.object(bot, "load_token", return_value="tok"),
            patch.dict("sys.modules", {"telegram": None, "telegram.ext": None}),
        ):
            result = await bot.start(token="tok")
        assert result is False
        assert not bot.is_running

    @pytest.mark.asyncio
    async def test_stop_when_not_running_is_noop(self):
        from integrations.telegram_bot.bot import TelegramBot

        bot = TelegramBot()
        # Should not raise
        await bot.stop()

    @pytest.mark.asyncio
    async def test_stop_calls_shutdown(self):
        """stop() invokes the Application shutdown sequence."""
        from integrations.telegram_bot.bot import TelegramBot

        bot = TelegramBot()
        bot._running = True

        mock_updater = AsyncMock()
        mock_app = AsyncMock()
        mock_app.updater = mock_updater
        bot._app = mock_app

        await bot.stop()

        mock_updater.stop.assert_awaited_once()
        mock_app.stop.assert_awaited_once()
        mock_app.shutdown.assert_awaited_once()
        assert not bot.is_running


# ── Dashboard route tests ─────────────────────────────────────────────────────


class TestTelegramRoutes:
    def test_status_not_running(self, client):
        """GET /telegram/status returns running=False when bot is idle."""
        from integrations.telegram_bot.bot import telegram_bot

        telegram_bot._running = False
        telegram_bot._token = None

        resp = client.get("/telegram/status")
        assert resp.status_code == 200
        data = resp.json()
        assert data["running"] is False
        assert data["token_set"] is False

    def test_status_running(self, client):
        """GET /telegram/status returns running=True when bot is active."""
        from integrations.telegram_bot.bot import telegram_bot

        telegram_bot._running = True
        telegram_bot._token = "tok"

        resp = client.get("/telegram/status")
        assert resp.status_code == 200
        data = resp.json()
        assert data["running"] is True
        assert data["token_set"] is True

        # Cleanup
        telegram_bot._running = False
        telegram_bot._token = None

    def test_setup_empty_token(self, client):
        """POST /telegram/setup with empty token returns error."""
        resp = client.post("/telegram/setup", json={"token": ""})
        assert resp.status_code == 200
        data = resp.json()
        assert data["ok"] is False
        assert "empty" in data["error"].lower()

    def test_setup_success(self, client):
        """POST /telegram/setup with valid token starts bot and returns ok."""
        from integrations.telegram_bot.bot import telegram_bot

        telegram_bot._running = False
        with (
            patch.object(telegram_bot, "save_token") as mock_save,
            patch.object(telegram_bot, "start", new_callable=AsyncMock, return_value=True),
        ):
            resp = client.post("/telegram/setup", json={"token": "bot123:abc"})

        assert resp.status_code == 200
        data = resp.json()
        assert data["ok"] is True
        mock_save.assert_called_once_with("bot123:abc")

    def test_setup_failure(self, client):
        """POST /telegram/setup returns error dict when bot fails to start."""
        from integrations.telegram_bot.bot import telegram_bot

        telegram_bot._running = False
        with (
            patch.object(telegram_bot, "save_token"),
            patch.object(telegram_bot, "start", new_callable=AsyncMock, return_value=False),
        ):
            resp = client.post("/telegram/setup", json={"token": "bad-token"})

        assert resp.status_code == 200
        data = resp.json()
        assert data["ok"] is False
        assert "error" in data

    def test_setup_stops_running_bot_first(self, client):
        """POST /telegram/setup stops any running bot before starting new one."""
        from integrations.telegram_bot.bot import telegram_bot

        telegram_bot._running = True

        with (
            patch.object(telegram_bot, "save_token"),
            patch.object(telegram_bot, "stop", new_callable=AsyncMock) as mock_stop,
            patch.object(telegram_bot, "start", new_callable=AsyncMock, return_value=True),
        ):
            resp = client.post("/telegram/setup", json={"token": "new-token"})

        mock_stop.assert_awaited_once()
        assert resp.json()["ok"] is True
        telegram_bot._running = False


# ── Module singleton test ─────────────────────────────────────────────────────


def test_module_singleton_exists():
    """telegram_bot module exposes a singleton TelegramBot instance."""
    from integrations.telegram_bot.bot import TelegramBot, telegram_bot

    assert isinstance(telegram_bot, TelegramBot)
