"""task-2729+16 회귀 테스트 — finish-task.sh CODE_ROOT evidence env-fix.

테스터: 닌기르수(Ningirsu), 개발5팀 마르둑(Marduk) 팀
대상 스크립트: scripts/finish-task.sh
작업 참조: task-2729+16 (env-fix 3 hunk: H1/H2/H3)

검증 방식:
  - 정적 소스 검증 (grep/regex) 위주 — 전체 실행 부작용 없음
  - bash 문법 검사 (bash -n)
  - H2 tier③ 블록 행동 특성은 정적 가드 소스 확인으로 대체
    (사유: bash 함수 내 embedded python heredoc(_detect_task_worktree)을
     독립 bash subprocess에서 source 실행하려면 WORKSPACE/TASK_ID 전역변수가
     필요하며, 격리 실행 시 python3 heredoc 탈출 시퀀스 파싱이 복잡해
     결정적이지 않음. 정적 검증으로 충분히 동치 보장.)

동료 엔키(Enki)가 동시 구현 중 — H1~H3 중 일부 미적용 시 관련 테스트 FAIL.
테스트 강도를 약화시키지 말 것 (절대 금지).
"""
from __future__ import annotations

import re
import subprocess
from pathlib import Path

# ---------------------------------------------------------------------------
# 경로 설정 — 절대경로 하드코딩 금지, __file__ 기준 상대경로
# ---------------------------------------------------------------------------
_REPO = Path(__file__).resolve().parents[2]
_FINISH_TASK = _REPO / "scripts" / "finish-task.sh"


def _src() -> str:
    """finish-task.sh 전체 소스 캐시 없이 읽기. 테스트 간 독립성 유지."""
    return _FINISH_TASK.read_text(encoding="utf-8")


# ---------------------------------------------------------------------------
# H1: WORKSPACE override + backward-compat
# ---------------------------------------------------------------------------

class TestH1WorkspaceOverride:
    """H1 hunk: WORKSPACE 정의에 FINISH_TASK_WORKSPACE_OVERRIDE 포함"""

    def test_workspace_override_present(self) -> None:
        """H1: WORKSPACE 정의가 FINISH_TASK_WORKSPACE_OVERRIDE 를 최우선 fallback으로 포함.

        검증:
          1. `FINISH_TASK_WORKSPACE_OVERRIDE` 가 WORKSPACE 정의 라인에 존재.
          2. 기존 `${WORKSPACE:-` fallback 도 보존(backward-compat).
          3. 삼중 중첩 형태: ${FINISH_TASK_WORKSPACE_OVERRIDE:-${WORKSPACE:-...}}
        """
        src = _src()
        # WORKSPACE 정의 라인 찾기
        workspace_def_lines = [
            ln for ln in src.splitlines()
            if re.search(r'^WORKSPACE\s*=', ln.strip())
        ]
        assert workspace_def_lines, "WORKSPACE 정의 라인이 finish-task.sh에 없음"

        workspace_def = workspace_def_lines[0]

        # FINISH_TASK_WORKSPACE_OVERRIDE 포함 확인
        assert "FINISH_TASK_WORKSPACE_OVERRIDE" in workspace_def, (
            f"WORKSPACE 정의에 FINISH_TASK_WORKSPACE_OVERRIDE 없음: {workspace_def!r}"
        )

        # 기존 ${WORKSPACE:- 패턴 보존(backward-compat) 확인
        assert "${WORKSPACE:-" in workspace_def, (
            f"WORKSPACE 정의에 기존 fallback ${{WORKSPACE:-}} 가 없음 (backward-compat 파손): {workspace_def!r}"
        )

        # 삼중 중첩 패턴 확인: FINISH_TASK_WORKSPACE_OVERRIDE 가 외부, WORKSPACE 가 내부
        pattern = re.compile(
            r'WORKSPACE\s*=\s*"\$\{FINISH_TASK_WORKSPACE_OVERRIDE:-\$\{WORKSPACE:-'
        )
        assert pattern.search(src), (
            "WORKSPACE 정의가 삼중 중첩 형태 "
            "'${FINISH_TASK_WORKSPACE_OVERRIDE:-${WORKSPACE:-...}}' 가 아님"
        )


# ---------------------------------------------------------------------------
# H1: _detect_task_worktree 함수 정의
# ---------------------------------------------------------------------------

class TestH1DetectTaskWorktreeFunc:
    """H1 hunk: _detect_task_worktree() 함수 정의 검증"""

    def test_detect_task_worktree_func_defined(self) -> None:
        """H1: _detect_task_worktree() 함수 정의 존재 + 핵심 git 명령 포함.

        검증:
          1. 함수 정의 `_detect_task_worktree()` 존재.
          2. `git worktree list --porcelain` 호출 코드 포함.
          3. `--fixed-strings` 플래그 사용 (grep/git log 보안 패턴).
        """
        src = _src()

        # 함수 정의 존재
        assert "_detect_task_worktree()" in src, (
            "_detect_task_worktree() 함수 정의가 finish-task.sh에 없음 (H1 미적용)"
        )

        # git worktree list --porcelain 사용 확인
        assert 'worktree", "list", "--porcelain"' in src or \
               '"worktree", "list", "--porcelain"' in src or \
               "worktree list --porcelain" in src, (
            "_detect_task_worktree 내 'git worktree list --porcelain' 호출 없음"
        )

        # --fixed-strings 사용 확인 (git log --grep 에서 task_id 리터럴 매칭)
        assert "--fixed-strings" in src, (
            "_detect_task_worktree 내 '--fixed-strings' 사용 없음 "
            "(task_id 가 특수문자 포함 시 regex injection 위험)"
        )


# ---------------------------------------------------------------------------
# H2: tier③ 블록 존재
# ---------------------------------------------------------------------------

class TestH2Tier3Block:
    """H2 hunk: tier③ 자동 탐지 블록 존재 검증"""

    def test_tier3_block_present(self) -> None:
        """H2: tier③ 블록(_WT_DETECTED 패턴 + PROJECT_PATH 승격) 존재.

        검증:
          1. `_WT_DETECTED="$(_detect_task_worktree)"` 패턴 존재.
          2. `PROJECT_PATH="$_WT_DETECTED"` 대입 존재.
          3. 두 패턴이 같은 if 블록 안에 연속 위치 (소스 순서로 tier③ 블록 구성).
        """
        src = _src()

        # _WT_DETECTED 탐지 호출 패턴
        assert '_WT_DETECTED="$(_detect_task_worktree)"' in src, (
            'tier③ 블록: _WT_DETECTED="$(_detect_task_worktree)" 패턴 없음 (H2 미적용)'
        )

        # PROJECT_PATH 승격 대입
        assert 'PROJECT_PATH="$_WT_DETECTED"' in src, (
            'tier③ 블록: PROJECT_PATH="$_WT_DETECTED" 대입 없음 (H2 미적용)'
        )

        # 소스 순서 검증: _WT_DETECTED 탐지가 PROJECT_PATH 대입보다 앞
        idx_detect = src.index('_WT_DETECTED="$(_detect_task_worktree)"')
        idx_assign = src.index('PROJECT_PATH="$_WT_DETECTED"')
        assert idx_detect < idx_assign, (
            "tier③ 블록 순서 오류: _WT_DETECTED 탐지가 PROJECT_PATH 대입 앞에 없음"
        )


# ---------------------------------------------------------------------------
# H3: QC_EVIDENCE_ROOT 전파
# ---------------------------------------------------------------------------

class TestH3QcEvidenceRoot:
    """H3 hunk: QC_EVIDENCE_ROOT 정의 및 QC 호출 양 분기에 환경변수 전파"""

    def test_qc_evidence_root_propagation(self) -> None:
        """H3: QC_EVIDENCE_ROOT 정의 + QC python3 호출 2분기 모두에 env prefix.

        검증:
          1. `QC_EVIDENCE_ROOT="${PROJECT_PATH:-$WORKSPACE}"` 정의 존재.
          2. QC python3 호출 라인 2개 모두 `PROJECT_PATH="$QC_EVIDENCE_ROOT" WORKTREE_PATH="$QC_EVIDENCE_ROOT"` prefix.
          3. 정규식으로 2건 매칭 확인.
        """
        src = _src()

        # QC_EVIDENCE_ROOT 정의 존재
        assert 'QC_EVIDENCE_ROOT="${PROJECT_PATH:-$WORKSPACE}"' in src, (
            'H3: QC_EVIDENCE_ROOT="${PROJECT_PATH:-$WORKSPACE}" 정의 없음 (H3 미적용)'
        )

        # QC python3 호출 라인의 env prefix 패턴 (2분기: team/no-team)
        # 패턴: PROJECT_PATH="$QC_EVIDENCE_ROOT" WORKTREE_PATH="$QC_EVIDENCE_ROOT" python3 ...QC_SCRIPT...
        qc_env_prefix_pattern = re.compile(
            r'PROJECT_PATH="\$QC_EVIDENCE_ROOT"\s+WORKTREE_PATH="\$QC_EVIDENCE_ROOT"\s+python3'
        )
        matches = qc_env_prefix_pattern.findall(src)
        assert len(matches) >= 2, (
            f"H3: QC python3 호출에 PROJECT_PATH/WORKTREE_PATH env prefix 가 "
            f"{len(matches)}개만 있음 (2개 필요 — team 분기, no-team 분기). "
            "finish-task.sh QC 실행 분기 2개 모두에 prefix 필요."
        )

    def test_qc_evidence_root_defined_before_qc_calls(self) -> None:
        """H3: QC_EVIDENCE_ROOT 정의가 QC python3 호출보다 소스 순서상 앞에 위치."""
        src = _src()

        assert 'QC_EVIDENCE_ROOT="${PROJECT_PATH:-$WORKSPACE}"' in src, (
            'QC_EVIDENCE_ROOT 정의 없음 — test_qc_evidence_root_propagation 도 실패해야 함'
        )

        idx_def = src.index('QC_EVIDENCE_ROOT="${PROJECT_PATH:-$WORKSPACE}"')

        # 첫 번째 QC env-prefix python3 호출 위치
        qc_call_pattern = re.compile(
            r'PROJECT_PATH="\$QC_EVIDENCE_ROOT"\s+WORKTREE_PATH="\$QC_EVIDENCE_ROOT"\s+python3'
        )
        m = qc_call_pattern.search(src)
        assert m is not None, "QC env-prefix python3 호출 미발견"
        assert idx_def < m.start(), (
            "QC_EVIDENCE_ROOT 정의가 QC python3 호출보다 뒤에 있음 (순서 오류)"
        )


# ---------------------------------------------------------------------------
# bash 문법 검사
# ---------------------------------------------------------------------------

class TestBashSyntax:
    """bash -n 으로 finish-task.sh 문법 유효성 검증"""

    def test_bash_syntax_valid(self) -> None:
        """bash -n finish-task.sh 가 returncode 0 (파싱 오류 없음)."""
        result = subprocess.run(
            ["bash", "-n", str(_FINISH_TASK)],
            capture_output=True,
            text=True,
            timeout=30,
        )
        assert result.returncode == 0, (
            f"bash -n finish-task.sh 실패 (syntax error):\n"
            f"stdout: {result.stdout}\nstderr: {result.stderr}"
        )


# ---------------------------------------------------------------------------
# H2 + H1: canonical 가드 정적 확인 + backward-compat
# ---------------------------------------------------------------------------

class TestCanonicalGuardAndBackwardCompat:
    """_detect_task_worktree 내 canonical 가드 + tier③ 빈 문자열 backward-compat"""

    def test_detect_task_worktree_isolated_behavior(self) -> None:
        """_detect_task_worktree 내 canonical 경로 제외 가드 정적 확인.

        실제 bash 격리 실행 대신 정적 소스 검증으로 대체.
        사유: _detect_task_worktree 는 내부적으로 python3 heredoc(PYEOF_DETECT_WT)을 사용하며,
        bash -c 로 함수만 추출 실행 시 WORKSPACE/TASK_ID 전역변수 주입 및
        heredoc 탈출 처리가 복잡해 결정적 격리 실행이 어렵다.
        소스에서 `os.path.realpath(cur) == canon` 가드가 존재함을 확인하는 것이
        동치(canonical worktree 탐지 제외)를 충분히 보장한다.

        함수 범위 추출 전략:
          `_detect_task_worktree()` 정의 이후 heredoc 종료 마커(PYEOF_DETECT_WT)를 찾고,
          그 다음 줄의 `}` 까지 포함시킨다. bash heredoc은 내부에 `{`/`}` 를 포함할 수
          있어 단순 brace-counting이 오작동하므로, PYEOF 마커 이후 닫힘 `}` 라인까지
          슬라이싱하는 방식을 사용한다.
        """
        src = _src()

        # canonical 경로 동등성 가드: os.path.realpath(cur) == canon
        assert "os.path.realpath(cur) == canon" in src, (
            "_detect_task_worktree 내 'os.path.realpath(cur) == canon' 가드 없음 — "
            "canonical worktree 가 탐지 결과에 포함될 위험"
        )

        # _detect_task_worktree 함수 범위 추출:
        #   함수 선언 위치부터 heredoc 종료 마커(PYEOF_DETECT_WT) 다음에 오는
        #   첫 번째 닫힘 `}` 라인까지를 함수 바디로 간주한다.
        func_start = src.find("_detect_task_worktree()")
        assert func_start != -1, "_detect_task_worktree() 함수 정의 없음"

        # PYEOF_DETECT_WT 는 heredoc 선언(<<'PYEOF_DETECT_WT')과 종료 마커 2회 등장.
        # 첫 등장: heredoc 선언 라인 (func_start 이후)
        # 두 번째 등장: heredoc 본문 끝 마커 라인
        first_pyeof = src.find("PYEOF_DETECT_WT", func_start)
        assert first_pyeof != -1, "PYEOF_DETECT_WT 첫 등장 없음"
        second_pyeof = src.find("PYEOF_DETECT_WT", first_pyeof + len("PYEOF_DETECT_WT"))
        assert second_pyeof != -1, "PYEOF_DETECT_WT 두 번째 등장(heredoc 종료 마커) 없음"

        # 두 번째 PYEOF_DETECT_WT 이후 첫 번째 `}` 위치를 함수 끝으로 삼음
        closing_brace = src.find("}", second_pyeof)
        assert closing_brace != -1, "_detect_task_worktree 함수 닫힘 } 없음"

        func_body = src[func_start:closing_brace + 1]

        assert "os.path.realpath(cur) == canon" in func_body, (
            "os.path.realpath(cur) == canon 가드가 _detect_task_worktree 함수 바깥에 있음.\n"
            f"추출된 함수 바디(첫 200자): {func_body[:200]!r}"
        )

    def test_backward_compat_canonical_path(self) -> None:
        """tier③ 블록이 빈 문자열 반환 시 PROJECT_PATH 미변경을 소스에서 확인.

        tier③ 블록은 `[ -n "$_WT_DETECTED" ]` 가드를 포함해야 한다.
        canonical-path task 는 _detect_task_worktree 가 빈 문자열을 반환하므로
        PROJECT_PATH 가 변경되지 않아야 한다 (backward-compat 보장).
        """
        src = _src()

        # tier③ 블록 내 빈문자열 가드 확인
        # 패턴: if [ -n "$_WT_DETECTED" ] ... PROJECT_PATH="$_WT_DETECTED"
        # 혹은 [ -n "$_WT_DETECTED" ] && [ -d "$_WT_DETECTED" ] && ... 복합 가드
        nonempty_guard_pattern = re.compile(
            r'\[\s*-n\s*"\$_WT_DETECTED"\s*\]'
        )
        assert nonempty_guard_pattern.search(src), (
            'tier③ 블록에 [ -n "$_WT_DETECTED" ] 가드 없음 — '
            '빈 문자열 반환 시 PROJECT_PATH 가 오염될 위험 (backward-compat 파손)'
        )

        # unset _WT_DETECTED 정리 확인 (메모리 오염 방지)
        assert "unset _WT_DETECTED" in src, (
            "tier③ 블록 후 'unset _WT_DETECTED' 없음 — 변수 누출 위험"
        )
