# pyright: reportUnusedVariable=false
"""
test_task_scope.py — task_scope.py 회귀 테스트 (Guard MVP Phase 1, task-2434)

시나리오 (7개):
    1. test_extension_only             — extension/foo.js → scopes=["extension"]
    2. test_server_only                — server/api.py → scopes=["server"]
    3. test_extension_and_skills       — extension/x + skills/y → scopes=["extension","skills"]
    4. test_docs_only                  — README.md → scopes=["docs"], test_paths=[]
    5. test_unscoped                   — 미분류 path → scopes=["unscoped"], test_paths=[]
    6. test_diff_sets_separation       — head_diff/index_staged/working_tree/untracked 4집합 분리
    7. test_output_files_written       — --output-dir 시 JSON 2개 파일 생성
"""

import json
import subprocess
import sys
from pathlib import Path

# ---------------------------------------------------------------------------
# 모듈 경로
# ---------------------------------------------------------------------------
_SCRIPTS_DIR = Path("/home/jay/workspace/scripts")
_WORKSPACE   = Path("/home/jay/workspace")

if str(_SCRIPTS_DIR) not in sys.path:
    sys.path.insert(0, str(_SCRIPTS_DIR))

# ---------------------------------------------------------------------------
# 헬퍼: 임시 git repo 초기화
# ---------------------------------------------------------------------------

def init_git_repo(path: Path) -> None:
    """tmp_path 하위에 최소 git repo를 생성한다."""
    def _git(*args: str) -> None:
        subprocess.run(
            ["git", "-C", str(path)] + list(args),
            check=True,
            capture_output=True,
        )

    _git("init")
    _git("config", "user.email", "test@test.com")
    _git("config", "user.name",  "Test")

    # 초기 커밋(base) 생성
    readme = path / "README.md"
    readme.write_text("init\n")
    _git("add", ".")
    _git("commit", "-m", "init")


def make_commit(path: Path, file_rel: str, content: str = "change\n") -> str:
    """파일 추가 + 커밋 후 HEAD SHA 반환."""
    target = path / file_rel
    target.parent.mkdir(parents=True, exist_ok=True)
    target.write_text(content)
    subprocess.run(["git", "-C", str(path), "add", file_rel],
                   check=True, capture_output=True)
    subprocess.run(["git", "-C", str(path), "commit", "-m", f"add {file_rel}"],
                   check=True, capture_output=True)
    result = subprocess.run(
        ["git", "-C", str(path), "rev-parse", "HEAD"],
        check=True, capture_output=True, text=True,
    )
    return result.stdout.strip()


def get_base_sha(path: Path) -> str:
    """초기 커밋 SHA(첫번째 커밋) 반환."""
    result = subprocess.run(
        ["git", "-C", str(path), "rev-list", "--max-parents=0", "HEAD"],
        check=True, capture_output=True, text=True,
    )
    return result.stdout.strip()


# ---------------------------------------------------------------------------
# 1. test_extension_only
# ---------------------------------------------------------------------------

def test_extension_only() -> None:
    """extension/foo.js 만 변경 → scopes=["extension"], test_paths=["extension/__tests__/"]"""
    from task_scope import classify_scopes, scope_test_matrix

    paths = ["extension/foo.js"]
    scopes = classify_scopes(paths)
    test_paths = scope_test_matrix(scopes)

    assert scopes == ["extension"], (
        f"expected ['extension'] but got {scopes}"
    )
    assert test_paths == ["extension/__tests__/"], (
        f"expected ['extension/__tests__/'] but got {test_paths}"
    )


# ---------------------------------------------------------------------------
# 2. test_server_only
# ---------------------------------------------------------------------------

def test_server_only() -> None:
    """server/api.py 만 변경 → scopes=["server"]"""
    from task_scope import classify_scopes, scope_test_matrix

    paths = ["server/api.py"]
    scopes = classify_scopes(paths)
    test_paths = scope_test_matrix(scopes)

    assert scopes == ["server"], (
        f"expected ['server'] but got {scopes}"
    )
    assert "server/tests/" in test_paths, (
        f"server/tests/ not in test_paths={test_paths}"
    )


# ---------------------------------------------------------------------------
# 3. test_extension_and_skills
# ---------------------------------------------------------------------------

def test_extension_and_skills() -> None:
    """extension/x + skills/y 동시 변경 → scopes=["extension","skills"] (sorted)"""
    from task_scope import classify_scopes

    paths = ["extension/background.js", "skills/summarizer.py"]
    scopes = classify_scopes(paths)

    assert scopes == ["extension", "skills"], (
        f"expected ['extension', 'skills'] but got {scopes}"
    )
    # sorted 확인
    assert scopes == sorted(scopes), f"scopes must be sorted, got {scopes}"


# ---------------------------------------------------------------------------
# 4. test_docs_only
# ---------------------------------------------------------------------------

def test_docs_only() -> None:
    """README.md 만 변경 → scopes=["docs"], test_paths=[] (smoke only)"""
    from task_scope import classify_scopes, scope_test_matrix

    paths = ["README.md"]
    scopes = classify_scopes(paths)
    test_paths = scope_test_matrix(scopes)

    assert scopes == ["docs"], f"expected ['docs'] but got {scopes}"
    assert test_paths == [], (
        f"docs scope should have no dedicated test path, got {test_paths}"
    )


# ---------------------------------------------------------------------------
# 5. test_unscoped
# ---------------------------------------------------------------------------

def test_unscoped() -> None:
    """매칭 안 되는 path → scopes=["unscoped"], test_paths=[]"""
    from task_scope import classify_scopes, scope_test_matrix

    paths = ["some/random/file.txt", "another.xyz"]
    scopes = classify_scopes(paths)
    test_paths = scope_test_matrix(scopes)

    assert scopes == ["unscoped"], f"expected ['unscoped'] but got {scopes}"
    assert test_paths == [], (
        f"unscoped should have no dedicated test path, got {test_paths}"
    )


# ---------------------------------------------------------------------------
# 6. test_diff_sets_separation
# ---------------------------------------------------------------------------

def test_diff_sets_separation(tmp_path: Path) -> None:
    """head_diff / index_staged / working_tree_modified / untracked 4집합이 명시 분리됨.

    - base_sha = 초기 커밋
    - 이후 커밋 1개 → head_diff에만 포함
    - staged 파일 → index_staged에만 포함
    - modified (unstaged) 파일 → working_tree_modified에만
    - untracked 파일 → untracked에만
    """
    from task_scope import get_diff_sets

    repo = tmp_path / "repo"
    repo.mkdir()
    init_git_repo(repo)
    base_sha = get_base_sha(repo)

    # 커밋된 파일 (head_diff에 포함되어야 함)
    make_commit(repo, "server/committed.py")

    # staged 파일 (아직 commit 안 함)
    staged_file = repo / "extension" / "staged.js"
    staged_file.parent.mkdir(parents=True, exist_ok=True)
    staged_file.write_text("staged\n")
    subprocess.run(["git", "-C", str(repo), "add", "extension/staged.js"],
                   check=True, capture_output=True)

    # modified (unstaged)
    modified_file = repo / "README.md"
    modified_file.write_text("modified content\n")
    # do NOT git add → working_tree_modified

    # untracked
    untracked_file = repo / "untracked_new.txt"
    untracked_file.write_text("untracked\n")

    diff_sets, err = get_diff_sets(base_sha, str(repo))

    assert err is None, f"get_diff_sets returned error: {err}"

    # head_diff: 커밋된 파일 포함
    assert "server/committed.py" in diff_sets["head_diff"], (
        f"committed file not in head_diff: {diff_sets['head_diff']}"
    )
    # head_diff: staged-only 파일은 포함되지 않아야 함
    assert "extension/staged.js" not in diff_sets["head_diff"], (
        f"staged-only file should not be in head_diff: {diff_sets['head_diff']}"
    )

    # index_staged: staged 파일 포함
    assert "extension/staged.js" in diff_sets["index_staged"], (
        f"staged file not in index_staged: {diff_sets['index_staged']}"
    )

    # working_tree_modified: 수정된 파일 포함
    assert "README.md" in diff_sets["working_tree_modified"], (
        f"modified file not in working_tree_modified: {diff_sets['working_tree_modified']}"
    )

    # untracked: untracked 파일 포함
    assert "untracked_new.txt" in diff_sets["untracked"], (
        f"untracked file not in untracked: {diff_sets['untracked']}"
    )

    # 4집합 키 전부 존재 확인
    for key in ("head_diff", "index_staged", "working_tree_modified", "untracked"):
        assert key in diff_sets, f"key '{key}' missing from diff_sets"


# ---------------------------------------------------------------------------
# 7. test_output_files_written
# ---------------------------------------------------------------------------

def test_output_files_written(tmp_path: Path) -> None:
    """--output-dir 인자 사용 시 changed_paths.json + scope_matrix.json 생성."""
    repo = tmp_path / "repo"
    repo.mkdir()
    init_git_repo(repo)
    base_sha = get_base_sha(repo)

    # extension 파일 커밋
    make_commit(repo, "extension/popup.js")

    output_dir = tmp_path / "output"

    result = subprocess.run(
        [
            sys.executable,
            str(_SCRIPTS_DIR / "task_scope.py"),
            "--base-sha", base_sha,
            "--output-dir", str(output_dir),
            "--cwd", str(repo),
        ],
        capture_output=True,
        text=True,
        cwd=str(_WORKSPACE),
    )

    assert result.returncode == 0, (
        f"task_scope.py exited with rc={result.returncode}\n"
        f"stdout={result.stdout}\nstderr={result.stderr}"
    )

    changed_paths_file = output_dir / "changed_paths.json"
    scope_matrix_file  = output_dir / "scope_matrix.json"

    assert changed_paths_file.exists(), (
        f"changed_paths.json not created in {output_dir}"
    )
    assert scope_matrix_file.exists(), (
        f"scope_matrix.json not created in {output_dir}"
    )

    with changed_paths_file.open() as f:
        cp = json.load(f)
    with scope_matrix_file.open() as f:
        sm = json.load(f)

    # changed_paths.json 구조 검증
    for key in ("base_sha", "head_sha", "head_diff", "index_staged",
                "working_tree_modified", "untracked"):
        assert key in cp, f"key '{key}' missing from changed_paths.json"

    # head_diff에 커밋된 extension 파일 포함
    assert "extension/popup.js" in cp["head_diff"], (
        f"extension/popup.js not in head_diff: {cp['head_diff']}"
    )

    # scope_matrix.json 구조 + 내용 검증
    assert "scopes" in sm, "key 'scopes' missing from scope_matrix.json"
    assert "test_paths" in sm, "key 'test_paths' missing from scope_matrix.json"
    assert "extension" in sm["scopes"], (
        f"extension not in scopes: {sm['scopes']}"
    )
    assert "extension/__tests__/" in sm["test_paths"], (
        f"extension/__tests__/ not in test_paths: {sm['test_paths']}"
    )
