import pytest
from app.services.gitbucket import fetch_repos, _cache
from unittest.mock import AsyncMock, MagicMock, patch

@pytest.fixture(autouse=True)
def clear_cache():
    """Clear cache before each test."""
    _cache["data"] = []
    _cache["timestamp"] = 0.0
    yield
    _cache["data"] = []
    _cache["timestamp"] = 0.0

@pytest.mark.asyncio
async def test_fetch_repos_success(mocker):
    # Create mock response
    mock_response = MagicMock()
    mock_response.status_code = 200
    mock_response.json.return_value = [
        {"name": "repo1", "description": "Desc 1"},
        {"name": "repo2", "description": "Desc 2"}
    ]

    # Create mock client that works with async with
    mock_client = MagicMock()
    mock_client.__aenter__ = AsyncMock(return_value=mock_client)
    mock_client.__aexit__ = AsyncMock(return_value=None)
    mock_client.get = AsyncMock(return_value=mock_response)

    # Patch AsyncClient to return our mock
    with patch("httpx.AsyncClient", return_value=mock_client):
        repos = await fetch_repos()
        assert len(repos) == 2
        assert repos[0]["name"] == "repo1"

@pytest.mark.asyncio
async def test_fetch_repos_fallback_on_error(mocker):
    """Test that empty list returned on error when no cache."""
    mock_client = MagicMock()
    mock_client.__aenter__ = AsyncMock(return_value=mock_client)
    mock_client.__aexit__ = AsyncMock(return_value=None)
    mock_client.get = AsyncMock(side_effect=Exception("Connection error"))

    with patch("httpx.AsyncClient", return_value=mock_client):
        repos = await fetch_repos()
        assert repos == []

@pytest.mark.asyncio
async def test_fetch_repos_uses_cache(mocker):
    """Test that cache returns within TTL."""
    mock_response = MagicMock()
    mock_response.status_code = 200
    mock_response.json.return_value = [{"name": "cached_repo", "description": "Cached"}]

    mock_client = MagicMock()
    mock_client.__aenter__ = AsyncMock(return_value=mock_client)
    mock_client.__aexit__ = AsyncMock(return_value=None)
    mock_client.get = AsyncMock(return_value=mock_response)

    with patch("httpx.AsyncClient", return_value=mock_client):
        # First call populates cache
        repos1 = await fetch_repos()
        assert len(repos1) == 1
        assert repos1[0]["name"] == "cached_repo"

        # Second call within TTL should return cached data without calling API again
        repos2 = await fetch_repos()
        assert repos2 == repos1
        # Verify cache was populated
        assert _cache["data"] == repos1
