#!/usr/bin/env python3
"""
test_stash_lifecycle_legacy.py
task: task-2571 TODO-7 T-4

검증 목표:
- spec §9 legacy stash 호환성 검증
- legacy 포맷 (표준 메타데이터 없는 stash) 은 source=unknown 으로 분류
- unknown 분기 적용 시 모두 preserve

작성자: 하누만 (개발4팀 QA)
"""

import json
import os
import shutil
import subprocess
import sys
import tempfile
from datetime import datetime, timezone
from pathlib import Path

import pytest

WORKTREE_ROOT = Path(__file__).resolve().parents[2]
STASH_AUDIT_PY = WORKTREE_ROOT / "scripts" / "stash_audit.py"


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

def _init_temp_repo() -> str:
    """격리된 임시 git repo 초기화."""
    d = tempfile.mkdtemp(prefix="stash-lifecycle-test-")
    env = _git_env()
    subprocess.run(["git", "init", "-q", "-b", "main"], cwd=d, check=True, env=env)
    subprocess.run(["git", "config", "user.email", "test@example.com"], cwd=d, check=True, env=env)
    subprocess.run(["git", "config", "user.name", "test"], cwd=d, check=True, env=env)
    (Path(d) / "a.txt").write_text("hello")
    subprocess.run(["git", "add", "a.txt"], cwd=d, check=True, env=env)
    subprocess.run(["git", "commit", "-q", "-m", "init"], cwd=d, check=True, env=env)
    return d


def _git_env() -> dict:
    """git 명령 실행용 환경변수."""
    env = os.environ.copy()
    env["GIT_AUTHOR_NAME"] = "test"
    env["GIT_AUTHOR_EMAIL"] = "test@example.com"
    env["GIT_COMMITTER_NAME"] = "test"
    env["GIT_COMMITTER_EMAIL"] = "test@example.com"
    return env


def _stash_push(repo_dir: str, message: str, filename: str) -> None:
    """파일 하나 dirty 상태로 만들고 stash push."""
    env = _git_env()
    fpath = Path(repo_dir) / filename
    fpath.write_text(f"dirty: {message}\n")
    subprocess.run(["git", "add", filename], cwd=repo_dir, check=True, env=env)
    subprocess.run(["git", "stash", "push", "-m", message], cwd=repo_dir, check=True, env=env)


def _run_audit_json(repo_dir: str) -> dict:
    """stash_audit.py --json 실행 후 파싱된 dict 반환."""
    result = subprocess.run(
        [sys.executable, str(STASH_AUDIT_PY), "--json", "--workspace", repo_dir],
        capture_output=True,
        text=True,
    )
    assert result.returncode == 0, (
        f"stash_audit.py 실행 실패 (exit={result.returncode})\n"
        f"stderr: {result.stderr}"
    )
    return json.loads(result.stdout)


# ---------------------------------------------------------------------------
# spec §2 정책 결정 reference implementation
# ---------------------------------------------------------------------------

def decide_action(
    source: str,
    approve: bool,
    pr_verified: bool,
    idx_in_drop_list: bool,
    etid: str | None,
    task_id: str | None,
) -> str:
    """spec §2 결정 흐름 reference implementation."""
    if source == "pre-task":
        return "popped" if approve else "dry-run-pop"

    if source == "finish-task":
        if approve and pr_verified and (etid == task_id):
            return "popped"
        if not approve:
            return "dry-run-pop"
        return "preserved"

    if source == "other-files":
        if approve and idx_in_drop_list:
            return "dropped"
        return "dry-run-drop"

    if source in ("wip", "quarantine", "unknown"):
        return "preserved"

    return "skipped"


def simulate_dispatch(entries: list[dict], approve: bool, task_id: str) -> dict:
    """spec §2/§3 기반 dispatch 시뮬레이션."""
    decisions = []
    skipped_unknown_count = 0

    for e in entries:
        source = e["source"]
        etid = e.get("task_id")
        idx = e["index"]

        action = decide_action(
            source=source,
            approve=approve,
            pr_verified=False,
            idx_in_drop_list=False,
            etid=etid,
            task_id=task_id,
        )

        if source == "unknown":
            skipped_unknown_count += 1

        decisions.append({
            "index": idx,
            "source": source,
            "task_id": etid,
            "action": action,
        })

    return {
        "timestamp_utc": datetime.now(timezone.utc).isoformat(),
        "task_id": task_id,
        "approval_mode": "approved" if approve else "dry-run",
        "stash_count_before": len(entries),
        "decisions": decisions,
        "skipped_unknown_count": skipped_unknown_count,
    }


# ---------------------------------------------------------------------------
# Legacy stash 포맷 정의
# spec §2.2: 표준 메타데이터 없는 포맷 → source=unknown
# ---------------------------------------------------------------------------

LEGACY_STASH_MESSAGES = [
    # git이 자동 생성하는 legacy 포맷 시뮬레이션
    # (실제 git stash 는 "On main: <msg>" 또는 "WIP on main: <SHA> <msg>" 포맷 사용)
    # stash_audit.py 에서는 패턴 매칭 실패 시 unknown 으로 분류
    "some old stash from 2025",
    "My work before vacation",
    "temp changes backup 20240301",
]


# ---------------------------------------------------------------------------
# Fixture
# ---------------------------------------------------------------------------

@pytest.fixture()
def legacy_repo():
    """legacy 포맷 stash 3건 시드된 임시 repo."""
    repo_dir = _init_temp_repo()

    for i, msg in enumerate(LEGACY_STASH_MESSAGES):
        _stash_push(repo_dir, msg, f"legacy_{i}.txt")

    yield repo_dir

    shutil.rmtree(repo_dir, ignore_errors=True)


# ---------------------------------------------------------------------------
# 테스트
# ---------------------------------------------------------------------------

def test_legacy_all_stashes_detected(legacy_repo):
    """legacy stash 3건이 모두 감지되어야 한다."""
    data = _run_audit_json(legacy_repo)
    entries = data.get("entries", [])
    assert len(entries) == 3, (
        f"3건 시드 후 {len(entries)}건 감지됨"
    )


def test_legacy_all_classified_as_unknown(legacy_repo):
    """
    legacy 포맷 stash 3건 모두 source=unknown 으로 분류되어야 한다.
    spec §2.2: 패턴 매칭 실패 → source=unknown → quarantine.
    """
    data = _run_audit_json(legacy_repo)
    entries = data.get("entries", [])

    for entry in entries:
        assert entry["source"] == "unknown", (
            f"legacy stash 가 unknown 이 아닌 '{entry['source']}' 로 분류됨\n"
            f"raw_message: {entry.get('raw_message', '')}"
        )


def test_legacy_dispatch_all_preserved(legacy_repo):
    """dispatch 정책 적용 시 legacy stash 전부 action=preserved 이어야 한다."""
    data = _run_audit_json(legacy_repo)
    entries = data.get("entries", [])

    # dry-run 모드
    log_dry = simulate_dispatch(entries, approve=False, task_id="task-2571")
    for d in log_dry["decisions"]:
        assert d["action"] == "preserved", (
            f"legacy (dry-run) action 기대: preserved, 실제: {d['action']}"
        )

    # approved 모드에서도 unknown 은 preserve
    log_approved = simulate_dispatch(entries, approve=True, task_id="task-2571")
    for d in log_approved["decisions"]:
        assert d["action"] == "preserved", (
            f"legacy (approved) action 기대: preserved, 실제: {d['action']}"
        )


def test_legacy_skipped_unknown_count_is_3(legacy_repo):
    """skipped_unknown_count 가 3이어야 한다."""
    data = _run_audit_json(legacy_repo)
    entries = data.get("entries", [])
    log = simulate_dispatch(entries, approve=False, task_id="task-2571")

    assert log["skipped_unknown_count"] == 3, (
        f"skipped_unknown_count 기대: 3, 실제: {log['skipped_unknown_count']}"
    )


def test_legacy_no_task_id_extracted(legacy_repo):
    """legacy stash 메시지에서 task_id 가 None 이어야 한다."""
    data = _run_audit_json(legacy_repo)
    entries = data.get("entries", [])

    for entry in entries:
        assert entry.get("task_id") is None, (
            f"legacy stash 에서 예상치 못한 task_id 추출됨: {entry.get('task_id')}\n"
            f"raw_message: {entry.get('raw_message', '')}"
        )


def test_legacy_entries_have_raw_message(legacy_repo):
    """legacy entries 에 raw_message 필드가 있어야 한다."""
    data = _run_audit_json(legacy_repo)
    entries = data.get("entries", [])

    for entry in entries:
        assert "raw_message" in entry, (
            f"entry 에 raw_message 없음: {entry}"
        )
        assert entry["raw_message"], "raw_message 가 빈 문자열임"


def test_legacy_audit_json_summary_has_unknown_count(legacy_repo):
    """summary.count_by_source 에 unknown 카운트가 3이어야 한다."""
    data = _run_audit_json(legacy_repo)
    summary = data.get("summary", {})
    count_by_source = 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']}"
    )
