"""회귀 테스트: dispatch cron cmd에 --session unique SID 포함 검증 (task-2453).

헤임달(QA/테스터, dev2팀) 작성.

Case 1: _generate_session_id 단위 테스트
Case 2: 소스 정적 검증 — dispatch/__init__.py에 --session + _generate_session_id 포함 여부
Case 3: 소스 정적 검증 — 연속 2곳 이상 호출로 SID 유니크 보장 구조 확인
Case 4: 소스 정적 검증 — --once 인자 보존 여부
"""

import re
from pathlib import Path

import pytest

# ── 경로 설정 ──────────────────────────────────────────────────────────────────
# 파일 위치: tests/dispatch/test_session_isolation.py → worktree 루트는 parent×3
_WORKTREE = Path(__file__).resolve().parent.parent.parent
_DISPATCH_SRC = _WORKTREE / "dispatch" / "__init__.py"

# conftest.py가 /home/jay/workspace를 sys.path[0]에 등록하여 worktree dispatch 대신
# 원본 workspace dispatch를 로드한다. worktree 경로를 최선두에 삽입해 우선 적용한다.
# 또한 이미 캐시된 dispatch 모듈을 강제 reload 한다.
import sys as _sys
_wt_str = str(_WORKTREE)
if _wt_str in _sys.path:
    _sys.path.remove(_wt_str)
_sys.path.insert(0, _wt_str)
# 이미 import된 dispatch가 잘못된 경로를 가리키면 제거해서 재import 강제
if "dispatch" in _sys.modules and not getattr(_sys.modules["dispatch"], "__file__", "").startswith(_wt_str):
    del _sys.modules["dispatch"]


# ── 소스 텍스트 캐시 ───────────────────────────────────────────────────────────
@pytest.fixture(scope="module")
def dispatch_source() -> str:
    """dispatch/__init__.py 전체 소스를 한 번만 읽어 공유한다."""
    return _DISPATCH_SRC.read_text(encoding="utf-8")


# ── _generate_session_id 임포트 ────────────────────────────────────────────────
@pytest.fixture(scope="module")
def generate_session_id():
    """실제 _generate_session_id 함수를 worktree dispatch에서 임포트하여 반환한다."""
    import importlib
    import sys

    # worktree 경로가 최선두에 있어야 원본 workspace dispatch가 아닌
    # worktree dispatch를 로드한다.
    wt = str(_WORKTREE)
    if sys.path[0] != wt:
        if wt in sys.path:
            sys.path.remove(wt)
        sys.path.insert(0, wt)

    # 이미 로드된 dispatch가 worktree 것인지 확인: __file__ 경로로 검증
    existing = sys.modules.get("dispatch")
    if existing is not None:
        existing_file = getattr(existing, "__file__", "") or ""
        if str(_DISPATCH_SRC) not in existing_file:
            # 원본 dispatch가 캐시돼 있으면 제거 후 재로드
            del sys.modules["dispatch"]
            existing = None

    if existing is None:
        importlib.import_module("dispatch")

    dispatch_mod = sys.modules["dispatch"]
    assert hasattr(dispatch_mod, "_generate_session_id"), (
        f"dispatch 모듈({dispatch_mod.__file__})에 _generate_session_id 없음"
    )
    return dispatch_mod._generate_session_id


# ══════════════════════════════════════════════════════════════════════════════
# Case 1: _generate_session_id 단위 테스트
# ══════════════════════════════════════════════════════════════════════════════

class TestGenerateSessionId:
    """_generate_session_id 함수 자체의 단위 테스트."""

    def test_prefix_contains_task_id(self, generate_session_id):
        """task_id가 반환값의 prefix로 포함되어야 한다."""
        task_id = "task-2453"
        result = generate_session_id(task_id)
        assert result.startswith(f"{task_id}-"), (
            f"SID '{result}'이 task_id='{task_id}' prefix로 시작하지 않음"
        )

    def test_uniqueness_on_consecutive_calls(self, generate_session_id):
        """연속 2회 호출 결과가 서로 달라야 한다 (entropy: epoch + urandom(2))."""
        task_id = "task-2453"
        sid1 = generate_session_id(task_id)
        # time.time()이 같은 초여도 os.urandom(2)가 달라야 하므로 충분히 다름
        sid2 = generate_session_id(task_id)
        assert sid1 != sid2, (
            f"연속 2회 호출 결과가 동일함: '{sid1}' == '{sid2}' — uniqueness 보장 실패"
        )

    def test_format_matches_regex(self, generate_session_id):
        """형식이 '{task_id}-{digits}-{4hex}' 패턴과 일치해야 한다."""
        task_id = "task-2453"
        result = generate_session_id(task_id)
        # task-2453-<epoch_seconds>-<4hexchar>
        pattern = r"^task-\d+-\d+-[0-9a-f]{4}$"
        assert re.match(pattern, result), (
            f"SID '{result}'이 패턴 '{pattern}'과 일치하지 않음"
        )


# ══════════════════════════════════════════════════════════════════════════════
# Case 2: cron cmd에 --session <unique_sid> 포함 (소스 정적 검증)
# ══════════════════════════════════════════════════════════════════════════════

class TestSessionInCronCmd:
    """dispatch/__init__.py 소스에서 --session 및 _generate_session_id 호출 검증."""

    def test_session_flag_present_at_least_twice(self, dispatch_source):
        """cron cmd 구성 영역에 '--session' 문자열이 2건 이상 있어야 한다.

        normal dispatch + composite dispatch 각각 1건씩 = 최소 2건.
        """
        occurrences = re.findall(r'"--session"', dispatch_source)
        assert len(occurrences) >= 2, (
            f"'\"--session\"' 출현 횟수={len(occurrences)} — 최소 2건 필요 "
            f"(normal dispatch + composite dispatch)"
        )

    def test_generate_session_id_called_in_cron_blocks(self, dispatch_source):
        """_generate_session_id() 호출이 정의 1 + 실제 호출 2 = 3건 이상 있어야 한다."""
        occurrences = re.findall(r"_generate_session_id\(", dispatch_source)
        assert len(occurrences) >= 3, (
            f"'_generate_session_id(' 출현 횟수={len(occurrences)} — "
            f"정의 1 + 호출 2 이상(최소 3건) 필요"
        )

    def test_session_value_follows_session_flag(self, dispatch_source):
        """--session 바로 다음에 _cokacdir_session_id 변수가 오는 패턴이 2건 이상."""
        # "--session", \n        _cokacdir_session_id 형태 검증
        pattern = r'"--session",\s*\n\s*_cokacdir_session_id'
        matches = re.findall(pattern, dispatch_source)
        assert len(matches) >= 2, (
            f"'--session' 뒤에 _cokacdir_session_id가 오는 패턴 발견 횟수={len(matches)} — "
            f"최소 2건 필요"
        )


# ══════════════════════════════════════════════════════════════════════════════
# Case 3: 연속 2회 dispatch 시 SID가 서로 다른 구조 확인 (소스 + 런타임)
# ══════════════════════════════════════════════════════════════════════════════

class TestSessionUniquenessAcrossDispatches:
    """SID 유니크성이 구조적으로 보장됨을 검증."""

    def test_session_id_generation_uses_urandom(self, dispatch_source):
        """_generate_session_id 구현에 os.urandom이 포함되어 SID 고유성을 보장한다."""
        # 함수 정의 블록 추출
        fn_match = re.search(
            r"def _generate_session_id\(.*?\n((?:.*\n)*?.*?return.*?\n)",
            dispatch_source,
        )
        assert fn_match is not None, "_generate_session_id 함수 정의를 찾을 수 없음"
        fn_body = fn_match.group(0)
        assert "os.urandom" in fn_body, (
            "_generate_session_id 구현에 os.urandom이 없음 — uniqueness 미보장"
        )

    def test_two_consecutive_sids_are_different(self, generate_session_id):
        """런타임: 같은 task_id로 연속 생성된 두 SID는 달라야 한다."""
        task_id = "task-2453"
        sids = [generate_session_id(task_id) for _ in range(2)]
        assert sids[0] != sids[1], (
            f"연속 dispatch 시 SID 동일: {sids} — stale session 회피 실패 가능"
        )

    def test_each_cron_block_generates_own_sid(self, dispatch_source):
        """각 cron 블록이 독립적으로 _generate_session_id를 호출하는지 확인.

        두 블록 모두 _cokacdir_session_id = _generate_session_id(...) 패턴을 가져야 함.
        """
        pattern = r"_cokacdir_session_id\s*=\s*_generate_session_id\("
        matches = re.findall(pattern, dispatch_source)
        assert len(matches) >= 2, (
            f"각 cron 블록이 독립적으로 SID를 생성하는 패턴 발견 횟수={len(matches)} — "
            f"최소 2건 필요 (normal + composite)"
        )


# ══════════════════════════════════════════════════════════════════════════════
# Case 4 (헬퍼): --once 인자 보존 검증
# ══════════════════════════════════════════════════════════════════════════════

class TestOnceArgPreserved:
    """--session 추가 후에도 기존 --once 인자가 보존되는지 검증."""

    def test_once_flag_present_at_least_twice(self, dispatch_source):
        """cron cmd에 '--once' 플래그가 2건 이상 존재해야 한다."""
        occurrences = re.findall(r'"--once"', dispatch_source)
        assert len(occurrences) >= 2, (
            f"'\"--once\"' 출현 횟수={len(occurrences)} — 최소 2건 필요 "
            f"(기존 동작 보존 실패)"
        )

    def test_once_appears_before_session_in_cmd(self, dispatch_source):
        """--once가 --session보다 앞에 오는 패턴이 유지되어야 한다."""
        # cmd 리스트 내에서 "--once", ... "--session" 순서 확인
        pattern = r'"--once",\s*\n\s*"--session"'
        matches = re.findall(pattern, dispatch_source)
        assert len(matches) >= 2, (
            f"'--once' 다음 '--session' 순서 패턴 발견 횟수={len(matches)} — "
            f"최소 2건 필요 (cmd 구성 순서 보존)"
        )
