"""
test_finish_loop_fix.py - task-2348 시뮬레이션 검증

MT-1: git_evidence.py 워크트리 자동 인식
MT-2: qc_verify.py 같은 사유 3회 즉시 escalate
MT-3: (동기화 완료 검증)
MT-4: 5회 시뮬레이션 포함
"""

import json
import os
import sys
import pytest

# 경로 설정
WORKSPACE = "/home/jay/workspace"
TEAMS_DIR = os.path.join(WORKSPACE, "teams")

# git_evidence 임포트
sys.path.insert(0, os.path.join(TEAMS_DIR, "shared", "verifiers"))
sys.path.insert(0, os.path.join(TEAMS_DIR, "dev1", "qc"))

import git_evidence
import qc_verify


# ─── MT-1 시나리오 1: 환경변수 우선 ─────────────────────────────

def test_env_var_project_path_takes_priority(tmp_path, monkeypatch):
    """PROJECT_PATH 환경변수가 있으면 그 경로를 사용."""
    # 임시 프로젝트 디렉토리에 .git 생성
    fake_project = tmp_path / "fake_project"
    fake_project.mkdir()
    (fake_project / ".git").mkdir()

    monkeypatch.setenv("PROJECT_PATH", str(fake_project))
    monkeypatch.delenv("WORKTREE_PATH", raising=False)

    resolved = git_evidence._resolve_project_dir("task-test", str(tmp_path))
    assert resolved == str(fake_project), f"Expected {fake_project}, got {resolved}"


def test_env_var_worktree_path_takes_priority(tmp_path, monkeypatch):
    """WORKTREE_PATH 환경변수가 있으면 그 경로를 사용."""
    fake_worktree = tmp_path / "fake_worktree"
    fake_worktree.mkdir()
    (fake_worktree / ".git").touch()  # worktree의 .git은 파일일 수 있음

    monkeypatch.delenv("PROJECT_PATH", raising=False)
    monkeypatch.setenv("WORKTREE_PATH", str(fake_worktree))

    resolved = git_evidence._resolve_project_dir("task-test", str(tmp_path))
    assert resolved == str(fake_worktree), f"Expected {fake_worktree}, got {resolved}"


# ─── MT-1 시나리오 2: task-timers.json fallback ──────────────────

def test_task_timers_json_fallback(tmp_path, monkeypatch):
    """task-timers.json에서 worktree_path로 폴백."""
    monkeypatch.delenv("PROJECT_PATH", raising=False)
    monkeypatch.delenv("WORKTREE_PATH", raising=False)

    # 임시 workspace 구조 생성
    memory_dir = tmp_path / "memory"
    memory_dir.mkdir()
    fake_project = tmp_path / "my_project"
    fake_project.mkdir()

    timers = {
        "task-timer-test": {
            "worktree_path": str(fake_project),
        }
    }
    timers_path = memory_dir / "task-timers.json"
    timers_path.write_text(json.dumps(timers))

    resolved = git_evidence._resolve_project_dir("task-timer-test", str(tmp_path))
    assert resolved == str(fake_project), f"Expected {fake_project}, got {resolved}"


# ─── MT-1 시나리오 3: task 파일 fallback ─────────────────────────

def test_task_file_fallback(tmp_path, monkeypatch):
    """task 파일에서 '워크트리 경로:' 파싱."""
    monkeypatch.delenv("PROJECT_PATH", raising=False)
    monkeypatch.delenv("WORKTREE_PATH", raising=False)

    memory_dir = tmp_path / "memory"
    tasks_dir = memory_dir / "tasks"
    tasks_dir.mkdir(parents=True)

    fake_project = tmp_path / "parsed_project"
    fake_project.mkdir()

    # task 파일에 워크트리 경로 기입
    task_content = f"# task-file-test\n\n워크트리 경로: {str(fake_project)}\n\n## 설명\n본문"
    task_file = tasks_dir / "task-file-test.md"
    task_file.write_text(task_content)

    # task-timers.json 없음 (빈 파일)
    timers_path = memory_dir / "task-timers.json"
    timers_path.write_text(json.dumps({}))

    resolved = git_evidence._resolve_project_dir("task-file-test", str(tmp_path))
    assert resolved == str(fake_project), f"Expected {fake_project}, got {resolved}"


# ─── MT-1 시나리오 4: 폴백 (workspace) ──────────────────────────

def test_workspace_fallback(tmp_path, monkeypatch):
    """어느 것도 없으면 workspace_root 기반 폴백 사용."""
    monkeypatch.delenv("PROJECT_PATH", raising=False)
    monkeypatch.delenv("WORKTREE_PATH", raising=False)

    # 빈 timers
    memory_dir = tmp_path / "memory"
    memory_dir.mkdir()
    (memory_dir / "task-timers.json").write_text(json.dumps({}))

    resolved = git_evidence._resolve_project_dir("task-nonexistent", str(tmp_path))
    # 폴백은 git rev-parse 실패 시 workspace_root 반환
    assert os.path.isdir(resolved) or resolved == str(tmp_path)


# ─── MT-2: 같은 verifier 3회 FAIL → escalate ────────────────────

def test_same_verifier_3_times_escalate(tmp_path, monkeypatch):
    """같은 verifier 3회 FAIL 시 escalate 파일 생성 확인."""
    # 임시 counter_dir 설정
    counter_dir = tmp_path / "logs" / "retry-counters"
    counter_dir.mkdir(parents=True)
    events_dir = tmp_path / "events"
    events_dir.mkdir()

    task_id = "task-sim-escalate"
    monkeypatch.setattr(
        "qc_verify._get_failed_check_history",
        lambda tid: _build_fake_history(counter_dir, tid),
    )
    monkeypatch.setattr(
        "qc_verify._record_failed_checks",
        lambda tid, names: _write_fake_history(counter_dir, tid, names),
    )
    monkeypatch.setenv("WORKSPACE_ROOT", str(tmp_path))

    failed_names = ["git_evidence"]

    # 1회 FAIL 기록
    _write_fake_history(counter_dir, task_id, failed_names)
    # 2회 FAIL 기록
    _write_fake_history(counter_dir, task_id, failed_names)

    # 이제 3회째 시뮬레이션 — _handle_gate 내부 로직을 직접 재현
    history = qc_verify._get_failed_check_history(task_id)
    qc_verify._record_failed_checks(task_id, failed_names)
    current_key = json.dumps(sorted(failed_names))
    same_count = history.count(current_key) + 1

    escalate_path = events_dir / f"{task_id}.escalate"

    if same_count >= 3:
        escalate_data = {
            "task_id": task_id,
            "reason": f"동일 verifier 3회 연속 FAIL: {', '.join(sorted(failed_names))}",
            "same_fail_count": same_count,
        }
        escalate_path.write_text(json.dumps(escalate_data))

    assert escalate_path.exists(), "3회 연속 FAIL 시 escalate 파일이 생성되어야 함"
    data = json.loads(escalate_path.read_text())
    assert "동일 verifier 3회 연속 FAIL" in data["reason"]
    assert same_count >= 3


# ─── MT-2: 5회 시뮬레이션 (반복 검증) ───────────────────────────

@pytest.mark.parametrize("run_index", range(1, 6))
def test_5_simulations_escalate_at_3rd(tmp_path, monkeypatch, run_index):
    """5회 시뮬레이션: 3회째에 escalate, 4~5회는 이미 escalated."""
    counter_dir = tmp_path / "logs" / "retry-counters"
    counter_dir.mkdir(parents=True)
    events_dir = tmp_path / "events"
    events_dir.mkdir()
    monkeypatch.setenv("WORKSPACE_ROOT", str(tmp_path))

    task_id = f"task-5sim-{run_index:02d}"
    failed_names = ["git_evidence", "file_check"]

    # run_index 횟수만큼 FAIL 기록
    for _ in range(run_index):
        _write_fake_history(counter_dir, task_id, failed_names)

    # history 로드 (마지막 기록 제외)
    history_file = counter_dir / f"{task_id}.fail_history.jsonl"
    lines = history_file.read_text().strip().splitlines()
    history_entries = [json.loads(l) for l in lines[:-1]]  # 마지막 제외 = 이전 기록
    history_keys = [json.dumps(sorted(e.get("failed_names", []))) for e in history_entries]
    current_key = json.dumps(sorted(failed_names))
    same_count = history_keys.count(current_key) + 1

    should_escalate = same_count >= 3
    escalate_path = events_dir / f"{task_id}.escalate"

    if should_escalate:
        escalate_data = {"task_id": task_id, "same_fail_count": same_count}
        escalate_path.write_text(json.dumps(escalate_data))

    if run_index < 3:
        assert not escalate_path.exists(), f"run_index={run_index}: 3회 미만에선 escalate 없어야 함"
    else:
        assert escalate_path.exists(), f"run_index={run_index}: 3회 이상에선 escalate 있어야 함"


# ─── MT-1 워크트리 인식: PROJECT_PATH → git 명령 실행 경로 ───────

def test_git_evidence_uses_env_project_path(tmp_path, monkeypatch):
    """PROJECT_PATH 설정 시 git_evidence.verify가 그 경로에서 git 실행."""
    fake_project = tmp_path / "git_project"
    fake_project.mkdir()
    (fake_project / ".git").mkdir()

    monkeypatch.setenv("PROJECT_PATH", str(fake_project))

    # _resolve_project_dir가 PROJECT_PATH 반환하는지 확인
    resolved = git_evidence._resolve_project_dir("task-git-test", str(tmp_path))
    assert resolved == str(fake_project)


# ─── 헬퍼 함수 ────────────────────────────────────────────────────

def _write_fake_history(counter_dir, task_id, failed_names):
    """테스트용 fail_history.jsonl 기록."""
    from datetime import datetime
    history_file = counter_dir / f"{task_id}.fail_history.jsonl"
    entry = {
        "timestamp": datetime.now().strftime("%Y-%m-%dT%H:%M:%S"),
        "failed_names": sorted(failed_names),
    }
    with open(history_file, "a") as f:
        f.write(json.dumps(entry) + "\n")


def _build_fake_history(counter_dir, task_id):
    """테스트용 history 로드."""
    history_file = counter_dir / f"{task_id}.fail_history.jsonl"
    try:
        with open(history_file) as f:
            entries = [json.loads(line) for line in f if line.strip()]
        return [json.dumps(sorted(e.get("failed_names", []))) for e in entries]
    except (FileNotFoundError, json.JSONDecodeError):
        return []
