import json
import pytest
from unittest.mock import AsyncMock, MagicMock

from app.screener.ai_news import analyzer


def _mk_llm(content_text: str, in_tokens: int = 100, out_tokens: int = 20):
    llm = AsyncMock()
    resp = MagicMock()
    block = MagicMock()
    block.text = content_text
    resp.content = [block]
    resp.usage = MagicMock(input_tokens=in_tokens, output_tokens=out_tokens)
    llm.messages = MagicMock()
    llm.messages.create = AsyncMock(return_value=resp)
    return llm


NEWS = [
    {"title": "삼성전자, HBM 양산", "summary": "1분기 영업이익 사상 최대", "pub_date": "2026-05-14"},
    {"title": "메모리 가격 반등", "summary": "", "pub_date": "2026-05-14"},
]


@pytest.mark.asyncio
async def test_score_sentiment_success_parses_json():
    llm = _mk_llm(json.dumps({"score": 7.5, "reason": "HBM 호재"}))
    out = await analyzer.score_sentiment(llm, "005930", NEWS, name="삼성전자")
    assert out["ticker"] == "005930"
    assert out["score_raw"] == 7.5
    assert out["reason"] == "HBM 호재"
    assert out["news_count"] == 2
    assert out["tokens_input"] == 100
    assert out["tokens_output"] == 20


@pytest.mark.asyncio
async def test_score_sentiment_json_parse_fail_returns_zero():
    llm = _mk_llm("not valid json")
    out = await analyzer.score_sentiment(llm, "005930", NEWS)
    assert out["score_raw"] == 0.0
    assert "parse fail" in out["reason"]
    assert out["tokens_input"] == 100  # 호출은 발생했음


@pytest.mark.asyncio
async def test_score_sentiment_clamps_out_of_range():
    llm = _mk_llm(json.dumps({"score": 15.0, "reason": "초강세"}))
    out = await analyzer.score_sentiment(llm, "005930", NEWS)
    assert out["score_raw"] == 10.0  # +10 클램프


@pytest.mark.asyncio
async def test_score_sentiment_clamps_negative_out_of_range():
    llm = _mk_llm(json.dumps({"score": -42.0, "reason": "초악재"}))
    out = await analyzer.score_sentiment(llm, "005930", NEWS)
    assert out["score_raw"] == -10.0


@pytest.mark.asyncio
async def test_score_sentiment_includes_asof_date_in_prompt():
    """asof(현재 KST 일자)를 넘기면 prompt에 오늘 날짜가 포함되어 LLM이 현재 일자 기준으로 판단."""
    import datetime as _dt
    llm = _mk_llm(json.dumps({"score": 5.0, "reason": "ok"}))
    await analyzer.score_sentiment(
        llm, "005930", NEWS, name="삼성전자", asof=_dt.date(2026, 7, 2),
    )
    user_msg = llm.messages.create.call_args.kwargs["messages"][0]["content"]
    assert "2026-07-02" in user_msg


@pytest.mark.asyncio
async def test_score_sentiment_includes_summary_in_prompt():
    """summary 가 있으면 prompt 에 포함, 없으면 title 만."""
    llm = _mk_llm(json.dumps({"score": 5.0, "reason": "ok"}))
    await analyzer.score_sentiment(llm, "005930", NEWS, name="삼성전자")
    call = llm.messages.create.call_args
    user_msg = call.kwargs["messages"][0]["content"]
    assert "1분기 영업이익 사상 최대" in user_msg  # summary 포함
    assert "삼성전자, HBM 양산" in user_msg  # title 포함
    assert "2026-05-14" in user_msg  # pub_date 포함
