"""eid 약점 판정/포맷 순수 함수 테스트 (stdlib only, venv 불필요). W3-2.

실행: python3 tests/eid/test_weakness_compute.py  (또는 pytest)
"""

from __future__ import annotations

import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "app"))

from services.study.weakness_compute import (  # noqa: E402
    decide_tier,
    format_habit_block,
    format_weakness_block,
    overall_trend,
    topic_trend,
)

# worker 임계 미러 (테스트 고정값)
TH = dict(min_attempts=5, chronic_focus=3, relapse_focus=2, review_overdue=5)


def test_decide_tier_focus_on_chronic():
    assert decide_tier(chronic=3, relapsed=0, overdue=0, unsure=0, attempted=20, **TH) == "focus"


def test_decide_tier_focus_on_relapse():
    assert decide_tier(chronic=0, relapsed=2, overdue=0, unsure=0, attempted=20, **TH) == "focus"


def test_decide_tier_review_on_single_chronic():
    assert decide_tier(chronic=1, relapsed=0, overdue=0, unsure=0, attempted=20, **TH) == "review"


def test_decide_tier_review_on_overdue():
    assert decide_tier(chronic=0, relapsed=0, overdue=5, unsure=0, attempted=20, **TH) == "review"


def test_decide_tier_shallow_caps_to_watch():
    # 표본 미달(attempted<5) → chronic 많아도 focus/review 단정 안 함, watch 상한 (conservative)
    assert decide_tier(chronic=4, relapsed=3, overdue=9, unsure=0, attempted=3, **TH) == "watch"


def test_decide_tier_watch_on_unsure():
    assert decide_tier(chronic=0, relapsed=0, overdue=0, unsure=2, attempted=10, **TH) == "watch"


def test_decide_tier_none_when_clean():
    assert decide_tier(chronic=0, relapsed=0, overdue=0, unsure=0, attempted=20, **TH) is None


def test_topic_trend():
    assert topic_trend([]) == "정체"
    assert topic_trend([{"newly_correct": 10, "relapsed": 1, "chronic_remaining": 1}]) == "개선"
    assert topic_trend([{"newly_correct": 1, "relapsed": 5, "chronic_remaining": 4}]) == "악화"
    assert topic_trend([{"newly_correct": 3, "relapsed": 2, "chronic_remaining": 1}]) == "정체"


def test_overall_trend_majority():
    assert overall_trend([]) == "정체"
    assert overall_trend(["악화", "악화", "개선"]) == "악화"
    assert overall_trend(["개선", "개선", "악화"]) == "개선"
    assert overall_trend(["개선", "악화"]) == "정체"  # 동률


def test_format_weakness_block_empty_guards():
    out = format_weakness_block([], shallow_overall=False)
    assert "약점으로 판정된 토픽 없음" in out
    assert "추정하지 마라" in out  # 환각 약점 차단 문구


def test_format_weakness_block_content_and_shallow():
    ws = [{"topic": "가스설비", "chronic": 4, "relapsed": 1, "unsure": 2,
           "coverage_gap": 7, "overdue": 3, "trend": "악화", "tier": "focus"}]
    out = format_weakness_block(ws, shallow_overall=True)
    assert "가스설비" in out and "tier=focus" in out and "추세 악화" in out
    assert "표본 적음" in out  # shallow 주석


def test_format_habit_block():
    out = format_habit_block({
        "avoidance_topics": ["배관", "연소"], "session_abandon_rate": 0.25,
        "stale_due_count": 12, "skew_topic": "배관",
    })
    assert "배관" in out and "25%" in out and "12건" in out and "편중" in out


def _run():
    fns = [v for k, v in sorted(globals().items()) if k.startswith("test_")]
    fails = 0
    for fn in fns:
        try:
            fn(); print(f"  PASS {fn.__name__}")
        except Exception as exc:  # noqa: BLE001
            fails += 1; print(f"  FAIL {fn.__name__}: {type(exc).__name__}: {exc}")
    print(f"\n{len(fns) - fails}/{len(fns)} passed")
    return 1 if fails else 0


if __name__ == "__main__":
    raise SystemExit(_run())
