"""StockAgent.on_screener_schedule — 평일 16:30 KST 자동 잡 단위 테스트.

stock HTTP 호출은 service_proxy mock, 텔레그램은 messaging.send_raw mock.
"""
import os
import sys
import tempfile

_fd, _TMP = tempfile.mkstemp(suffix=".db")
os.close(_fd)
os.unlink(_TMP)
os.environ["AGENT_OFFICE_DB_PATH"] = _TMP

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))


import asyncio
from unittest.mock import AsyncMock, patch
import pytest


@pytest.fixture(autouse=True)
def _init_db():
    import gc
    gc.collect()
    if os.path.exists(_TMP):
        os.remove(_TMP)
    from app.db import init_db
    init_db()
    yield
    gc.collect()


def _success_body(asof="2026-05-12"):
    return {
        "asof": asof,
        "mode": "auto",
        "status": "success",
        "run_id": 42,
        "survivors_count": 600,
        "top_n": 20,
        "results": [],
        "telegram_payload": {
            "chat_target": "default",
            "parse_mode": "MarkdownV2",
            "text": "*KRX 강세주 스크리너* test body",
        },
        "warnings": [],
    }


def _holiday_body(asof="2026-05-05"):
    return {
        "asof": asof,
        "mode": "auto",
        "status": "skipped_holiday",
        "run_id": None,
        "survivors_count": None,
        "top_n": 0,
        "results": [],
        "telegram_payload": None,
        "warnings": [f"{asof} is a holiday — skipped"],
    }


def test_screener_success_sends_markdownv2_telegram():
    from app.agents.stock import StockAgent
    from app import service_proxy
    from app.telegram import messaging

    fake_snap = AsyncMock(return_value={"status": "ok"})
    fake_run = AsyncMock(return_value=_success_body())
    fake_send = AsyncMock(return_value={"ok": True, "message_id": 7777})

    with patch.object(service_proxy, "refresh_screener_snapshot", fake_snap), \
         patch.object(service_proxy, "run_stock_screener", fake_run), \
         patch.object(messaging, "send_raw", fake_send):
        agent = StockAgent()
        asyncio.run(agent.on_screener_schedule())

    fake_snap.assert_awaited_once()
    fake_run.assert_awaited_once_with(mode="auto")
    fake_send.assert_awaited_once()
    args, kwargs = fake_send.call_args
    # 첫 인자(text) 또는 kwargs로 전달
    text = args[0] if args else kwargs.get("text")
    assert "KRX 강세주 스크리너" in text
    assert kwargs.get("parse_mode") == "MarkdownV2"
    assert agent.state == "idle"


def test_screener_holiday_skips_telegram():
    from app.agents.stock import StockAgent
    from app import service_proxy
    from app.telegram import messaging

    fake_snap = AsyncMock(return_value={"status": "skipped_weekend"})
    fake_run = AsyncMock(return_value=_holiday_body())
    fake_send = AsyncMock(return_value={"ok": True, "message_id": 1})

    with patch.object(service_proxy, "refresh_screener_snapshot", fake_snap), \
         patch.object(service_proxy, "run_stock_screener", fake_run), \
         patch.object(messaging, "send_raw", fake_send):
        agent = StockAgent()
        asyncio.run(agent.on_screener_schedule())

    fake_run.assert_awaited_once()
    # 휴일이면 텔레그램 미발신
    fake_send.assert_not_awaited()
    assert agent.state == "idle"


def test_screener_snapshot_failure_still_runs_screener():
    """스냅샷 실패는 경고만 남기고 screener 호출은 계속됨."""
    from app.agents.stock import StockAgent
    from app import service_proxy
    from app.telegram import messaging

    fake_snap = AsyncMock(side_effect=RuntimeError("snapshot upstream down"))
    fake_run = AsyncMock(return_value=_success_body())
    fake_send = AsyncMock(return_value={"ok": True, "message_id": 8888})

    with patch.object(service_proxy, "refresh_screener_snapshot", fake_snap), \
         patch.object(service_proxy, "run_stock_screener", fake_run), \
         patch.object(messaging, "send_raw", fake_send):
        agent = StockAgent()
        asyncio.run(agent.on_screener_schedule())

    fake_snap.assert_awaited_once()
    fake_run.assert_awaited_once_with(mode="auto")
    fake_send.assert_awaited_once()


def test_screener_run_failure_notifies_operator():
    """screener/run 실패 시 운영자 알림 텔레그램 발송."""
    from app.agents.stock import StockAgent
    from app import service_proxy
    from app.telegram import messaging

    fake_snap = AsyncMock(return_value={"status": "ok"})
    fake_run = AsyncMock(side_effect=RuntimeError("stock 500"))
    fake_send = AsyncMock(return_value={"ok": True, "message_id": 1})

    with patch.object(service_proxy, "refresh_screener_snapshot", fake_snap), \
         patch.object(service_proxy, "run_stock_screener", fake_run), \
         patch.object(messaging, "send_raw", fake_send):
        agent = StockAgent()
        asyncio.run(agent.on_screener_schedule())

    # 운영자 알림 1회는 호출
    assert fake_send.await_count == 1
    args, kwargs = fake_send.call_args
    text = args[0] if args else kwargs.get("text")
    assert "스크리너 실패" in text
    assert agent.state == "idle"


def test_screener_unexpected_status_treated_as_failure():
    from app.agents.stock import StockAgent
    from app import service_proxy
    from app.telegram import messaging

    fake_snap = AsyncMock(return_value={"status": "ok"})
    fake_run = AsyncMock(return_value={"status": "weird", "asof": "2026-05-12"})
    fake_send = AsyncMock(return_value={"ok": True, "message_id": 1})

    with patch.object(service_proxy, "refresh_screener_snapshot", fake_snap), \
         patch.object(service_proxy, "run_stock_screener", fake_run), \
         patch.object(messaging, "send_raw", fake_send):
        agent = StockAgent()
        asyncio.run(agent.on_screener_schedule())

    # 운영자 알림 1회 + screener payload 미발송
    assert fake_send.await_count == 1
    args, kwargs = fake_send.call_args
    text = args[0] if args else kwargs.get("text")
    assert "스크리너 실패" in text
