"""extract_followup.py 회귀 테스트 (task-2360)."""

from __future__ import annotations

import importlib.util
import sys
from pathlib import Path

REPO = Path(__file__).resolve().parents[2]
SCRIPT = REPO / "scripts" / "extract_followup.py"


def _load(monkeypatch, workspace: Path):
    """isolated WORKSPACE_ROOT으로 모듈을 새로 로드."""
    monkeypatch.setenv("WORKSPACE_ROOT", str(workspace))
    # 캐시 무효화
    sys.modules.pop("extract_followup", None)
    spec = importlib.util.spec_from_file_location("extract_followup", SCRIPT)
    assert spec and spec.loader
    mod = importlib.util.module_from_spec(spec)
    sys.modules["extract_followup"] = mod
    spec.loader.exec_module(mod)
    return mod


def _setup_workspace(tmp: Path) -> Path:
    (tmp / "memory" / "events").mkdir(parents=True)
    (tmp / "memory" / "reports").mkdir(parents=True)
    (tmp / "memory" / "tasks").mkdir(parents=True)
    (tmp / "logs").mkdir(parents=True)
    return tmp


def test_extract_followup_txt_priority(tmp_path, monkeypatch):
    """followup.txt가 있으면 우선 사용된다."""
    ws = _setup_workspace(tmp_path)
    (ws / "memory" / "events" / "task-1.followup.txt").write_text(
        """## 핵심 결과
테스트.

## 회장 결정 필요
- A vs B
""",
        encoding="utf-8",
    )
    mod = _load(monkeypatch, ws)
    out = mod.extract_followup("task-1")
    assert out["source"] == "followup_txt"
    assert out["core_result"] == "테스트."
    assert out["decisions_needed"] == ["A vs B"]


def test_extract_followup_report_fallback(tmp_path, monkeypatch):
    """followup.txt 없으면 보고서에서 자동 추출."""
    ws = _setup_workspace(tmp_path)
    (ws / "memory" / "reports" / "task-2.md").write_text(
        """# task-2 작업

## 요약 (SCQA)
**A**: 핵심 답변입니다.

## 회장 결정 필요
- 결정 한 건

## 머지 필요
- PR #99

## 미해결
- 없음

## 다음 단계 권장
- task-3
""",
        encoding="utf-8",
    )
    mod = _load(monkeypatch, ws)
    out = mod.extract_followup("task-2")
    assert "report" in out["source"]
    assert "핵심 답변" in (out["core_result"] or "")
    assert out["decisions_needed"] == ["결정 한 건"]
    assert out["merges_needed"] == ["PR #99"]
    assert out["next_steps"] == ["task-3"]


def test_followup_supplements_missing_report_sections(tmp_path, monkeypatch):
    """followup.txt가 일부 섹션만 채우면, 빈 섹션은 보고서에서 보충된다."""
    ws = _setup_workspace(tmp_path)
    (ws / "memory" / "events" / "task-3.followup.txt").write_text(
        """## 회장 결정 필요
- 강조 결정
""",
        encoding="utf-8",
    )
    (ws / "memory" / "reports" / "task-3.md").write_text(
        """# task-3

## 머지 필요
- PR #1

## 회장 결정 필요
- (보고서 결정 — 우선순위 낮음)
""",
        encoding="utf-8",
    )
    mod = _load(monkeypatch, ws)
    out = mod.extract_followup("task-3")
    # followup.txt의 결정이 우선
    assert out["decisions_needed"] == ["강조 결정"]
    # 머지는 보고서에서 보충
    assert out["merges_needed"] == ["PR #1"]


def test_completion_fallback_when_no_other_source(tmp_path, monkeypatch):
    ws = _setup_workspace(tmp_path)
    (ws / "memory" / "events" / "task-4.completion.txt").write_text(
        "단순 완료 메시지", encoding="utf-8"
    )
    mod = _load(monkeypatch, ws)
    out = mod.extract_followup("task-4")
    assert out["source"] == "completion"
    assert out["core_result"] == "단순 완료 메시지"


def test_format_anu_message_truncation(tmp_path, monkeypatch):
    """4000자 초과 시 우선순위 낮은 섹션은 잘린다."""
    ws = _setup_workspace(tmp_path)
    long_items = [f"매우 긴 항목 {i} " + "X" * 200 for i in range(50)]
    (ws / "memory" / "events" / "task-5.followup.txt").write_text(
        "## 회장 결정 필요\n" + "\n".join(f"- {x}" for x in long_items), encoding="utf-8"
    )
    mod = _load(monkeypatch, ws)
    msg, _ = mod.build_message_for_task("task-5")
    assert len(msg) <= mod.TELEGRAM_LIMIT
    assert "상세 보고서: memory/reports/task-5.md" in msg


def test_send_anu_cron_dedupe(tmp_path, monkeypatch):
    """이미 .anu-notified가 있으면 발송 스킵."""
    ws = _setup_workspace(tmp_path)
    (ws / "memory" / "events" / "task-6.anu-notified").write_text("", encoding="utf-8")
    mod = _load(monkeypatch, ws)
    ok, status = mod.send_anu_cron("task-6", "msg", dry_run=True)
    assert not ok
    assert status == "already-notified"


def test_at_format_uses_space_separator(tmp_path, monkeypatch):
    """cokacdir 호환 — 'YYYY-MM-DD HH:MM:SS' 형식 (T 구분자 X)."""
    ws = _setup_workspace(tmp_path)
    mod = _load(monkeypatch, ws)
    at = mod._at_time_seconds(60)
    assert "T" not in at
    assert " " in at
    assert len(at) == 19


def test_format_message_includes_all_sections(tmp_path, monkeypatch):
    ws = _setup_workspace(tmp_path)
    items = {
        "core_result": "결과",
        "decisions_needed": ["d1"],
        "merges_needed": ["m1"],
        "unresolved": ["u1"],
        "next_steps": ["n1"],
    }
    mod = _load(monkeypatch, ws)
    msg = mod.format_anu_message("task-7", "dev7-team", "bot-h", items)
    for s in ["핵심 결과", "회장 결정 필요", "머지 필요", "미해결", "다음 단계 권장"]:
        assert s in msg
    assert "task-7" in msg
    assert "dev7-team" in msg
    assert "bot-h" in msg
    assert "memory/reports/task-7.md" in msg


def test_followup_without_headers_treated_as_core_result(tmp_path, monkeypatch):
    """헤더 없는 followup.txt는 전체를 core_result로 취급."""
    ws = _setup_workspace(tmp_path)
    (ws / "memory" / "events" / "task-8.followup.txt").write_text(
        "이건 그냥 한 줄 메모.\n핵심 결과만 있음.", encoding="utf-8"
    )
    mod = _load(monkeypatch, ws)
    out = mod.extract_followup("task-8")
    assert out["source"] == "followup_txt"
    assert out["core_result"] is not None
    assert "한 줄 메모" in out["core_result"]
