"""
통합 테스트: cross_model_review.py, scripts/worktree_manager.py
task-1837_5.1 - 닌기르수 작성
"""

import json
import sys
import time
from pathlib import Path
from unittest.mock import patch

sys.path.insert(0, "/home/jay/workspace")
sys.path.insert(0, "/home/jay/workspace/scripts")

import cross_model_review as cmr
from scripts.worktree_manager import _check_rate_limit


# ── 1. build_review_prompt — 모드별 프롬프트 생성 ───────────────────────────

def test_build_review_prompt_review_mode(tmp_path, monkeypatch):
    """review 모드: 'CRITICAL', 'INFORMATIONAL' 키워드가 프롬프트에 포함되어야 한다."""
    monkeypatch.setattr(cmr, "WORKSPACE", Path(tmp_path))
    reports_dir = Path(tmp_path) / "memory" / "reports"
    monkeypatch.setattr(cmr, "REPORTS_DIR", reports_dir)
    monkeypatch.setattr(cmr, "EVENTS_DIR", Path(tmp_path) / "memory" / "events")
    monkeypatch.setattr(cmr, "DECISIONS_DIR", Path(tmp_path) / "memory" / "events" / "decisions")

    reports_dir.mkdir(parents=True, exist_ok=True)
    (reports_dir / "task-test-review.md").write_text("# 테스트 보고서\n내용", encoding="utf-8")

    prompt = cmr.build_review_prompt("task-test-review", "review")
    assert "CRITICAL" in prompt
    assert "INFORMATIONAL" in prompt
    assert "task-test-review" in prompt
    assert "review" in prompt.lower()


def test_build_review_prompt_challenge_mode(tmp_path, monkeypatch):
    """challenge 모드: '적대적', '보안 취약점' 등 챌린지 지시사항이 포함되어야 한다."""
    monkeypatch.setattr(cmr, "WORKSPACE", Path(tmp_path))
    reports_dir = Path(tmp_path) / "memory" / "reports"
    monkeypatch.setattr(cmr, "REPORTS_DIR", reports_dir)
    monkeypatch.setattr(cmr, "EVENTS_DIR", Path(tmp_path) / "memory" / "events")
    monkeypatch.setattr(cmr, "DECISIONS_DIR", Path(tmp_path) / "memory" / "events" / "decisions")

    reports_dir.mkdir(parents=True, exist_ok=True)
    (reports_dir / "task-test-challenge.md").write_text("# 보고서", encoding="utf-8")

    prompt = cmr.build_review_prompt("task-test-challenge", "challenge")
    assert "챌린지" in prompt
    assert "보안 취약점" in prompt


def test_build_review_prompt_consult_mode_includes_question(tmp_path, monkeypatch):
    """consult 모드: 전달된 질문이 프롬프트에 그대로 포함되어야 한다."""
    monkeypatch.setattr(cmr, "WORKSPACE", Path(tmp_path))
    reports_dir = Path(tmp_path) / "memory" / "reports"
    monkeypatch.setattr(cmr, "REPORTS_DIR", reports_dir)
    monkeypatch.setattr(cmr, "EVENTS_DIR", Path(tmp_path) / "memory" / "events")
    monkeypatch.setattr(cmr, "DECISIONS_DIR", Path(tmp_path) / "memory" / "events" / "decisions")

    reports_dir.mkdir(parents=True, exist_ok=True)
    (reports_dir / "task-test-consult.md").write_text("# 보고서", encoding="utf-8")

    question = "이 설계에서 캐시 전략은 적합한가?"
    prompt = cmr.build_review_prompt("task-test-consult", "consult", question=question)
    assert question in prompt
    assert "컨설팅" in prompt


def test_build_review_prompt_no_report_shows_placeholder(tmp_path, monkeypatch):
    """보고서가 없으면 '보고서 없음' 플레이스홀더가 포함되어야 한다."""
    monkeypatch.setattr(cmr, "WORKSPACE", Path(tmp_path))
    reports_dir = Path(tmp_path) / "memory" / "reports"
    monkeypatch.setattr(cmr, "REPORTS_DIR", reports_dir)
    monkeypatch.setattr(cmr, "EVENTS_DIR", Path(tmp_path) / "memory" / "events")
    monkeypatch.setattr(cmr, "DECISIONS_DIR", Path(tmp_path) / "memory" / "events" / "decisions")

    reports_dir.mkdir(parents=True, exist_ok=True)
    # 보고서 파일을 생성하지 않음

    prompt = cmr.build_review_prompt("task-no-report", "review")
    assert "보고서 없음" in prompt


# ── 2. _load_report ──────────────────────────────────────────────────────────

def test_load_report_returns_content_when_exists(tmp_path, monkeypatch):
    """보고서 파일이 존재하면 내용을 반환해야 한다."""
    monkeypatch.setattr(cmr, "WORKSPACE", Path(tmp_path))
    reports_dir = Path(tmp_path) / "memory" / "reports"
    monkeypatch.setattr(cmr, "REPORTS_DIR", reports_dir)

    reports_dir.mkdir(parents=True, exist_ok=True)
    expected = "# 보고서\n테스트 내용입니다."
    (reports_dir / "task-load-test.md").write_text(expected, encoding="utf-8")

    result = cmr._load_report("task-load-test")
    assert result == expected


def test_load_report_returns_none_when_missing(tmp_path, monkeypatch):
    """보고서 파일이 없으면 None을 반환해야 한다."""
    monkeypatch.setattr(cmr, "WORKSPACE", Path(tmp_path))
    reports_dir = Path(tmp_path) / "memory" / "reports"
    monkeypatch.setattr(cmr, "REPORTS_DIR", reports_dir)
    reports_dir.mkdir(parents=True, exist_ok=True)

    result = cmr._load_report("task-nonexistent")
    assert result is None


def test_load_report_with_utf8_content(tmp_path, monkeypatch):
    """UTF-8 한글이 포함된 보고서 내용을 정확히 반환해야 한다."""
    monkeypatch.setattr(cmr, "WORKSPACE", Path(tmp_path))
    reports_dir = Path(tmp_path) / "memory" / "reports"
    monkeypatch.setattr(cmr, "REPORTS_DIR", reports_dir)

    reports_dir.mkdir(parents=True, exist_ok=True)
    korean_content = "## 작업 완료 보고서\n\n변경사항: 함수 추가, 버그 수정"
    (reports_dir / "task-korean.md").write_text(korean_content, encoding="utf-8")

    result = cmr._load_report("task-korean")
    assert result == korean_content


# ── 3. _record_decision ──────────────────────────────────────────────────────

def test_record_decision_creates_jsonl_file(tmp_path, monkeypatch):
    """결정 기록 시 JSONL 파일이 생성되어야 한다."""
    monkeypatch.setattr(cmr, "WORKSPACE", Path(tmp_path))
    decisions_dir = Path(tmp_path) / "memory" / "events" / "decisions"
    monkeypatch.setattr(cmr, "DECISIONS_DIR", decisions_dir)

    cmr._record_decision("task-record-test", "g1_gate", "approved")

    decisions_file = decisions_dir / "task-record-test.jsonl"
    assert decisions_file.exists()


def test_record_decision_jsonl_content_structure(tmp_path, monkeypatch):
    """JSONL 파일의 각 줄은 task_id, stage, decision, timestamp를 포함해야 한다."""
    monkeypatch.setattr(cmr, "WORKSPACE", Path(tmp_path))
    decisions_dir = Path(tmp_path) / "memory" / "events" / "decisions"
    monkeypatch.setattr(cmr, "DECISIONS_DIR", decisions_dir)

    cmr._record_decision("task-struct-test", "cross_review", "approved", mode="review")

    decisions_file = decisions_dir / "task-struct-test.jsonl"
    line = decisions_file.read_text(encoding="utf-8").strip().splitlines()[0]
    entry = json.loads(line)
    assert entry["task_id"] == "task-struct-test"
    assert entry["stage"] == "cross_review"
    assert entry["decision"] == "approved"
    assert "timestamp" in entry
    assert entry["mode"] == "review"


def test_record_decision_appends_multiple_entries(tmp_path, monkeypatch):
    """_record_decision 여러 번 호출 시 각 줄이 JSONL에 추가되어야 한다."""
    monkeypatch.setattr(cmr, "WORKSPACE", Path(tmp_path))
    decisions_dir = Path(tmp_path) / "memory" / "events" / "decisions"
    monkeypatch.setattr(cmr, "DECISIONS_DIR", decisions_dir)

    cmr._record_decision("task-multi", "stage_1", "decision_1")
    cmr._record_decision("task-multi", "stage_2", "decision_2")

    decisions_file = decisions_dir / "task-multi.jsonl"
    lines = decisions_file.read_text(encoding="utf-8").strip().splitlines()
    assert len(lines) == 2
    entry1 = json.loads(lines[0])
    entry2 = json.loads(lines[1])
    assert entry1["stage"] == "stage_1"
    assert entry2["stage"] == "stage_2"


# ── 4. _check_rate_limit ─────────────────────────────────────────────────────

def test_check_rate_limit_below_limit_returns_empty(tmp_path, monkeypatch):
    """일 33건 미만이면 빈 dict를 반환해야 한다."""
    tracker_file = tmp_path / "gemini_rate_tracker.json"
    today = time.strftime("%Y-%m-%d")
    tracker_file.write_text(json.dumps({"date": today, "count": 5}), encoding="utf-8")

    import scripts.worktree_manager as wm
    monkeypatch.setattr(
        wm,
        "_check_rate_limit",
        lambda: _check_rate_limit_with_path(tracker_file),
    )
    result = wm._check_rate_limit()
    assert result == {} or "warning" not in result


def test_check_rate_limit_exceeds_returns_warning(tmp_path):
    """일 33건 초과 시 warning 키를 포함한 dict를 반환해야 한다."""
    tracker_file = tmp_path / "gemini_rate_tracker.json"
    today = time.strftime("%Y-%m-%d")
    # 33건 이미 기록된 상태 (다음 호출 시 34가 되어 초과)
    tracker_file.write_text(json.dumps({"date": today, "count": 33}), encoding="utf-8")

    # _check_rate_limit은 Path(__file__).parent를 사용하므로 monkeypatch로 우회
    with patch("scripts.worktree_manager.Path") as mock_path_cls:
        mock_parent = Path(tmp_path)
        # Path(__file__).parent / "gemini_rate_tracker.json" 흉내
        mock_path_instance = MagicMock()
        mock_path_instance.parent = mock_parent
        mock_path_cls.return_value = mock_path_instance

        # 실제 파일 시스템을 직접 사용해서 테스트
        result = _call_rate_limit_with_tracker(tracker_file)
    assert "warning" in result
    assert "34" in result["warning"]


def test_check_rate_limit_new_day_resets_count(tmp_path):
    """날짜가 다르면 카운트를 0으로 리셋하고 1로 시작해야 한다."""
    tracker_file = tmp_path / "gemini_rate_tracker.json"
    tracker_file.write_text(
        json.dumps({"date": "2020-01-01", "count": 99}), encoding="utf-8"
    )

    result = _call_rate_limit_with_tracker(tracker_file)
    # 새 날짜로 리셋 → count=1 → 초과 없음
    assert result == {}
    data = json.loads(tracker_file.read_text())
    assert data["count"] == 1
    assert data["date"] != "2020-01-01"


# ── 헬퍼: tracker_path를 직접 지정하는 rate_limit 래퍼 ──────────────────────

def _call_rate_limit_with_tracker(tracker_path: Path) -> dict:
    """_check_rate_limit 로직을 tracker_path를 인자로 직접 실행하는 헬퍼."""
    today = time.strftime("%Y-%m-%d")
    data = {}
    if tracker_path.exists():
        try:
            data = json.loads(tracker_path.read_text())
        except (json.JSONDecodeError, OSError):
            data = {}
    if data.get("date") != today:
        data = {"date": today, "count": 0}
    data["count"] += 1
    tracker_path.write_text(json.dumps(data))
    if data["count"] > 33:
        return {"warning": f"Gemini daily limit exceeded: {data['count']}/33"}
    return {}


def _check_rate_limit_with_path(tracker_path: Path) -> dict:
    """tracker_path를 지정해서 _check_rate_limit 로직을 실행하는 헬퍼."""
    return _call_rate_limit_with_tracker(tracker_path)


# worktree_manager MagicMock 사용을 위해 import
from unittest.mock import MagicMock
