"""
task-2729+22 회귀: file_touch_ratio_check CODE_ROOT recognition (Option A replacement)

회장 verbatim 13 시나리오 — 전부 isolated temp(pytest tmp_path) git repo 로 검증.
canonical 무손상. 직접 코딩/실 spawn/activation 없음.

★ RAW_KEY_DENYLIST_LITERAL_IN_TEST_HYGIENE (task-2729+21 #194 BLOCKER) 해소:
  raw-key denylist 를 **소스에 full 16-hex literal 로 박지 않는다**. 키는 런타임에
  조각(split fragment)으로 조립하며(어떤 줄에도 연속된 full key 부재), assert
  실패 메시지에도 재구성된 키를 출력하지 않는다(masked). → PR diff raw key literal 0.

검증 대상 결함:
- env root(PROJECT_PATH→WORKTREE_PATH→QC_EVIDENCE_ROOT) 미인식 → canonical diff
  기준 ratio 0.00 false-negative
- env root 가 repo 하위 디렉토리면 git diff(top-level 기준)와 report 파싱 mismatch
- HEAD~5 가 커밋<5/shallow 에서 subprocess crash
"""

import importlib.util
import os
import pathlib
import re
import subprocess

import pytest

# ── 검증 대상 모듈 로드 (repo 내 shared verifier) ─────────────────────────────
THIS_FILE = pathlib.Path(__file__).resolve()
REPO_ROOT = THIS_FILE.parents[2]
MOD_PATH = REPO_ROOT / "teams" / "shared" / "verifiers" / "file_touch_ratio_check.py"

_spec = importlib.util.spec_from_file_location("ftr_check_2729p22", str(MOD_PATH))
assert _spec is not None and _spec.loader is not None
ftr = importlib.util.module_from_spec(_spec)
_spec.loader.exec_module(ftr)


# ── raw-key denylist 마스킹 헬퍼 ──────────────────────────────────────────────
# 키는 조각으로만 보관한다. 어떤 한 줄에도 연속된 full 16-hex literal 이 없도록
# 3/4/4/5 분절했고, full key 는 _denylist() 가 런타임에만 조립한다(디스크/로그 미기록).
_RAW_KEY_FRAGMENTS = (
    ("c11", "9085", "addb", "0f8b7"),   # collector(ANU) key fragments
    ("c38", "fb99", "5561", "6e24d"),   # secondary key fragments
)
# 추가로 차단할 키 변수 노출 패턴(=split 으로 보관)
_KEY_ASSIGN_NEEDLE = "COKACDIR_KEY_ANU" + " ="


def _denylist():
    """raw-key denylist 를 런타임에 조각 결합으로 생성(로그 미출력)."""
    return tuple("".join(parts) for parts in _RAW_KEY_FRAGMENTS)


def _mask(token):
    """assert 메시지/로그용 마스킹 — full key 재구성 출력 0."""
    if len(token) <= 6:
        return "***"
    return f"{token[:3]}…{token[-2:]}(len={len(token)})"


# ── git/repo 헬퍼 ─────────────────────────────────────────────────────────────
def _git(repo, *args):
    return subprocess.run(
        [
            "git", "-C", str(repo),
            "-c", "user.name=t", "-c", "user.email=t@t",
            "-c", "commit.gpgsign=false",
            *args,
        ],
        capture_output=True,
        text=True,
    )


def _commit(repo, files: dict, msg: str):
    for rel, content in files.items():
        p = pathlib.Path(repo) / rel
        p.parent.mkdir(parents=True, exist_ok=True)
        p.write_text(content, encoding="utf-8")
        _git(repo, "add", rel)
    _git(repo, "commit", "-q", "-m", msg)


def _build_repo(repo, n_commits: int, final_files: dict):
    """n_commits 개 커밋을 가진 repo 생성. 마지막 커밋에 final_files 포함."""
    repo = pathlib.Path(repo)
    repo.mkdir(parents=True, exist_ok=True)
    _git(repo, "init", "-q")
    for i in range(max(0, n_commits - 1)):
        _commit(repo, {f"_pad/{i}.txt": str(i)}, f"pad {i}")
    if n_commits >= 1:
        _commit(repo, final_files or {"_pad/last.txt": "x"}, "final")
    return repo


def _write_report(workspace, task_id: str, rel_files, prefix: str):
    """canonical workspace 의 memory/reports/{task_id}.md 작성.

    각 파일을 ``- {prefix}/{rel}`` 목록 항목으로 기록.
    """
    rp = pathlib.Path(workspace) / "memory" / "reports" / f"{task_id}.md"
    rp.parent.mkdir(parents=True, exist_ok=True)
    lines = ["# report", ""]
    for rel in rel_files:
        lines.append(f"- {prefix.rstrip('/')}/{rel}")
    rp.write_text("\n".join(lines) + "\n", encoding="utf-8")
    return rp


@pytest.fixture(autouse=True)
def _clear_env(monkeypatch):
    """각 테스트 전 CODE_ROOT env 초기화 (호스트 env 누수 방지)."""
    for v in ("PROJECT_PATH", "WORKTREE_PATH", "QC_EVIDENCE_ROOT"):
        monkeypatch.delenv(v, raising=False)
    yield


# ── #1 PR diff raw key literal 0 (소스/로그/diff 어디에도 full key 부재) ──────
def test_01_no_raw_key_literal_in_pr_diff():
    deny = _denylist()
    # (a) 본 PR 이 건드리는 소스(verifier + 본 테스트)에 full key literal 0
    for path in (MOD_PATH, THIS_FILE):
        src = path.read_text(encoding="utf-8")
        for tok in deny:
            assert tok not in src, f"raw key literal 노출: {_mask(tok)} @ {path.name}"
        assert _KEY_ASSIGN_NEEDLE not in src, f"키 변수 노출 @ {path.name}"
        # 16-hex 키 할당 literal 패턴도 0
        assert not re.search(
            r"COKACDIR_KEY[A-Z_]*\s*=\s*['\"][0-9a-f]{12,}", src
        ), f"키 할당 literal @ {path.name}"
    # (b) 실제 PR diff(origin/main..HEAD) 가 가용하면 그 diff 에도 full key 0
    diff = _git(REPO_ROOT, "diff", "origin/main...HEAD")
    if diff.returncode == 0 and diff.stdout:
        for tok in deny:
            assert tok not in diff.stdout, f"PR diff raw key 노출: {_mask(tok)}"
    # (c) positive control: 탐지기가 실제로 동작함을 보장(무력화 회귀 차단)
    planted = f"+ leaked {deny[0]} trailing"
    assert any(tok in planted for tok in deny), "raw-key detector 무력화 회귀"


# ── #2 env root = repo root → PASS (ratio 1.00) ──────────────────────────────
def test_02_env_root_equals_repo_root_pass(tmp_path, monkeypatch):
    repo = _build_repo(
        tmp_path / "repo", 6,
        {"teams/shared/verifiers/file_touch_ratio_check.py": "print('x')\n"},
    )
    _write_report(repo, "task-x", ["teams/shared/verifiers/file_touch_ratio_check.py"], str(repo))
    monkeypatch.setenv("PROJECT_PATH", str(repo))
    out = ftr.verify("task-x", str(repo))
    assert out["status"] == "PASS", out


# ── #3 env root = repo 하위 디렉토리 → top-level 정규화 후 PASS ───────────────
def test_03_env_subdir_toplevel_normalization_pass(tmp_path, monkeypatch):
    repo = _build_repo(
        tmp_path / "repo", 6,
        {"teams/shared/verifiers/file_touch_ratio_check.py": "print('x')\n"},
    )
    subdir = pathlib.Path(repo) / "teams" / "shared"
    assert subdir.is_dir()
    # report 는 canonical(repo) 경로로 기록, env 는 하위 디렉토리 지정
    _write_report(repo, "task-x", ["teams/shared/verifiers/file_touch_ratio_check.py"], str(repo))
    monkeypatch.setenv("PROJECT_PATH", str(subdir))
    out = ftr.verify("task-x", str(repo))
    # 하위 디렉토리여도 --show-toplevel 정규화로 diff/report 경로 일치 → PASS
    assert out["status"] == "PASS", out
    assert any("source=PROJECT_PATH" in d for d in out["details"]), out


# ── #4 env 우선순위 PROJECT_PATH > WORKTREE_PATH > QC_EVIDENCE_ROOT ───────────
def test_04_env_priority_order(tmp_path, monkeypatch):
    repo_a = _build_repo(tmp_path / "a", 6, {"a.txt": "1\n"})
    repo_b = _build_repo(tmp_path / "b", 6, {"b.txt": "1\n"})
    repo_c = _build_repo(tmp_path / "c", 6, {"c.txt": "1\n"})
    # report 는 repo_a(canonical) 기준
    _write_report(repo_a, "task-x", ["a.txt"], str(repo_a))

    # 세 env 모두 설정 → PROJECT_PATH(repo_a) 채택
    monkeypatch.setenv("PROJECT_PATH", str(repo_a))
    monkeypatch.setenv("WORKTREE_PATH", str(repo_b))
    monkeypatch.setenv("QC_EVIDENCE_ROOT", str(repo_c))
    out = ftr.verify("task-x", str(repo_a))
    assert any("source=PROJECT_PATH" in d for d in out["details"]), out

    # PROJECT_PATH 제거 → WORKTREE_PATH(repo_b) 채택
    monkeypatch.delenv("PROJECT_PATH")
    out2 = ftr.verify("task-x", str(repo_a))
    assert any("source=WORKTREE_PATH" in d for d in out2["details"]), out2

    # WORKTREE_PATH 도 제거 → QC_EVIDENCE_ROOT(repo_c) 채택
    monkeypatch.delenv("WORKTREE_PATH")
    out3 = ftr.verify("task-x", str(repo_a))
    assert any("source=QC_EVIDENCE_ROOT" in d for d in out3["details"]), out3


# ── #5 env invalid → canonical fallback / fail-safe 명확 ─────────────────────
def test_05_invalid_env_falls_back_to_workspace(tmp_path, monkeypatch):
    repo = _build_repo(tmp_path / "repo", 6, {"f.txt": "1\n"})
    _write_report(repo, "task-x", ["f.txt"], str(repo))
    # 존재하지 않는 경로 / git 아닌 경로
    bogus = tmp_path / "nope"
    nongit = tmp_path / "plain"
    nongit.mkdir()
    monkeypatch.setenv("PROJECT_PATH", str(bogus))
    monkeypatch.setenv("WORKTREE_PATH", str(nongit))
    out = ftr.verify("task-x", str(repo))
    assert any("source=workspace_root" in d for d in out["details"]), out
    assert out["status"] in ("PASS", "WARN", "FAIL"), out  # crash 없음


# ── #5b base_root "/" edge (MEDIUM, D안) — 빈/루트 prefix catch-all 차단 ──────
def test_05b_root_base_edge_no_catchall():
    # base_roots 가 "/" 만이면 strip 후 빈 prefix → catch-all 방지로 [] 반환
    assert ftr._extract_reported_files("- /a/b/c\n| /x/y |\n", ["/"]) == []
    # 정상 prefix 와 "/" 혼재 시, 정상 prefix 만 strip 후보로 사용
    out = ftr._extract_reported_files("- /repo/keep.txt\n", ["/", "/repo"])
    assert out == ["keep.txt"], out


# ── #6 canonical report + selected CODE_ROOT diff 조합 PASS ───────────────────
def test_06_canonical_report_selected_diff_combo(tmp_path, monkeypatch):
    canonical = _build_repo(tmp_path / "canon", 6, {"keep.txt": "1\n"})
    worktree = _build_repo(
        tmp_path / "wt", 6,
        {"teams/shared/verifiers/file_touch_ratio_check.py": "print('y')\n"},
    )
    # report 는 canonical 에 두되, 파일 경로 prefix 는 worktree 경로로 기록
    _write_report(
        canonical, "task-x",
        ["teams/shared/verifiers/file_touch_ratio_check.py"], str(worktree),
    )
    monkeypatch.setenv("WORKTREE_PATH", str(worktree))
    out = ftr.verify("task-x", str(canonical))
    assert out["status"] == "PASS", out
    assert any("source=WORKTREE_PATH" in d for d in out["details"]), out


# ── #7 worktree changed ↔ report changed 4/4 매칭 ratio 1.00 ─────────────────
def test_07_four_of_four_ratio_one(tmp_path, monkeypatch):
    files = {
        "teams/shared/verifiers/file_touch_ratio_check.py": "print(1)\n",
        "tests/regression/test_x.py": "print(2)\n",
        "memory/reports/task-x.md": "r\n",
        "memory/plans/p0b-pickup/design.md": "d\n",
    }
    repo = _build_repo(tmp_path / "repo", 6, files)
    _write_report(repo, "task-x", list(files.keys()), str(repo))
    monkeypatch.setenv("PROJECT_PATH", str(repo))
    out = ftr.verify("task-x", str(repo))
    assert out["status"] == "PASS", out
    assert any("File-Touch Ratio: 1.00" in d for d in out["details"]), out


# ── #8 HEAD~5 불가 / shallow / 커밋 부족 fail-safe ────────────────────────────
def test_08_head5_failsafe_low_commit_count(tmp_path, monkeypatch):
    # (a) 단일(루트) 커밋 — HEAD~5/HEAD~1 부재 → empty-tree 비교로 PASS
    repo1 = _build_repo(
        tmp_path / "r1", 1,
        {"teams/shared/verifiers/file_touch_ratio_check.py": "print('z')\n"},
    )
    _write_report(repo1, "task-x", ["teams/shared/verifiers/file_touch_ratio_check.py"], str(repo1))
    monkeypatch.setenv("PROJECT_PATH", str(repo1))
    out1 = ftr.verify("task-x", str(repo1))
    assert out1["status"] == "PASS", out1  # crash 없이 처리

    # (b) 3커밋 — HEAD~5 불가, bounded HEAD~2 로 graceful
    repo2 = _build_repo(
        tmp_path / "r2", 3,
        {"teams/shared/verifiers/file_touch_ratio_check.py": "print('z')\n"},
    )
    _write_report(repo2, "task-y", ["teams/shared/verifiers/file_touch_ratio_check.py"], str(repo2))
    monkeypatch.setenv("PROJECT_PATH", str(repo2))
    out2 = ftr.verify("task-y", str(repo2))
    assert out2["status"] in ("PASS", "WARN", "FAIL"), out2  # 미정의 crash 0


# ── #9 canonical(2716) dirty/diff 가 selected CODE_ROOT clean finalize false-block 안 함 ──
def test_09_canonical_dirty_does_not_block_selected_root(tmp_path, monkeypatch):
    canonical = _build_repo(tmp_path / "canon", 6, {"x.txt": "1\n"})
    # canonical 에 dirty(미커밋) 변경 주입 — 무관한 파일
    (pathlib.Path(canonical) / "dirty_uncommitted.txt").write_text("dirty\n", encoding="utf-8")
    worktree = _build_repo(
        tmp_path / "wt", 6,
        {"teams/shared/verifiers/file_touch_ratio_check.py": "print('w')\n"},
    )
    _write_report(
        canonical, "task-x",
        ["teams/shared/verifiers/file_touch_ratio_check.py"], str(worktree),
    )
    monkeypatch.setenv("PROJECT_PATH", str(worktree))
    out = ftr.verify("task-x", str(canonical))
    # selected CODE_ROOT(worktree) 기준 clean PASS — canonical dirty 가 false-block 안 함
    assert out["status"] == "PASS", out
    assert any("source=PROJECT_PATH" in d for d in out["details"]), out


# ── #10 per-team directory symlink parity 확인 ───────────────────────────────
def test_10_per_team_symlink_parity():
    teams_dir = REPO_ROOT / "teams"
    worktree_shared = str(
        (teams_dir / "shared" / "verifiers" / "file_touch_ratio_check.py").resolve()
    )
    suffix = "/teams/shared/verifiers/file_touch_ratio_check.py"
    converged = 0
    caveat = 0
    for team in sorted(teams_dir.iterdir()):
        if team.name == "shared":
            continue
        link = team / "qc" / "verifiers"
        if not link.is_symlink():
            continue
        resolved = str((link / "file_touch_ratio_check.py").resolve())
        target_str = os.readlink(link)
        if os.path.isabs(target_str):
            # dev8 absolute double-hop caveat: 워크트리 외부(canonical)로 해석될 수
            # 있음. 구조 무변경 — shared verifier 경로 suffix 로만 동일 파일임을 확인.
            caveat += 1
            assert resolved.endswith(suffix), (team.name, resolved)
        else:
            # 상대 symlink: 워크트리 shared 로 수렴 → 1파일 수정으로 동일성 유지
            converged += 1
            assert resolved == worktree_shared, (team.name, resolved)
    assert converged >= 8, f"상대 symlink 수렴 부족: {converged}"
    assert caveat == 1, f"dev8 absolute double-hop caveat 예상 1: {caveat}"


# ── #11 ACTIVE=false / systemd enable 0 / activation_epoch absent / real spawn 0 ──
def test_11_no_activation_no_spawn():
    src = MOD_PATH.read_text(encoding="utf-8")
    for tok in (
        "ACTIVE=true", "ACTIVE = true", "activation_epoch",
        "systemctl enable", "systemctl start",
    ):
        assert tok not in src, tok
    # real spawn/dispatch 호출 0 — 호출 패턴 기준(docstring 언급은 무해)
    for call in ("os.system", "subprocess.Popen", "Popen(", ".spawn", "dispatch(", "spawn("):
        assert call not in src, call
    # subprocess 호출은 git 전용(헬퍼 1곳) — git 외 외부 프로세스 spawn 0
    runs = re.findall(r"subprocess\.run\(", src)
    assert len(runs) == 1, f"subprocess.run 호출 수 예상 1(git 헬퍼): {len(runs)}"
    assert '["git", "-C", cwd]' in src, "subprocess.run 이 git 전용이 아님"


# ── #12 git_evidence·dispatch·callback prereg 무수정 (verifier 단독) ──────────
def test_12_no_foreign_module_touch():
    src = MOD_PATH.read_text(encoding="utf-8")
    # verifier 는 report read 외 파일 쓰기 없음 (open 쓰기 모드 0)
    for w in ('"w"', "'w'", '"a"', "'a'", '"w+"', "'w+'"):
        assert w not in src, w
    # 외부 모듈 import/수정 0
    for mod in ("git_evidence", "dispatch", "callback"):
        assert f"import {mod}" not in src
        assert f"from {mod}" not in src


# ── #13 canonical/외부 artifacts 무손상 (verify 가 파일 쓰기 0) ───────────────
def test_13_verify_writes_nothing(tmp_path, monkeypatch):
    repo = _build_repo(
        tmp_path / "repo", 6,
        {"teams/shared/verifiers/file_touch_ratio_check.py": "print(1)\n"},
    )
    _write_report(repo, "task-x", ["teams/shared/verifiers/file_touch_ratio_check.py"], str(repo))
    monkeypatch.setenv("PROJECT_PATH", str(repo))
    # 실행 전후 파일 트리 스냅샷 비교 — verify 가 어떤 파일도 생성/수정/삭제 안 함
    before = {p: p.stat().st_mtime_ns for p in pathlib.Path(repo).rglob("*") if p.is_file()}
    ftr.verify("task-x", str(repo))
    after = {p: p.stat().st_mtime_ns for p in pathlib.Path(repo).rglob("*") if p.is_file()}
    assert before == after, "verify 가 파일을 변경함 (부수효과 발생)"
