"""tests/regression/test_dashboard_report_hardening_2487.py — task-2487 회귀 (Group C, task-2547 corrected).

dashboard/helpers.py + report_parser.py 의 V2 + legacy dot-phase 호환 hardening 검증.

검증 대상:
- dashboard/helpers.py        : get_records_list 의 task-ID 접두사 제거 패턴 3개
- report_parser.py            : task_id 추출 + merge_branch 추출 패턴
"""
from __future__ import annotations

import importlib.util
import re
import sys
import tempfile
from pathlib import Path

WORKSPACE = Path(__file__).resolve().parents[2]


def _load_module(mod_name: str, file_rel: str):
    file_path = WORKSPACE / file_rel
    spec = importlib.util.spec_from_file_location(mod_name, str(file_path))
    if spec is None or spec.loader is None:
        raise ImportError(f"cannot load spec for {file_path}")
    module = importlib.util.module_from_spec(spec)
    sys.modules[mod_name] = module
    spec.loader.exec_module(module)
    return module


# --- Source level regex inspection (dashboard/helpers.py 직접 import 는 무거우므로 회피) ---


def test_dashboard_helpers_v2_pattern_in_first_desc():
    """get_records_list 의 first_desc task-ID 접두사 제거 패턴이 V2 retry/_phase 인식."""
    src = (WORKSPACE / "dashboard" / "helpers.py").read_text(encoding="utf-8")
    # task-2487+1 같은 retry suffix 가 패턴에 포함되어야 함
    assert "(?:\\+\\d+)?" in src, "dashboard/helpers.py V2 retry suffix 패턴 누락"


def test_dashboard_helpers_preserves_task_2543_mtime_fallback():
    """task-2543 mtime fallback off 변경이 보존되어야 한다.

    invariant: dashboard.helpers.get_records_list 내부 모든 _resolve_end_time_priority
    호출이 fallback_to_mtime=False 키워드를 명시해야 한다. (task-2543 § 5/9 14:32
    working-tree 일괄 갱신 회귀 방지 — mtime fallback 사용 시 reconcile 결과 손상)
    AST 기반 검사로 vacuous assertion 회피.
    """
    import ast

    src = (WORKSPACE / "dashboard" / "helpers.py").read_text(encoding="utf-8")
    tree = ast.parse(src)

    target_fn = None
    for node in ast.walk(tree):
        if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) and node.name == "get_records_list":
            target_fn = node
            break
    assert target_fn is not None, "get_records_list 함수를 dashboard/helpers.py에서 찾지 못함"

    resolve_calls = []
    for node in ast.walk(target_fn):
        if isinstance(node, ast.Call):
            func = node.func
            call_name = func.attr if isinstance(func, ast.Attribute) else getattr(func, "id", None)
            if call_name == "_resolve_end_time_priority":
                resolve_calls.append(node)

    assert resolve_calls, (
        "task-2543 회귀: get_records_list 내부에서 _resolve_end_time_priority 호출이 사라짐 — "
        "end_time 우선순위 라우팅 손상 가능성"
    )

    for call in resolve_calls:
        kw = {k.arg: k.value for k in call.keywords if k.arg}
        assert "fallback_to_mtime" in kw, (
            "task-2543 회귀 catch 불능: _resolve_end_time_priority 호출에 "
            "fallback_to_mtime 키워드가 명시되지 않음 (암묵 기본값 의존 금지)"
        )
        val_node = kw["fallback_to_mtime"]
        observed = val_node.value if isinstance(val_node, ast.Constant) else val_node
        assert observed is False, (
            "task-2543 회귀: _resolve_end_time_priority(fallback_to_mtime="
            f"{observed!r}) — False 강제 필요 (5/9 14:32 mtime 일괄 갱신 회귀 방지)"
        )


def test_report_parser_v2_pattern_in_title_extraction():
    """report_parser.parse_report 가 # task-2487+1 헤더에서 V2 task_id 를 추출한다."""
    rp = _load_module("rp_2487", "report_parser.py")
    with tempfile.NamedTemporaryFile(mode="w", suffix=".md", delete=False, encoding="utf-8") as tf:
        tf.write("# task-2487+1 — sample title\n\n- **팀**: dev2-team\n")
        tmp_path = tf.name
    try:
        out = rp.parse_report(tmp_path)
        assert out["task_id"] == "task-2487+1", f"V2 retry suffix 추출 실패: {out}"
    finally:
        Path(tmp_path).unlink(missing_ok=True)


def test_report_parser_legacy_pattern_in_title_extraction():
    """report_parser.parse_report 가 legacy # task-1234.5 헤더에서도 task_id 를 추출한다."""
    rp = _load_module("rp_2487_legacy", "report_parser.py")
    with tempfile.NamedTemporaryFile(mode="w", suffix=".md", delete=False, encoding="utf-8") as tf:
        tf.write("# task-1234.5 — legacy title\n\n- **팀**: dev2-team\n")
        tmp_path = tf.name
    try:
        out = rp.parse_report(tmp_path)
        # V2 anchored pattern 은 1234 부분만 잡고 .5 는 못 잡음.
        # 그러나 우선순위 1은 **작업 ID** 필드, 그 다음 # 헤더, 그 다음 파일명.
        # 파일명 fallback 으로도 task-1234.5 추출 가능해야 함.
        # 또한 # 헤더 task-\d+(?:\.\d+)? 으로 task-1234.5 도 매칭 가능.
        assert out["task_id"] is not None
        assert out["task_id"].startswith("task-1234"), f"legacy task_id 추출 실패: {out}"
    finally:
        Path(tmp_path).unlink(missing_ok=True)


def test_report_parser_merge_branch_v2_pattern():
    """report_parser.parse_report 가 V2 branch task/task-2487+1-dev2 를 추출한다."""
    rp = _load_module("rp_2487_branch", "report_parser.py")
    with tempfile.NamedTemporaryFile(mode="w", suffix=".md", delete=False, encoding="utf-8") as tf:
        tf.write(
            "# task-2487+1 — merge sample\n\n"
            "- **머지 브랜치**: task/task-2487+1-dev2\n"
            "- **상태**: 머지 필요\n"
        )
        tmp_path = tf.name
    try:
        out = rp.parse_report(tmp_path)
        assert out.get("merge_branch") == "task/task-2487+1-dev2", (
            f"V2 retry suffix branch 추출 실패: {out.get('merge_branch')}"
        )
    finally:
        Path(tmp_path).unlink(missing_ok=True)


def test_report_parser_merge_branch_legacy_pattern():
    """report_parser.parse_report 가 legacy task/task-1234.5-dev1 branch 도 추출한다."""
    rp = _load_module("rp_2487_branch_legacy", "report_parser.py")
    with tempfile.NamedTemporaryFile(mode="w", suffix=".md", delete=False, encoding="utf-8") as tf:
        tf.write(
            "# task-1234.5 — legacy merge\n\n"
            "- **머지 브랜치**: task/task-1234.5-dev1\n"
        )
        tmp_path = tf.name
    try:
        out = rp.parse_report(tmp_path)
        assert out.get("merge_branch") == "task/task-1234.5-dev1", (
            f"legacy branch 추출 실패: {out.get('merge_branch')}"
        )
    finally:
        Path(tmp_path).unlink(missing_ok=True)


def test_dashboard_helpers_retry_suffix_strip():
    """dashboard/helpers.py 의 prefix-strip regex 가 task-2487+1: 를 제거하는지 직접 검증."""
    src = (WORKSPACE / "dashboard" / "helpers.py").read_text(encoding="utf-8")
    # corrected hardening 패턴 marker (main은 (?:\.\d+)* 또는 (?:\.\d+)? 둘 다 허용)
    pat_marker_q = "task-\\d+(?:\\.\\d+)?(?:_\\d+\\.\\d+)?(?:_[a-z])?(?:\\+\\d+)?"
    pat_marker_star = "task-\\d+(?:\\.\\d+)*(?:_\\d+\\.\\d+)?(?:_[a-z])?(?:\\+\\d+)?"
    assert pat_marker_q in src or pat_marker_star in src, (
        "dashboard/helpers.py V2 task-ID strip pattern 누락"
    )

    # Smoke: re.sub 직접 적용
    pattern = r"^(?:task-\d+(?:\.\d+)?(?:_\d+\.\d+)?(?:_[a-z])?(?:\+\d+)?|Task\s+\d+[\.\d]*)\s*[:：]?\s*"
    assert re.sub(pattern, "", "task-2487+1: sample title").strip() == "sample title"
    assert re.sub(pattern, "", "task-1234.5: legacy title").strip() == "legacy title"
