"""
regression: test_stash_origin_audit_compat.py
task: task-2570 TODO-4
작성자: 하누만(테스터)

검증 목표:
1. 기존/unknown stash가 'unknown'으로 fallback 분류되는지 검증
2. finish-task.sh가 stash_audit.py 미존재 환경에서도 동작하는지 검증
   (bash -n 통과 + `if [ -x` 조건문 존재)
3. 기존 _STASH_AUDIT_BEFORE / _STASH_AUDIT_AFTER 변수 + cleanup-audit.jsonl 경로 보존
   (task-2569 호환성)
"""

import json
import os
import re
import subprocess
import sys
from pathlib import Path

import pytest

WORKTREE_ROOT = Path(__file__).resolve().parents[2]
STASH_AUDIT_PY = WORKTREE_ROOT / "scripts" / "stash_audit.py"
FINISH_TASK_SH = WORKTREE_ROOT / "scripts" / "finish-task.sh"


# ---------------------------------------------------------------------------
# Helper
# ---------------------------------------------------------------------------

def _run_audit(args: list, cwd: str | None = None) -> subprocess.CompletedProcess:
    """stash_audit.py 호출 helper."""
    cmd = [sys.executable, str(STASH_AUDIT_PY)] + args
    return subprocess.run(
        cmd,
        capture_output=True,
        text=True,
        cwd=cwd or str(WORKTREE_ROOT),
    )


def _git(args: list, cwd: str, check: bool = True) -> subprocess.CompletedProcess:
    """git 명령 helper (tmp repo 전용)."""
    env = os.environ.copy()
    env["GIT_AUTHOR_NAME"] = "test"
    env["GIT_AUTHOR_EMAIL"] = "test@test.com"
    env["GIT_COMMITTER_NAME"] = "test"
    env["GIT_COMMITTER_EMAIL"] = "test@test.com"
    return subprocess.run(
        ["git"] + args,
        cwd=cwd,
        capture_output=True,
        text=True,
        check=check,
        env=env,
    )


@pytest.fixture()
def repo_with_unknown_stash(tmp_path):
    """
    패턴 매칭이 안 되는 unknown stash만 있는 isolated git repo.
    """
    repo = str(tmp_path)
    _git(["init", "-b", "main"], cwd=repo)
    _git(["config", "user.email", "test@test.com"], cwd=repo)
    _git(["config", "user.name", "test"], cwd=repo)
    _git(["commit", "--allow-empty", "-m", "init: initial commit"], cwd=repo)

    # 표준 포맷을 전혀 따르지 않는 stash 메시지들
    unknown_messages = [
        "some random work in progress",
        "backup before experiment",
        "temp save 2026-05-14",
    ]
    for i, msg in enumerate(unknown_messages):
        fname = f"unknown_{i}.txt"
        fpath = tmp_path / fname
        fpath.write_text(f"content {i}\n", encoding="utf-8")
        _git(["add", fname], cwd=repo)
        _git(["stash", "push", "-m", msg], cwd=repo)

    return repo


# ---------------------------------------------------------------------------
# 1. unknown stash fallback 분류 검증
# ---------------------------------------------------------------------------

def test_unknown_stash_fallback_classification(repo_with_unknown_stash):
    """
    표준 패턴을 따르지 않는 stash는 'unknown'으로 fallback 분류되어야 한다.
    """
    result = _run_audit(["--json", "--workspace", repo_with_unknown_stash])
    assert result.returncode == 0, (
        f"stash_audit.py 실행 실패\nstderr: {result.stderr}"
    )
    data = json.loads(result.stdout)
    entries = data.get("entries", data.get("stashes", []))
    assert len(entries) > 0, "entries가 비어있음"

    sources = [e.get("source") for e in entries]
    # 모든 항목이 unknown이어야 함
    non_unknown = [s for s in sources if s != "unknown"]
    assert len(non_unknown) == 0, (
        f"unknown 외의 분류가 존재함: {non_unknown}\n"
        f"전체 sources: {sources}"
    )


def test_unknown_stash_count_by_source_has_unknown(repo_with_unknown_stash):
    """summary.count_by_source에 'unknown' 카테고리가 있어야 한다."""
    result = _run_audit(["--json", "--workspace", repo_with_unknown_stash])
    assert result.returncode == 0
    data = json.loads(result.stdout)
    count_by_source = data.get("summary", {}).get("count_by_source", {})
    assert "unknown" in count_by_source, (
        f"count_by_source에 'unknown'이 없음: {count_by_source}"
    )
    assert count_by_source["unknown"] >= 3, (
        f"unknown count가 3 미만: {count_by_source['unknown']}"
    )


def test_empty_repo_unknown_fallback(tmp_path):
    """
    stash가 없는 repo에서 실행 시 entries가 빈 리스트여야 한다.
    """
    repo = str(tmp_path)
    _git(["init", "-b", "main"], cwd=repo)
    _git(["config", "user.email", "test@test.com"], cwd=repo)
    _git(["config", "user.name", "test"], cwd=repo)
    _git(["commit", "--allow-empty", "-m", "init"], cwd=repo)

    result = _run_audit(["--json", "--workspace", repo])
    assert result.returncode == 0
    data = json.loads(result.stdout)
    entries = data.get("entries", data.get("stashes", []))
    assert entries == [], (
        f"stash가 없는 repo에서 entries가 비어있지 않음: {entries}"
    )


def test_unknown_stash_caller_script_is_unknown(repo_with_unknown_stash):
    """unknown으로 분류된 stash의 caller_script는 'unknown'이어야 한다."""
    result = _run_audit(["--json", "--workspace", repo_with_unknown_stash])
    assert result.returncode == 0
    data = json.loads(result.stdout)
    entries = data.get("entries", data.get("stashes", []))
    for entry in entries:
        if entry.get("source") == "unknown":
            assert entry.get("caller_script") == "unknown", (
                f"unknown source의 caller_script가 'unknown'이 아님: "
                f"{entry.get('caller_script')}"
            )


# ---------------------------------------------------------------------------
# 2. finish-task.sh — stash_audit.py 미존재 환경에서도 동작 검증
# ---------------------------------------------------------------------------

def test_finish_task_sh_bash_syntax_check():
    """
    finish-task.sh가 bash -n (문법 검사) 를 통과해야 한다.
    """
    result = subprocess.run(
        ["bash", "-n", str(FINISH_TASK_SH)],
        capture_output=True,
        text=True,
    )
    assert result.returncode == 0, (
        f"bash -n 문법 검사 실패\n"
        f"stderr: {result.stderr}\n"
        f"파일: {FINISH_TASK_SH}"
    )


def test_finish_task_sh_has_if_x_guard_for_stash_audit():
    """
    finish-task.sh에 `if [ -x ... stash_audit.py ]` 패턴이 존재해야 한다.
    stash_audit.py가 없어도 graceful하게 동작함을 보장.
    """
    content = FINISH_TASK_SH.read_text(encoding="utf-8")
    # `if [ -x ...stash_audit.py` 패턴
    pattern = re.compile(r'if\s+\[\s+-x\s+.*stash_audit\.py')
    assert pattern.search(content), (
        "finish-task.sh에 `if [ -x ... stash_audit.py ]` 조건문이 없음\n"
        "stash_audit.py 미존재 환경에서 스크립트가 실패할 수 있음"
    )


def test_finish_task_sh_stash_audit_guard_appears_twice():
    """
    `if [ -x ... stash_audit.py ]` 가드가 최소 2번 (BEFORE/AFTER 각각) 존재해야 한다.
    """
    content = FINISH_TASK_SH.read_text(encoding="utf-8")
    pattern = re.compile(r'if\s+\[\s+-x\s+.*stash_audit\.py')
    matches = pattern.findall(content)
    assert len(matches) >= 2, (
        f"`if [ -x ... stash_audit.py ]` 가드가 {len(matches)}번만 존재 (최소 2번 필요)\n"
        f"BEFORE/AFTER 블록 각각에 가드가 있어야 함"
    )


def test_stash_audit_callable_without_workspace_stash_side_effects():
    """
    stash_audit.py 호출이 실제 워크스페이스 stash에 side effect를 주지 않는지 검증.
    (pop/drop 금지 — 호출 전후 stash count 동일해야 함)
    """
    # 워크스페이스의 현재 stash 수 기록
    before_result = subprocess.run(
        ["git", "stash", "list"],
        cwd=str(WORKTREE_ROOT),
        capture_output=True,
        text=True,
    )
    before_count = len(before_result.stdout.strip().splitlines()) if before_result.stdout.strip() else 0

    # stash_audit.py 실행
    _run_audit(["--json", "--workspace", str(WORKTREE_ROOT)])

    # 실행 후 stash 수 확인
    after_result = subprocess.run(
        ["git", "stash", "list"],
        cwd=str(WORKTREE_ROOT),
        capture_output=True,
        text=True,
    )
    after_count = len(after_result.stdout.strip().splitlines()) if after_result.stdout.strip() else 0

    assert before_count == after_count, (
        f"stash_audit.py 실행 후 stash 수가 변경됨: {before_count} → {after_count}\n"
        f"read-only 원칙 위반 (pop/drop 가능성)"
    )


# ---------------------------------------------------------------------------
# 3. task-2569 호환성: _STASH_AUDIT_BEFORE/AFTER + cleanup-audit.jsonl
# ---------------------------------------------------------------------------

def test_finish_task_sh_stash_audit_before_variable_preserved():
    """
    task-2569 호환성: _STASH_AUDIT_BEFORE 변수가 finish-task.sh에 보존되어야 한다.
    """
    content = FINISH_TASK_SH.read_text(encoding="utf-8")
    assert "_STASH_AUDIT_BEFORE" in content, (
        "finish-task.sh에 _STASH_AUDIT_BEFORE 변수가 없음 (task-2569 호환성 깨짐)"
    )


def test_finish_task_sh_stash_audit_after_variable_preserved():
    """
    task-2569 호환성: _STASH_AUDIT_AFTER 변수가 finish-task.sh에 보존되어야 한다.
    """
    content = FINISH_TASK_SH.read_text(encoding="utf-8")
    assert "_STASH_AUDIT_AFTER" in content, (
        "finish-task.sh에 _STASH_AUDIT_AFTER 변수가 없음 (task-2569 호환성 깨짐)"
    )


def test_finish_task_sh_cleanup_audit_jsonl_path_preserved():
    """
    task-2569 호환성: cleanup-audit.jsonl 경로가 finish-task.sh에 보존되어야 한다.
    """
    content = FINISH_TASK_SH.read_text(encoding="utf-8")
    assert "cleanup-audit.jsonl" in content, (
        "finish-task.sh에 cleanup-audit.jsonl 경로가 없음 (task-2569 호환성 깨짐)"
    )


def test_finish_task_sh_audit_start_log_format():
    """
    finish-task.sh의 BEFORE 블록이 'audit': 'finish-task-start' 포맷을 포함해야 한다.
    """
    content = FINISH_TASK_SH.read_text(encoding="utf-8")
    assert "finish-task-start" in content, (
        "BEFORE 블록에 'finish-task-start' audit 레코드가 없음"
    )


def test_finish_task_sh_audit_end_log_format():
    """
    finish-task.sh의 AFTER 블록이 'audit': 'finish-task-end' 포맷을 포함해야 한다.
    """
    content = FINISH_TASK_SH.read_text(encoding="utf-8")
    assert "finish-task-end" in content, (
        "AFTER 블록에 'finish-task-end' audit 레코드가 없음"
    )


def test_finish_task_sh_stash_before_greater_than_five_warn():
    """
    finish-task.sh에 stash 5개 초과 시 WARN 로직이 있어야 한다 (task-2569 호환).
    """
    content = FINISH_TASK_SH.read_text(encoding="utf-8")
    # -gt 5 패턴 + WARN 포함
    pattern = re.compile(r'-gt\s+5')
    assert pattern.search(content), (
        "finish-task.sh에 stash 5개 초과 WARN 조건(-gt 5)이 없음"
    )
    assert "WARN" in content and "stash" in content.lower(), (
        "finish-task.sh에 stash 과다 WARN 메시지가 없음"
    )


def test_finish_task_sh_stash_count_assignment_before():
    """
    _STASH_AUDIT_BEFORE가 `git stash list | wc -l` 형태로 할당되어야 한다.
    """
    content = FINISH_TASK_SH.read_text(encoding="utf-8")
    # _STASH_AUDIT_BEFORE=... 할당 라인에 git stash list 포함 여부
    pattern = re.compile(
        r'_STASH_AUDIT_BEFORE\s*=\s*\$\(.*git stash list.*wc -l.*\)',
        re.DOTALL,
    )
    assert pattern.search(content), (
        "_STASH_AUDIT_BEFORE가 `git stash list | wc -l` 형태로 할당되지 않음"
    )


def test_finish_task_sh_stash_count_assignment_after():
    """
    _STASH_AUDIT_AFTER가 `git stash list | wc -l` 형태로 할당되어야 한다.
    """
    content = FINISH_TASK_SH.read_text(encoding="utf-8")
    pattern = re.compile(
        r'_STASH_AUDIT_AFTER\s*=\s*\$\(.*git stash list.*wc -l.*\)',
        re.DOTALL,
    )
    assert pattern.search(content), (
        "_STASH_AUDIT_AFTER가 `git stash list | wc -l` 형태로 할당되지 않음"
    )
