"""Tests for app/services/ai_summary.py"""

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

import pytest

from app.services import ai_summary


@pytest.fixture(autouse=True)
def clear_cache():
    """Clear the module-level CACHE dict before each test."""
    ai_summary.CACHE.clear()


# =============================================================================
# _call_ai tests
# =============================================================================


@pytest.mark.asyncio
async def test_call_ai_no_key():
    """When AI_API_KEY is None, _call_ai returns None immediately."""
    with patch("app.services.ai_summary.AI_API_KEY", None):
        result = await ai_summary._call_ai("test prompt")
    assert result is None


@pytest.mark.asyncio
async def test_call_ai_success():
    """Mock httpx.AsyncClient to return success JSON, verify content returned."""
    mock_response = MagicMock()
    mock_response.raise_for_status.return_value = None
    mock_response.json.return_value = {
        "choices": [{"message": {"content": "  test response content  "}}]
    }

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

    with patch("app.services.ai_summary.AI_API_KEY", "test-key"), \
         patch("httpx.AsyncClient", return_value=mock_client):
        result = await ai_summary._call_ai("test prompt")

    assert result == "test response content"


@pytest.mark.asyncio
async def test_call_ai_http_error():
    """Mock httpx to raise HTTP error, verify _call_ai returns None."""
    mock_client = MagicMock()
    mock_client.__aenter__.return_value = mock_client
    mock_client.__aexit__.return_value = None
    mock_client.post.side_effect = Exception("Connection error")

    with patch("app.services.ai_summary.AI_API_KEY", "test-key"), \
         patch("httpx.AsyncClient", return_value=mock_client):
        result = await ai_summary._call_ai("test prompt")

    assert result is None


# =============================================================================
# generate_one_liner tests
# =============================================================================


@pytest.mark.asyncio
async def test_generate_one_liner_no_abstract():
    """Returns None immediately when abstract is None."""
    result = await ai_summary.generate_one_liner("Some title", None)
    assert result is None


@pytest.mark.asyncio
async def test_generate_one_liner_success():
    """Mock _call_ai, verify result is returned and cached."""
    mock_result = "这项研究显示新疗法显著提高生存率"
    with patch("app.services.ai_summary._call_ai", new=AsyncMock(return_value=mock_result)):
        result = await ai_summary.generate_one_liner(
            "Test title", "Test abstract about cancer"
        )

    assert result == mock_result

    # Verify cache was populated
    cache_key = hashlib.md5(
        "Test abstract about cancer".encode("utf-8")[:500]
    ).hexdigest()
    assert cache_key in ai_summary.CACHE
    assert ai_summary.CACHE[cache_key] == mock_result


@pytest.mark.asyncio
async def test_generate_one_liner_cached():
    """Call twice with same abstract -- second call uses cache, _call_ai called once."""
    mock_call_ai = AsyncMock(return_value="cached summary")
    with patch("app.services.ai_summary._call_ai", mock_call_ai):
        result1 = await ai_summary.generate_one_liner(
            "Title A", "Same abstract content"
        )
        result2 = await ai_summary.generate_one_liner(
            "Title B", "Same abstract content"
        )

    assert result1 == "cached summary"
    assert result2 == "cached summary"
    mock_call_ai.assert_awaited_once()


# =============================================================================
# generate_structured_summary tests
# =============================================================================


@pytest.mark.asyncio
async def test_generate_structured_summary_no_abstract():
    """Returns None immediately when abstract is None."""
    result = await ai_summary.generate_structured_summary("Some title", None)
    assert result is None


@pytest.mark.asyncio
async def test_generate_structured_summary_success():
    """Mock _call_ai, verify structured summary is returned."""
    mock_result = "背景：...\n方法：...\n结果：...\n结论：..."
    with patch(
        "app.services.ai_summary._call_ai", new=AsyncMock(return_value=mock_result)
    ):
        result = await ai_summary.generate_structured_summary(
            "Test title",
            "Test abstract about cancer research methods and results",
        )

    assert result == mock_result


# =============================================================================
# translate_text tests
# =============================================================================


@pytest.mark.asyncio
async def test_translate_text_en2zh():
    """Mock _call_ai, verify translation from English to Chinese."""
    mock_result = "中文翻译结果"
    with patch(
        "app.services.ai_summary._call_ai", new=AsyncMock(return_value=mock_result)
    ):
        result = await ai_summary.translate_text(
            "Lung cancer treatment advances", "en2zh"
        )

    assert result == mock_result


@pytest.mark.asyncio
async def test_translate_text_zh2en():
    """Mock _call_ai, verify translation from Chinese to English."""
    mock_result = "English translation result"
    with patch(
        "app.services.ai_summary._call_ai", new=AsyncMock(return_value=mock_result)
    ):
        result = await ai_summary.translate_text("肺癌治疗进展", "zh2en")

    assert result == mock_result


# =============================================================================
# generate_clinical_implication tests
# =============================================================================


@pytest.mark.asyncio
async def test_generate_clinical_implication_no_abstract():
    """Returns None immediately when abstract is None."""
    result = await ai_summary.generate_clinical_implication("Some title", None)
    assert result is None


# =============================================================================
# batch_summarize tests
# =============================================================================


@pytest.mark.asyncio
async def test_batch_summarize_one_liner():
    """batch_summarize with 2 articles, one_liner mode."""
    articles = [
        {"title": "Study A", "abstract": "Abstract A about lung cancer treatment"},
        {
            "title": "Study B",
            "abstract": "Abstract B about breast cancer biomarkers",
        },
    ]
    mock_result = "单项研究总结"
    with patch(
        "app.services.ai_summary._call_ai", new=AsyncMock(return_value=mock_result)
    ):
        results = await ai_summary.batch_summarize(articles, mode="one_liner")

    assert len(results) == 2
    assert results[0]["ai_summary"] == mock_result
    assert results[1]["ai_summary"] == mock_result


@pytest.mark.asyncio
async def test_batch_summarize_structured():
    """batch_summarize with 2 articles, structured mode."""
    articles = [
        {"title": "Study A", "abstract": "Abstract A content"},
        {"title": "Study B", "abstract": "Abstract B content"},
    ]
    mock_result = "结构化摘要结果"
    with patch(
        "app.services.ai_summary._call_ai", new=AsyncMock(return_value=mock_result)
    ):
        results = await ai_summary.batch_summarize(articles, mode="structured")

    assert len(results) == 2
    assert results[0]["ai_summary"] == mock_result
    assert results[1]["ai_summary"] == mock_result


@pytest.mark.asyncio
async def test_batch_summarize_translate():
    """batch_summarize with 2 articles, translate mode."""
    articles = [
        {"title": "Study Title A", "abstract": "Abstract content A"},
        {"title": "Study Title B", "abstract": "Abstract content B"},
    ]
    mock_result = "翻译结果"
    with patch(
        "app.services.ai_summary._call_ai", new=AsyncMock(return_value=mock_result)
    ):
        results = await ai_summary.batch_summarize(articles, mode="translate")

    assert len(results) == 2
    assert results[0]["title_zh"] == mock_result
    assert results[0]["abstract_zh"] == mock_result
    assert results[1]["title_zh"] == mock_result
    assert results[1]["abstract_zh"] == mock_result


@pytest.mark.asyncio
async def test_batch_summarize_implication():
    """batch_summarize with 2 articles, implication mode."""
    articles = [
        {
            "title": "Study A",
            "abstract": "Abstract A about clinical practice",
        },
        {
            "title": "Study B",
            "abstract": "Abstract B about treatment guidelines",
        },
    ]
    mock_result = "临床意义解读"
    with patch(
        "app.services.ai_summary._call_ai", new=AsyncMock(return_value=mock_result)
    ):
        results = await ai_summary.batch_summarize(articles, mode="implication")

    assert len(results) == 2
    assert results[0]["clinical_implication"] == mock_result
    assert results[1]["clinical_implication"] == mock_result
