"""task-2457 Phase 2-A: pre-commit hook 테스트.

명세(루가 동시 작성 중인 hook 본체):
  차단 케이스 (exit 1, stderr "[BLOCKED] ..."):
    1. .tasks/locks/<task-id>.lock 부재 → "start_task_guard not passed"
    2. 현재 branch == main → "main direct commit prohibited"
    3. branch가 task 패턴 아님 (예: feature/foo) → "branch does not match task pattern"
    4. branch task-id != lock task-id → "branch/lock task-id mismatch"

  PASS 케이스 (exit 0):
    P1. lock 존재 + branch task-id 일치 + main 아님
    P2. TASKCTL_BYPASS=1 + TASKCTL_BYPASS_REASON 제공 → bypass evidence 기록 + PASS

  bypass evidence 검증 (.tasks/evidence/<task-id>/bypass-<ts>.json):
    - bypass=true / timestamp(ISO8601) / actor=$USER / reason=$TASKCTL_BYPASS_REASON
    - reason 누락 시 → 가드 자체 FAIL (exit 1)
"""

from __future__ import annotations

import json
import os


# ---------- 차단 케이스 ----------


class TestPreCommitFail:
    def test_lock_missing_blocks(self, git_repo, checkout_branch, run_hook):
        """1) lock 파일이 없으면 차단."""
        checkout_branch(git_repo, "task/task-2457-dev3")
        # .tasks/locks/ 비워둠
        result = run_hook(git_repo, "pre-commit")
        assert result.returncode == 1, (
            f"lock 부재인데 통과됨. stdout={result.stdout!r} stderr={result.stderr!r}"
        )
        assert "[BLOCKED]" in result.stderr
        assert "start_task_guard not passed" in result.stderr

    def test_main_branch_blocks(self, git_repo, run_hook):
        """2) main 브랜치 직접 commit은 차단."""
        # git_repo 픽스처는 main 브랜치 상태로 시작
        result = run_hook(git_repo, "pre-commit")
        assert result.returncode == 1, (
            f"main commit인데 통과됨. stdout={result.stdout!r} stderr={result.stderr!r}"
        )
        assert "[BLOCKED]" in result.stderr
        assert "main direct commit prohibited" in result.stderr

    def test_non_task_branch_blocks(
        self, git_repo, checkout_branch, make_lock, run_hook
    ):
        """3) task 패턴이 아닌 branch (feature/foo) 차단."""
        checkout_branch(git_repo, "feature/foo")
        # lock이 있어도 branch 패턴이 틀리면 차단
        make_lock(git_repo, task_id="task-2457", branch="feature/foo")
        result = run_hook(git_repo, "pre-commit")
        assert result.returncode == 1, (
            f"task 패턴 아닌데 통과됨. stdout={result.stdout!r} stderr={result.stderr!r}"
        )
        assert "[BLOCKED]" in result.stderr
        assert "branch does not match task pattern" in result.stderr

    def test_branch_lock_mismatch_blocks(
        self, git_repo, checkout_branch, make_lock, run_hook
    ):
        """4) branch task-id != lock 내부 task_id → 차단.

        주의: hook은 lock 파일을 `.tasks/locks/<branch-task-id>.lock` 경로로 찾는다.
        진짜 mismatch 시나리오는 그 파일이 존재하면서 내부 task_id 필드가
        다른 값을 가진 경우다.
        """
        checkout_branch(git_repo, "task/task-1000-dev3")
        # 파일 이름은 task-1000.lock 이지만, 내부 task_id 필드는 task-2000
        # → hook이 파싱 후 mismatch 감지
        make_lock(
            git_repo,
            task_id="task-1000",
            branch="task/task-1000-dev3",
        )
        # 내부 필드 task_id 를 강제로 task-2000 으로 바꾼다
        lock_path = git_repo / ".tasks" / "locks" / "task-1000.lock"
        payload = json.loads(lock_path.read_text(encoding="utf-8"))
        payload["task_id"] = "task-2000"
        lock_path.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8")

        result = run_hook(git_repo, "pre-commit")
        assert result.returncode == 1, (
            f"branch/lock 불일치인데 통과됨. stdout={result.stdout!r} stderr={result.stderr!r}"
        )
        assert "[BLOCKED]" in result.stderr
        assert "branch/lock task-id mismatch" in result.stderr


# ---------- PASS 케이스 ----------


class TestPreCommitPass:
    def test_valid_lock_branch_passes(
        self, git_repo, checkout_branch, make_lock, run_hook
    ):
        """P1) lock 존재 + branch task-id 일치 + main 아님 → PASS."""
        branch = "task/task-2457-dev3"
        checkout_branch(git_repo, branch)
        make_lock(git_repo, task_id="task-2457", branch=branch)
        result = run_hook(git_repo, "pre-commit")
        assert result.returncode == 0, (
            f"정상 케이스인데 차단됨. stdout={result.stdout!r} stderr={result.stderr!r}"
        )

    def test_bypass_with_reason_passes_and_writes_evidence(
        self, git_repo, checkout_branch, run_hook
    ):
        """P2) TASKCTL_BYPASS=1 + reason 제공 → PASS + evidence 기록."""
        branch = "task/task-2457-dev3"
        checkout_branch(git_repo, branch)
        # bypass 모드에서는 lock 없어도 통과해야 한다
        env = {
            "TASKCTL_BYPASS": "1",
            "TASKCTL_BYPASS_REASON": "emergency hotfix for chairman demo",
        }
        result = run_hook(git_repo, "pre-commit", env_extra=env)
        assert result.returncode == 0, (
            f"bypass 통과 못함. stdout={result.stdout!r} stderr={result.stderr!r}"
        )

        # evidence 파일 생성 확인
        evidence_dir = git_repo / ".tasks" / "evidence" / "task-2457"
        assert evidence_dir.is_dir(), "evidence 디렉토리가 만들어지지 않음"
        bypass_files = list(evidence_dir.glob("bypass-*.json"))
        assert bypass_files, f"bypass-*.json evidence 파일이 없음. dir={list(evidence_dir.iterdir())}"

        # JSON 4필드 검증
        data = json.loads(bypass_files[0].read_text(encoding="utf-8"))
        assert data.get("bypass") is True, f"bypass 필드 != true: {data}"
        assert data.get("timestamp"), f"timestamp 누락: {data}"
        # ISO8601 (대략 형식) 체크
        ts = data["timestamp"]
        assert "T" in ts and (ts.endswith("Z") or "+" in ts or "-" in ts[10:]), (
            f"timestamp 가 ISO8601 형식 아님: {ts!r}"
        )
        assert data.get("actor") == os.environ.get("USER"), (
            f"actor 가 $USER 와 불일치: {data.get('actor')} vs {os.environ.get('USER')}"
        )
        assert data.get("reason") == "emergency hotfix for chairman demo", (
            f"reason 불일치: {data.get('reason')!r}"
        )


# ---------- bypass evidence 음성 케이스 ----------


class TestPreCommitBypassEvidence:
    def test_bypass_without_reason_blocks(
        self, git_repo, checkout_branch, run_hook
    ):
        """TASKCTL_BYPASS=1 만 있고 reason 없음 → 가드 자체 FAIL (exit 1)."""
        branch = "task/task-2457-dev3"
        checkout_branch(git_repo, branch)
        env = {"TASKCTL_BYPASS": "1"}
        # TASKCTL_BYPASS_REASON 일부러 지우기
        env_full = {**env}
        # subprocess 의 env에는 TASKCTL_BYPASS_REASON이 들어가지 않도록 부재 보장
        # run_hook 내부에서 os.environ.copy() 후 update 하므로, 호출 측에서
        # 미리 환경에 없도록 명시 (pop 헬퍼 대신 부재 가정)
        os.environ.pop("TASKCTL_BYPASS_REASON", None)
        result = run_hook(git_repo, "pre-commit", env_extra=env_full)
        assert result.returncode == 1, (
            f"reason 없는 bypass인데 통과됨. stdout={result.stdout!r} stderr={result.stderr!r}"
        )
        assert "[BLOCKED]" in result.stderr
        # 메시지 키워드(명세 모호 → reason 키워드만 확인)
        assert "reason" in result.stderr.lower()
