"""Tests for CloudflaredManager service."""

from unittest.mock import AsyncMock, MagicMock, patch

import pytest

from app.modules.integrations.application.cloudflared_manager import CloudflaredManager, get_cloudflared_manager


@pytest.mark.anyio
async def test_cloudflared_manager_singleton():
    mgr1 = get_cloudflared_manager()
    mgr2 = get_cloudflared_manager()
    assert mgr1 is mgr2


@pytest.mark.anyio
async def test_cloudflared_manager_health_check_success():
    manager = CloudflaredManager()
    with patch("httpx.AsyncClient.get", new_callable=AsyncMock) as mock_get:
        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_get.return_value = mock_response

        is_healthy = await manager.is_tunnel_healthy()
        assert is_healthy is True


@pytest.mark.anyio
async def test_cloudflared_manager_health_check_failure_and_fallback():
    manager = CloudflaredManager()
    with (
        patch("httpx.AsyncClient.get", side_effect=Exception("Connection refused")),
        patch.object(manager, "_is_system_process_running", return_value=False),
    ):
        # If no process running, should be False
        manager._process = None
        is_healthy = await manager.is_tunnel_healthy()
        assert is_healthy is False


@pytest.mark.anyio
async def test_cloudflared_manager_check_and_recover_when_healthy():
    manager = CloudflaredManager()
    with (
        patch.object(
            manager, "is_tunnel_healthy", new_callable=AsyncMock
        ) as mock_health,
        patch.object(manager, "start_tunnel", new_callable=AsyncMock) as mock_start,
    ):
        mock_health.return_value = True
        await manager.check_and_recover()
        mock_start.assert_not_called()


@pytest.mark.anyio
async def test_cloudflared_manager_check_and_recover_when_unhealthy():
    manager = CloudflaredManager()
    with (
        patch.object(manager, "is_tunnel_healthy", side_effect=[False, True]),
        patch.object(manager, "_is_system_process_running", return_value=False),
        patch.object(manager, "start_tunnel", new_callable=AsyncMock) as mock_start,
    ):
        mock_start.return_value = True
        await manager.check_and_recover()
        mock_start.assert_called_once()
