# pyright: reportUnusedVariable=false
"""
test_qc_report_guard.py — qc_report_guard.py 회귀 테스트 (Guard MVP Phase 1, task-2434)

시나리오 (9개):
    1. test_pass_pass_ok                             — JSON=PASS + frontmatter PASS → ok=True
    2. test_warn_overall_pass_fail                    — JSON=WARN + 본문 OVERALL PASS → ok=False (task-2431)
    3. test_warn_pass_with_warn_ok                   — JSON=WARN + frontmatter PASS_WITH_WARN + 항목 명시 → ok=True
    4. test_warn_pass_with_warn_missing_items_fail    — JSON=WARN + PASS_WITH_WARN 그러나 항목 누락 → ok=False
    5. test_fail_overall_fail_ok                     — JSON=FAIL + 본문 OVERALL FAIL + 사유 → ok=True
    6. test_quoted_overall_pass_ignored              — 인용 블록 가짜 verdict 무시, 실제 PASS_WITH_WARN → ok=True
    7. test_no_verdict_marker_fail                   — frontmatter도 ## QC Verdict도 없음 → ok=False
    8. test_pass_pass_with_warn_mismatch_fail        — JSON=PASS + 보고서 PASS_WITH_WARN → ok=False (MISMATCH)
    9. test_missing_warns_no_uncaught_exception_when_json_pass — JSON=PASS 시 UnboundLocalError 없음

    (통합) test_guard_sh_help                    — guard.sh --help → exit 0
    (통합) test_guard_sh_phase2_3_not_implemented — guard.sh enqueue → exit 2 + "not implemented"
"""

import json
import subprocess
import sys
from pathlib import Path

import pytest

# ---------------------------------------------------------------------------
# 상수
# ---------------------------------------------------------------------------
_SCRIPTS_DIR = Path("/home/jay/workspace/scripts")
_WORKSPACE   = Path("/home/jay/workspace")
_GUARD_QC_PY = _SCRIPTS_DIR / "qc_report_guard.py"
_GUARD_SH    = _SCRIPTS_DIR / "guard.sh"

TASK_ID      = "task-2434-qc-test"

# ---------------------------------------------------------------------------
# skipif 마커
# ---------------------------------------------------------------------------
_QC_MISSING  = not _GUARD_QC_PY.exists()
_SH_MISSING  = not _GUARD_SH.exists()

_skip_qc = pytest.mark.skipif(
    _QC_MISSING,
    reason="qc_report_guard.py not yet implemented (Thor in progress)",
)
_skip_sh = pytest.mark.skipif(
    _SH_MISSING,
    reason="guard.sh not yet implemented (Thor in progress)",
)

# ---------------------------------------------------------------------------
# 헬퍼: 임시 파일 생성
# ---------------------------------------------------------------------------

def _write_qc_result(path: Path, task_id: str, verdict: str,
                     checks_summary: dict | None = None) -> Path:
    """memory/events/<task_id>.qc-result JSON 파일 생성."""
    f = path / "events" / f"{task_id}.qc-result"
    f.parent.mkdir(parents=True, exist_ok=True)
    payload = {
        "task_id": task_id,
        "qc_result": verdict,
        "checks_summary": checks_summary or {},
    }
    f.write_text(json.dumps(payload), encoding="utf-8")
    return f


def _write_report(path: Path, task_id: str, content: str) -> Path:
    """memory/reports/<task_id>.md 파일 생성."""
    f = path / "reports" / f"{task_id}.md"
    f.parent.mkdir(parents=True, exist_ok=True)
    f.write_text(content, encoding="utf-8")
    return f


def _run_check(tmp: Path, task_id: str,
               qc_verdict: str,
               report_content: str | None,
               checks_summary: dict | None = None) -> dict:
    """
    qc_report_guard.py 의 check() 함수를 subprocess CLI로 호출하여
    결과 dict를 반환.  --json-output 플래그로 stdout에 JSON 출력 기대.
    CLI가 없으면 직접 임포트하여 check() 호출.
    """
    ws = tmp / "workspace"
    ws.mkdir(parents=True, exist_ok=True)

    qc_path     = _write_qc_result(ws, task_id, qc_verdict, checks_summary)
    report_path = None
    if report_content is not None:
        report_path = _write_report(ws, task_id, report_content)

    # 직접 임포트 시도
    if str(_SCRIPTS_DIR) not in sys.path:
        sys.path.insert(0, str(_SCRIPTS_DIR))

    import importlib
    mod = importlib.import_module("qc_report_guard")

    result = mod.check(
        task_id=task_id,
        workspace=str(ws),
        qc_result_path=str(qc_path),
        report_path=str(report_path) if report_path else None,
    )
    return result


# ---------------------------------------------------------------------------
# 1. test_pass_pass_ok
# ---------------------------------------------------------------------------

@_skip_qc
def test_pass_pass_ok(tmp_path: Path) -> None:
    """JSON=PASS + frontmatter qc_verdict: PASS → ok=True."""
    report = (
        "---\n"
        "qc_verdict: PASS\n"
        "---\n"
        "# Report\n\n"
        "## QC Verdict\n\nPASS\n"
    )
    result = _run_check(tmp_path, TASK_ID, qc_verdict="PASS",
                        report_content=report)

    assert result["ok"] is True, (
        f"expected ok=True but got ok={result['ok']}, violations={result.get('violations')}"
    )
    assert result["violations"] == [], (
        f"expected no violations but got {result['violations']}"
    )


# ---------------------------------------------------------------------------
# 2. test_warn_overall_pass_fail  (★ task-2431 사고 회귀 가드)
# ---------------------------------------------------------------------------

@_skip_qc
def test_warn_overall_pass_fail(tmp_path: Path) -> None:
    """JSON=WARN + 보고서 본문 '## QC Verdict\\n\\nOVERALL PASS' → ok=False.

    WARN qc-result인데 보고서가 OVERALL PASS로 선언하는 불일치를 반드시 잡아야 한다.
    """
    report = (
        "---\n"
        "title: Some Report\n"
        "---\n"
        "# Report\n\n"
        "## QC Verdict\n\nOVERALL PASS\n"
    )
    result = _run_check(tmp_path, TASK_ID, qc_verdict="WARN",
                        report_content=report)

    assert result["ok"] is False, (
        f"expected ok=False (WARN vs OVERALL PASS mismatch) but got ok={result['ok']}"
    )
    assert len(result["violations"]) > 0, (
        f"expected at least 1 violation but got {result['violations']}"
    )


# ---------------------------------------------------------------------------
# 3. test_warn_pass_with_warn_ok
# ---------------------------------------------------------------------------

@_skip_qc
def test_warn_pass_with_warn_ok(tmp_path: Path) -> None:
    """JSON=WARN + frontmatter PASS_WITH_WARN + 본문에 WARN 항목 명시 → ok=True."""
    report = (
        "---\n"
        "qc_verdict: PASS_WITH_WARN\n"
        "---\n"
        "# Report\n\n"
        "## QC Verdict\n\n"
        "PASS_WITH_WARN\n\n"
        "### WARN Items\n"
        "- WARN: 테스트 커버리지 60% (목표 80% 미달)\n"
    )
    result = _run_check(tmp_path, TASK_ID, qc_verdict="WARN",
                        report_content=report,
                        checks_summary={"테스트 커버리지": "WARN"})

    assert result["ok"] is True, (
        f"expected ok=True (WARN + PASS_WITH_WARN with items) "
        f"but got ok={result['ok']}, violations={result.get('violations')}, "
        f"missing_warns={result.get('missing_warns')}"
    )
    assert result.get("missing_warns", []) == [], (
        f"expected no missing_warns but got {result.get('missing_warns')}"
    )


# ---------------------------------------------------------------------------
# 4. test_warn_pass_with_warn_missing_items_fail
# ---------------------------------------------------------------------------

@_skip_qc
def test_warn_pass_with_warn_missing_items_fail(tmp_path: Path) -> None:
    """JSON=WARN + frontmatter PASS_WITH_WARN 그러나 본문에 WARN 항목 없음 → ok=False."""
    report = (
        "---\n"
        "qc_verdict: PASS_WITH_WARN\n"
        "---\n"
        "# Report\n\n"
        "## QC Verdict\n\n"
        "PASS_WITH_WARN\n\n"
        "모든 항목이 기준을 충족합니다.\n"
        # WARN 항목 누락 — missing_warns 비어있지 않아야 함
    )
    result = _run_check(tmp_path, TASK_ID, qc_verdict="WARN",
                        report_content=report,
                        checks_summary={"문서 업데이트": "WARN"})

    assert result["ok"] is False, (
        f"expected ok=False (PASS_WITH_WARN but no WARN items) "
        f"but got ok={result['ok']}"
    )
    assert len(result.get("missing_warns", [])) > 0 or len(result.get("violations", [])) > 0, (
        f"expected missing_warns or violations but got "
        f"missing_warns={result.get('missing_warns')}, violations={result.get('violations')}"
    )


# ---------------------------------------------------------------------------
# 5. test_fail_overall_fail_ok
# ---------------------------------------------------------------------------

@_skip_qc
def test_fail_overall_fail_ok(tmp_path: Path) -> None:
    """JSON=FAIL + 본문 '## QC Verdict\\n\\nOVERALL FAIL' + 사유 명시 → ok=True.

    FAIL verdict를 정직하게 보고서에도 기재하면 ok=True 여야 한다.
    """
    report = (
        "---\n"
        "qc_verdict: FAIL\n"
        "---\n"
        "# Report\n\n"
        "## QC Verdict\n\n"
        "OVERALL FAIL\n\n"
        "사유: 핵심 테스트 3건 실패\n"
    )
    result = _run_check(tmp_path, TASK_ID, qc_verdict="FAIL",
                        report_content=report)

    assert result["ok"] is True, (
        f"expected ok=True (FAIL honestly reported) "
        f"but got ok={result['ok']}, violations={result.get('violations')}"
    )


# ---------------------------------------------------------------------------
# 6. test_quoted_overall_pass_ignored
# ---------------------------------------------------------------------------

@_skip_qc
def test_quoted_overall_pass_ignored(tmp_path: Path) -> None:
    """인용 블록(> ...) 안의 가짜 OVERALL PASS 무시 — 실제 verdict PASS_WITH_WARN → ok=True.

    보고서에 '> 이전 표현: OVERALL PASS' 인용이 있어도
    실제 ## QC Verdict 섹션이 PASS_WITH_WARN이면 ok=True여야 한다.
    """
    report = (
        "---\n"
        "qc_verdict: PASS_WITH_WARN\n"
        "---\n"
        "# Report\n\n"
        "> 이전 표현: OVERALL PASS\n\n"
        "## QC Verdict\n\n"
        "PASS_WITH_WARN\n\n"
        "### WARN Items\n"
        "- WARN: 문서 업데이트 지연\n"
    )
    result = _run_check(tmp_path, TASK_ID, qc_verdict="WARN",
                        report_content=report,
                        checks_summary={"문서 업데이트": "WARN"})

    assert result["ok"] is True, (
        f"expected ok=True (quoted OVERALL PASS should be ignored) "
        f"but got ok={result['ok']}, violations={result.get('violations')}, "
        f"report_verdict={result.get('report_verdict')}"
    )


# ---------------------------------------------------------------------------
# 7. test_no_verdict_marker_fail
# ---------------------------------------------------------------------------

@_skip_qc
def test_no_verdict_marker_fail(tmp_path: Path) -> None:
    """frontmatter도 ## QC Verdict 섹션도 없으면 ok=False (보고서 정직성 강제)."""
    report = (
        "# 그냥 보고서\n\n"
        "작업 내용만 있고 QC Verdict 선언 없음.\n"
    )
    result = _run_check(tmp_path, TASK_ID, qc_verdict="PASS",
                        report_content=report)

    assert result["ok"] is False, (
        f"expected ok=False (no verdict marker) but got ok={result['ok']}"
    )
    assert result.get("report_verdict") is None or result.get("report_verdict") == "", (
        f"expected report_verdict to be None/empty but got {result.get('report_verdict')}"
    )


# ===========================================================================
# guard.sh 통합 테스트
# ===========================================================================

@_skip_sh
def test_guard_sh_help() -> None:
    """./scripts/guard.sh --help → exit 0 + 사용법 출력."""
    result = subprocess.run(
        ["bash", str(_GUARD_SH), "--help"],
        capture_output=True, text=True,
        cwd=str(_WORKSPACE),
    )
    assert result.returncode == 0, (
        f"expected exit 0 for --help but got rc={result.returncode}\n"
        f"stdout={result.stdout}\nstderr={result.stderr}"
    )
    combined = result.stdout + result.stderr
    assert any(kw in combined.lower() for kw in ("usage", "guard", "scope", "help", "사용")), (
        f"Expected usage info in --help output:\n{combined}"
    )


@_skip_sh
def test_guard_sh_phase2_3_not_implemented() -> None:
    """guard.sh enqueue task-x → exit 2 + stderr에 'not implemented'."""
    for cmd in ("enqueue", "pre-task", "merge-next"):
        result = subprocess.run(
            ["bash", str(_GUARD_SH), cmd, "task-x"],
            capture_output=True, text=True,
            cwd=str(_WORKSPACE),
        )
        assert result.returncode == 2, (
            f"guard.sh {cmd}: expected exit 2 but got rc={result.returncode}\n"
            f"stdout={result.stdout}\nstderr={result.stderr}"
        )
        assert "not implemented" in result.stderr.lower(), (
            f"guard.sh {cmd}: expected 'not implemented' in stderr but got:\n{result.stderr}"
        )


# ===========================================================================
# 추가 시나리오 (Thor PASS_WITH_WARN / UnboundLocal fix 검증)
# ===========================================================================

@_skip_qc
def test_pass_pass_with_warn_mismatch_fail(tmp_path: Path) -> None:
    """JSON=PASS인데 보고서가 PASS_WITH_WARN이면 정직성 위반 — FAIL"""
    qc_path = tmp_path / "qc.json"
    qc_path.write_text('{"qc_result": "PASS", "checks_summary": {}}', encoding="utf-8")
    report_path = tmp_path / "report.md"
    report_path.write_text(
        "---\nqc_verdict: PASS_WITH_WARN\n---\n# 보고서\n\n## QC Verdict\n\nPASS with WARN\n",
        encoding="utf-8",
    )
    from qc_report_guard import check
    result = check("test-x", workspace=str(tmp_path),
                   report_path=str(report_path), qc_result_path=str(qc_path))
    assert result["ok"] is False, f"PASS+PASS_WITH_WARN 은 mismatch여야: {result}"
    assert any("MISMATCH" in v for v in result["violations"])


@_skip_qc
def test_missing_warns_no_uncaught_exception_when_json_pass(tmp_path: Path) -> None:
    """JSON=PASS일 때 missing_warns 분기 들어가지 않아도 UnboundLocalError 없이 동작"""
    qc_path = tmp_path / "qc.json"
    qc_path.write_text('{"qc_result": "PASS", "checks_summary": {"foo": "PASS"}}', encoding="utf-8")
    report_path = tmp_path / "report.md"
    report_path.write_text(
        "---\nqc_verdict: PASS\n---\n# 보고서\n\n## QC Verdict\n\nOVERALL PASS\n",
        encoding="utf-8",
    )
    from qc_report_guard import check
    result = check("test-x", workspace=str(tmp_path),
                   report_path=str(report_path), qc_result_path=str(qc_path))
    assert result["ok"] is True, f"PASS+PASS는 OK여야: {result}"
    assert result["missing_warns"] == []  # UnboundLocal 발생 시 여기서 폭발
