"""Citation updater service tests"""
from unittest.mock import AsyncMock, MagicMock, patch

import httpx
import pytest


@pytest.mark.asyncio
async def test_update_citation_counts_empty():
    """Empty lit_ids -> returns 0"""
    from app.services.citation_updater import update_citation_counts
    db = AsyncMock()
    result = await update_citation_counts(db, [])
    assert result == 0


@pytest.mark.asyncio
async def test_update_citation_counts_success():
    """Batch query with valid API response -> updates cited_by_count"""
    db = AsyncMock()
    mock_json = {
        "resultList": {
            "result": [
                {"extId": "39000101", "citedByCount": 42},
                {"extId": "39000102", "citedByCount": 7},
            ]
        }
    }
    mock_response = MagicMock(spec=httpx.Response)
    mock_response.status_code = 200
    mock_response.json.return_value = mock_json

    with patch("app.services.citation_updater.httpx.AsyncClient") as mock_cls:
        mock_instance = AsyncMock()
        mock_cls.return_value.__aenter__.return_value = mock_instance
        mock_instance.get.return_value = mock_response

        from app.services.citation_updater import update_citation_counts
        result = await update_citation_counts(db, [39000101, 39000102])
        assert result == 2
        assert db.execute.call_count >= 2


@pytest.mark.asyncio
async def test_update_citation_counts_retry_then_succeed():
    """429 -> retries -> succeeds"""
    fail_response = MagicMock(spec=httpx.Response)
    fail_response.status_code = 429
    fail_response.raise_for_status.side_effect = httpx.HTTPStatusError("Too Many", request=MagicMock(), response=fail_response)

    ok_response = MagicMock(spec=httpx.Response)
    ok_response.status_code = 200
    ok_response.json.return_value = {"resultList": {"result": [{"extId": "39000101", "citedByCount": 5}]}}

    db = AsyncMock()
    with patch("app.services.citation_updater.httpx.AsyncClient") as mock_cls:
        mock_instance = AsyncMock()
        mock_cls.return_value.__aenter__.return_value = mock_instance
        mock_instance.get.side_effect = [fail_response, ok_response]

        from app.services.citation_updater import update_citation_counts
        result = await update_citation_counts(db, [39000101])
        assert result == 1
        assert mock_instance.get.call_count == 2


@pytest.mark.asyncio
async def test_update_citation_counts_retry_exhausted():
    """All retries exhausted -> continues to next batch"""
    fail_response = MagicMock(spec=httpx.Response)
    fail_response.status_code = 503
    fail_response.raise_for_status.side_effect = httpx.HTTPStatusError("Unavailable", request=MagicMock(), response=fail_response)

    db = AsyncMock()
    with patch("app.services.citation_updater.httpx.AsyncClient") as mock_cls:
        mock_instance = AsyncMock()
        mock_cls.return_value.__aenter__.return_value = mock_instance
        mock_instance.get.return_value = fail_response

        from app.services.citation_updater import update_citation_counts
        result = await update_citation_counts(db, [39000101])
        assert result == 0  # error caught, continues


@pytest.mark.asyncio
async def test_fetch_with_retry_success():
    """_fetch_with_retry succeeds on first try"""
    from app.services.citation_updater import _fetch_with_retry
    client = AsyncMock(spec=httpx.AsyncClient)
    mock_resp = MagicMock(spec=httpx.Response)
    mock_resp.status_code = 200
    mock_resp.json.return_value = {"key": "value"}
    client.get.return_value = mock_resp

    result = await _fetch_with_retry(client, "https://example.com", {"q": "test"})
    assert result == {"key": "value"}
    client.get.assert_awaited_once()


@pytest.mark.asyncio
async def test_fetch_with_retry_non_retryable():
    """400 raises immediately without retry"""
    from app.services.citation_updater import _fetch_with_retry
    client = AsyncMock(spec=httpx.AsyncClient)
    mock_resp = MagicMock(spec=httpx.Response)
    mock_resp.status_code = 400
    mock_resp.raise_for_status.side_effect = httpx.HTTPStatusError("Bad Request", request=MagicMock(), response=mock_resp)
    client.get.return_value = mock_resp

    with pytest.raises(httpx.HTTPStatusError):
        await _fetch_with_retry(client, "https://example.com", {"q": "test"})
    client.get.assert_awaited_once()


@pytest.mark.asyncio
async def test_fetch_with_retry_timeout_then_succeed():
    """Timeout -> retries -> succeeds"""
    from app.services.citation_updater import _fetch_with_retry
    client = AsyncMock(spec=httpx.AsyncClient)
    success_resp = MagicMock(spec=httpx.Response)
    success_resp.status_code = 200
    success_resp.json.return_value = {"key": "value"}
    client.get.side_effect = [httpx.TimeoutException("timeout"), success_resp]

    result = await _fetch_with_retry(client, "https://example.com", {"q": "test"})
    assert result == {"key": "value"}
    assert client.get.call_count == 2


__all__ = []
