"""Tests for duty_teller.run (main entry point with mocks)."""

import asyncio
from unittest.mock import MagicMock, patch

import pytest

from duty_teller.run import main, _run_uvicorn, _set_default_menu_button_webapp


def test_main_builds_app_and_starts_thread():
    """main: calls require_bot_token, builds Application, register_handlers, starts uvicorn thread, run_polling."""
    mock_app = MagicMock()
    mock_app.run_polling = MagicMock(side_effect=KeyboardInterrupt())
    mock_builder = MagicMock()
    mock_builder.token.return_value = mock_builder
    mock_builder.post_init.return_value = mock_builder
    mock_builder.build.return_value = mock_app

    # Avoid loading real DB when main() imports api.app (prevents unclosed connection warnings).
    mock_session = MagicMock()
    mock_scope = MagicMock()
    mock_scope.return_value.__enter__.return_value = mock_session
    mock_scope.return_value.__exit__.return_value = None

    with patch("duty_teller.run.require_bot_token"):
        with patch("duty_teller.run.config") as mock_cfg:
            mock_cfg.MINI_APP_SKIP_AUTH = False
            mock_cfg.HTTP_HOST = "127.0.0.1"
            mock_cfg.HTTP_PORT = 8080
            with patch("duty_teller.run.ApplicationBuilder", return_value=mock_builder):
                with patch("duty_teller.run.register_handlers") as mock_register:
                    with patch("duty_teller.run.threading.Thread") as mock_thread_class:
                        with patch(
                            "duty_teller.run._wait_for_http_ready", return_value=True
                        ):
                            with patch(
                                "duty_teller.db.session.session_scope", mock_scope
                            ):
                                mock_thread = MagicMock()
                                mock_thread_class.return_value = mock_thread
                                with pytest.raises(KeyboardInterrupt):
                                    main()
    mock_register.assert_called_once_with(mock_app)
    mock_builder.token.assert_called_once()
    mock_thread.start.assert_called_once()
    mock_app.run_polling.assert_called_once()


def test_run_uvicorn_creates_server():
    """_run_uvicorn: creates uvicorn config and runs server (mocked)."""
    with patch("uvicorn.Server") as mock_server_class:
        mock_server = MagicMock()
        # run_until_complete needs a real coroutine
        mock_server.serve = MagicMock(return_value=asyncio.sleep(0))
        mock_server_class.return_value = mock_server
        _run_uvicorn(MagicMock(), 8080)
    mock_server_class.assert_called_once()
    mock_server.serve.assert_called_once()


def test_set_default_menu_button_skips_when_no_base_url():
    """_set_default_menu_button_webapp: returns early when MINI_APP_BASE_URL or BOT_TOKEN not set."""
    with patch("duty_teller.run.config") as mock_cfg:
        mock_cfg.MINI_APP_BASE_URL = ""
        mock_cfg.BOT_TOKEN = "token"
        _set_default_menu_button_webapp()
        mock_cfg.MINI_APP_BASE_URL = "https://example.com"
        mock_cfg.BOT_TOKEN = ""
        _set_default_menu_button_webapp()
    # No urlopen should be called
    with patch("duty_teller.run.config") as mock_cfg:
        mock_cfg.MINI_APP_BASE_URL = None
        mock_cfg.BOT_TOKEN = "x"
        _set_default_menu_button_webapp()


def test_set_default_menu_button_skips_when_not_https():
    """_set_default_menu_button_webapp: returns early when menu_url does not start with https."""
    with patch("duty_teller.run.config") as mock_cfg:
        mock_cfg.MINI_APP_BASE_URL = "http://example.com"
        mock_cfg.BOT_TOKEN = "token"
        _set_default_menu_button_webapp()
    # No urlopen


def test_set_default_menu_button_calls_telegram_api():
    """_set_default_menu_button_webapp: when URL is https, calls safe_urlopen (Telegram API mocked)."""
    with patch("duty_teller.run.config") as mock_cfg:
        mock_cfg.MINI_APP_BASE_URL = "https://example.com/"
        mock_cfg.BOT_TOKEN = "123:ABC"
        with patch("duty_teller.run.safe_urlopen") as mock_safe_urlopen:
            mock_resp = MagicMock()
            mock_resp.status = 200
            mock_resp.__enter__ = MagicMock(return_value=mock_resp)
            mock_resp.__exit__ = MagicMock(return_value=False)
            mock_safe_urlopen.return_value = mock_resp
            _set_default_menu_button_webapp()
    mock_safe_urlopen.assert_called_once()
    req = mock_safe_urlopen.call_args[0][0]
    assert "setChatMenuButton" in req.get_full_url()
    assert "123:ABC" in req.get_full_url()


def test_set_default_menu_button_handles_urlopen_error():
    """_set_default_menu_button_webapp: on safe_urlopen error, logs and does not raise."""
    with patch("duty_teller.run.config") as mock_cfg:
        mock_cfg.MINI_APP_BASE_URL = "https://example.com/"
        mock_cfg.BOT_TOKEN = "123:ABC"
        with patch("duty_teller.run.safe_urlopen") as mock_safe_urlopen:
            mock_safe_urlopen.side_effect = OSError("network error")
            _set_default_menu_button_webapp()
    mock_safe_urlopen.assert_called_once()


def test_set_default_menu_button_handles_non_200_response():
    """_set_default_menu_button_webapp: on non-200 response, logs warning."""
    with patch("duty_teller.run.config") as mock_cfg:
        mock_cfg.MINI_APP_BASE_URL = "https://example.com/"
        mock_cfg.BOT_TOKEN = "123:ABC"
        with patch("duty_teller.run.safe_urlopen") as mock_safe_urlopen:
            mock_resp = MagicMock()
            mock_resp.status = 400
            mock_resp.__enter__ = MagicMock(return_value=mock_resp)
            mock_resp.__exit__ = MagicMock(return_value=False)
            mock_safe_urlopen.return_value = mock_resp
            _set_default_menu_button_webapp()
    mock_safe_urlopen.assert_called_once()


def test_run_uvicorn_uses_config_host():
    """_run_uvicorn: passes config.HTTP_HOST to uvicorn.Config."""
    with patch("uvicorn.Server") as mock_server_class:
        mock_server = MagicMock()
        mock_server.serve = MagicMock(return_value=asyncio.sleep(0))
        mock_server_class.return_value = mock_server
        with patch("duty_teller.run.config") as mock_cfg:
            mock_cfg.HTTP_HOST = "127.0.0.1"
            _run_uvicorn(MagicMock(), 8080)
    call_kwargs = mock_server_class.call_args[0][0]
    assert call_kwargs.host == "127.0.0.1"
    assert call_kwargs.port == 8080
