#!/usr/bin/env python3
"""
two_pass_audit.py의 threading regex 수정 검증 테스트.

수정 전: threading backslash-dot w+(?!Lock|RLock|Event|Semaphore)
  -> \\w+가 "Event" 소비 후 lookahead가 "(" 위치에서 평가 -> 오탐

수정 후: threading backslash-dot (?!Lock|RLock|Event|Semaphore|Condition|Barrier)\\w+
  -> lookahead가 \\w+ 앞에서 평가 -> Event 등을 올바르게 제외
"""

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

import pytest

# two_pass_audit.py를 패키지 __init__ 없이 직접 로드 (verifiers/__init__.py 부작용 회피)
_TWO_PASS_PATH = Path(__file__).parent.parent / "two_pass_audit.py"
_spec = importlib.util.spec_from_file_location("two_pass_audit_module", _TWO_PASS_PATH)
assert _spec is not None and _spec.loader is not None
_two_pass_module = importlib.util.module_from_spec(_spec)
_spec.loader.exec_module(_two_pass_module)  # type: ignore[union-attr]


# ---------------------------------------------------------------------------
# regex 상수 (직접 테스트 — 소스 변경 독립적)
# ---------------------------------------------------------------------------

# 수정 전 (버그 있는) 패턴
BUGGY_PATTERN = r"threading\.\w+(?!Lock|RLock|Event|Semaphore)"

# 수정 후 (올바른) 패턴
FIXED_PATTERN = r"threading\.(?!Lock|RLock|Event|Semaphore|Condition|Barrier)\w+"


# ---------------------------------------------------------------------------
# 1. 수정 전 패턴 — 오탐 확인 (왜 버그인지 문서화)
# ---------------------------------------------------------------------------


class TestBuggyPatternFalsePositives:
    """수정 전 패턴이 오탐(false positive)을 일으킴을 확인."""

    def test_buggy_pattern_matches_event(self):
        """버그 패턴은 threading.Event()를 오탐함"""
        assert (
            re.search(BUGGY_PATTERN, "e = threading.Event()") is not None
        ), "버그 패턴은 threading.Event()를 오탐해야 함"

    def test_buggy_pattern_matches_condition(self):
        """버그 패턴은 threading.Condition()도 오탐함"""
        assert re.search(BUGGY_PATTERN, "c = threading.Condition()") is not None

    def test_buggy_pattern_matches_barrier(self):
        """버그 패턴은 threading.Barrier(5)도 오탐함"""
        assert re.search(BUGGY_PATTERN, "b = threading.Barrier(5)") is not None


# ---------------------------------------------------------------------------
# 2. 수정 후 패턴 — 안전 패턴은 탐지하지 않음 (False Negative 없음)
# ---------------------------------------------------------------------------


class TestFixedPatternNoFalsePositives:
    """수정 후 패턴은 안전한 threading primitive를 탐지하지 않음."""

    def test_fixed_does_not_match_event(self):
        """threading.Event()는 탐지하지 않음"""
        assert re.search(FIXED_PATTERN, "e = threading.Event()") is None

    def test_fixed_does_not_match_lock(self):
        """threading.Lock()은 탐지하지 않음"""
        assert re.search(FIXED_PATTERN, "lock = threading.Lock()") is None

    def test_fixed_does_not_match_rlock(self):
        """threading.RLock()은 탐지하지 않음"""
        assert re.search(FIXED_PATTERN, "rlock = threading.RLock()") is None

    def test_fixed_does_not_match_semaphore(self):
        """threading.Semaphore(5)는 탐지하지 않음"""
        assert re.search(FIXED_PATTERN, "sem = threading.Semaphore(5)") is None

    def test_fixed_does_not_match_condition(self):
        """threading.Condition()은 탐지하지 않음"""
        assert re.search(FIXED_PATTERN, "cond = threading.Condition()") is None

    def test_fixed_does_not_match_barrier(self):
        """threading.Barrier(3)는 탐지하지 않음"""
        assert re.search(FIXED_PATTERN, "b = threading.Barrier(3)") is None


# ---------------------------------------------------------------------------
# 3. 수정 후 패턴 — 위험 패턴은 여전히 탐지함
# ---------------------------------------------------------------------------


class TestFixedPatternDetectsRiskyUsage:
    """수정 후 패턴도 위험한 threading 사용을 탐지함."""

    def test_fixed_matches_thread(self):
        """threading.Thread(target=...)는 탐지함 (락 없음 경고 대상)"""
        assert re.search(FIXED_PATTERN, "t = threading.Thread(target=fn)") is not None

    def test_fixed_matches_timer(self):
        """threading.Timer(1, fn)는 탐지함"""
        assert re.search(FIXED_PATTERN, "t = threading.Timer(1, fn)") is not None

    def test_fixed_matches_local(self):
        """threading.local()은 탐지함"""
        assert re.search(FIXED_PATTERN, "tls = threading.local()") is not None

    def test_fixed_matches_current_thread(self):
        """threading.current_thread()는 탐지함"""
        assert re.search(FIXED_PATTERN, "threading.current_thread()") is not None


# ---------------------------------------------------------------------------
# 4. two_pass_audit.py 소스에 수정된 패턴이 실제 반영됐는지 확인
# ---------------------------------------------------------------------------


class TestSourceContainsFixedPattern:
    """two_pass_audit.py 소스 파일에 수정된 패턴이 있음을 확인."""

    def _load_source(self) -> str:
        source_path = Path(__file__).parent.parent / "two_pass_audit.py"
        assert source_path.exists(), f"소스 파일이 없음: {source_path}"
        return source_path.read_text(encoding="utf-8")

    def test_fixed_pattern_in_source(self):
        """소스에 수정된 lookahead 패턴이 포함됨"""
        src = self._load_source()
        # 수정된 패턴의 핵심: lookahead가 \w+ 앞에 위치
        assert r"threading\.(?!" in src, "수정된 패턴 'threading\\.(?!' 이 소스에 없음"

    def test_buggy_pattern_not_in_source(self):
        """소스에 버그 있는 패턴이 없음"""
        src = self._load_source()
        assert r"threading\.\w+(?!" not in src, "버그 패턴 'threading\\.\\w+(?!' 이 소스에 아직 남아 있음"

    def test_condition_and_barrier_in_exclusion_list(self):
        """Condition과 Barrier가 제외 목록에 포함됨"""
        src = self._load_source()
        assert "Condition" in src
        assert "Barrier" in src
