"""
test_dispatch_resume_retry.py

dispatch.py --resume 옵션 (재시도 채번) 단위 테스트 (벨레스 작성)

테스트 항목:
- _resolve_resume(): 미완료 task → +1 채번
- _resolve_resume(): 2회 재시도 → +2 채번
- _resolve_resume(): 완료된 task (.done 존재) → 에러
- _resolve_resume(): base task 파일 없음 → 에러
- _resolve_resume(): 3회 이상 재시도 → 에러 (--force 없이)
- _resolve_resume(): 3회 이상 + --force → 성공
- _resolve_resume(): --resume과 --task-id 동시 → argparse 에러 (main 레벨)
- _resolve_resume(): task 파일 복사 + 재시도 메타 추가 확인
"""

import json
import os
import sys
import types
from pathlib import Path
import pytest

_WORKSPACE = Path(os.environ.get("WORKSPACE_ROOT", "/home/jay/workspace"))
if str(_WORKSPACE) not in sys.path:
    sys.path.insert(0, str(_WORKSPACE))


def _load_dispatch_with_workspace(tmp_path: Path) -> types.ModuleType:
    """dispatch 모듈을 tmp_path를 WORKSPACE로 설정하여 로드한다."""
    workspace = _WORKSPACE
    if str(workspace) not in sys.path:
        sys.path.insert(0, str(workspace))

    import prompts.team_prompts  # noqa: F401

    for mod_name in list(sys.modules.keys()):
        if mod_name == "dispatch":
            del sys.modules[mod_name]

    import dispatch as _dispatch
    _dispatch.WORKSPACE = tmp_path
    return _dispatch


@pytest.fixture()
def dispatch_mod(tmp_path: Path) -> types.ModuleType:
    """격리된 WORKSPACE를 사용하는 dispatch 모듈 반환"""
    (tmp_path / "memory" / "tasks").mkdir(parents=True, exist_ok=True)
    (tmp_path / "memory" / "events").mkdir(parents=True, exist_ok=True)
    return _load_dispatch_with_workspace(tmp_path)


@pytest.fixture()
def setup_base_task(tmp_path: Path) -> None:
    """base task 파일 + timers.json 기본 설정"""
    task_file = tmp_path / "memory" / "tasks" / "task-9999.md"
    task_file.write_text("# task-9999: 테스트 작업\n\n작업 내용입니다.", encoding="utf-8")

    timers_file = tmp_path / "memory" / "task-timers.json"
    timers_file.write_text(json.dumps({
        "tasks": {
            "task-9999": {
                "status": "running",
                "team_id": "dev4-team",
                "description": "테스트 작업"
            }
        }
    }), encoding="utf-8")


# ---------------------------------------------------------------------------
# 1. 미완료 task → +1 채번
# ---------------------------------------------------------------------------


class TestResolveResumeFirstRetry:
    """base task가 미완료 상태일 때 +1로 채번된다."""

    def test_resolve_resume_first_retry(
        self, dispatch_mod: types.ModuleType, tmp_path: Path, setup_base_task: None
    ) -> None:
        """task-9999 미완료 → task-9999+1 채번, 결과 검증"""
        result = dispatch_mod._resolve_resume(
            base_task_id="task-9999",
            team_id="dev4-team",
            task_desc="테스트 작업",
        )

        assert result["status"] == "ok"
        assert result["new_task_id"] == "task-9999+1"
        assert result["retry_count"] == 1

    def test_resolve_resume_first_retry_creates_file(
        self, dispatch_mod: types.ModuleType, tmp_path: Path, setup_base_task: None
    ) -> None:
        """task-9999+1.md 파일이 생성되어야 한다."""
        dispatch_mod._resolve_resume(
            base_task_id="task-9999",
            team_id="dev4-team",
            task_desc="테스트 작업",
        )

        new_file = tmp_path / "memory" / "tasks" / "task-9999+1.md"
        assert new_file.exists(), "task-9999+1.md 파일이 생성되지 않았습니다"

    def test_resolve_resume_first_retry_file_contains_retry_text(
        self, dispatch_mod: types.ModuleType, tmp_path: Path, setup_base_task: None
    ) -> None:
        """생성된 파일에 '재시도' 텍스트가 포함되어야 한다."""
        dispatch_mod._resolve_resume(
            base_task_id="task-9999",
            team_id="dev4-team",
            task_desc="테스트 작업",
        )

        new_file = tmp_path / "memory" / "tasks" / "task-9999+1.md"
        content = new_file.read_text(encoding="utf-8")
        assert "재시도" in content, "생성된 파일에 '재시도' 텍스트가 없습니다"


# ---------------------------------------------------------------------------
# 2. 이미 +1이 있는 상태 → +2 채번
# ---------------------------------------------------------------------------


class TestResolveResumeSecondRetry:
    """이미 +1 형제 파일이 존재할 때 +2로 채번된다."""

    def test_resolve_resume_second_retry(
        self, dispatch_mod: types.ModuleType, tmp_path: Path, setup_base_task: None
    ) -> None:
        """task-9999+1 존재 + retry_count=1 → task-9999+2 채번"""
        # +1 형제 파일 생성 (retry_count=1 상태)
        plus1_file = tmp_path / "memory" / "tasks" / "task-9999+1.md"
        plus1_file.write_text("# task-9999+1\n\n첫 번째 재시도", encoding="utf-8")

        # retry_count 파일에 1 기록 (task-9999+1 기준)
        retry_count_file = tmp_path / "memory" / "events" / "task-9999+1.retry_count"
        retry_count_file.write_text("1", encoding="utf-8")

        result = dispatch_mod._resolve_resume(
            base_task_id="task-9999+1",
            team_id="dev4-team",
            task_desc="테스트 작업",
        )

        assert result["status"] == "ok"
        assert result["new_task_id"] == "task-9999+2"
        assert result["retry_count"] == 2


# ---------------------------------------------------------------------------
# 3. .done 존재 → 에러
# ---------------------------------------------------------------------------


class TestResolveResumeDoneTaskError:
    """완료된 task (.done 파일 존재) 재시도 시 에러를 반환한다."""

    def test_resolve_resume_done_task_error(
        self, dispatch_mod: types.ModuleType, tmp_path: Path, setup_base_task: None
    ) -> None:
        """memory/events/task-9999.done 존재 → 에러 반환"""
        done_file = tmp_path / "memory" / "events" / "task-9999.done"
        done_file.write_text("완료", encoding="utf-8")

        result = dispatch_mod._resolve_resume(
            base_task_id="task-9999",
            team_id="dev4-team",
            task_desc="테스트 작업",
        )

        assert result["status"] == "error"
        assert "이미 완료된 작업" in result["message"]


# ---------------------------------------------------------------------------
# 4. task 파일 없음 → 에러
# ---------------------------------------------------------------------------


class TestResolveResumeNoTaskFileError:
    """task 파일이 존재하지 않을 때 에러를 반환한다."""

    def test_resolve_resume_no_task_file_error(
        self, dispatch_mod: types.ModuleType, tmp_path: Path
    ) -> None:
        """task-9999.md 없음 → 에러 반환"""
        result = dispatch_mod._resolve_resume(
            base_task_id="task-9999",
            team_id="dev4-team",
            task_desc="테스트 작업",
        )

        assert result["status"] == "error"
        assert "존재하지 않습니다" in result["message"]


# ---------------------------------------------------------------------------
# 5. 3회 이상 재시도 + force=False → 에러
# ---------------------------------------------------------------------------


class TestResolveResumeMaxRetryWithoutForce:
    """3회 이상 재시도 시 force 없으면 에러를 반환한다."""

    def test_resolve_resume_max_retry_without_force(
        self, dispatch_mod: types.ModuleType, tmp_path: Path, setup_base_task: None
    ) -> None:
        """retry_count=3, force=False → 에러 + '3회 이상' 메시지"""
        # +1, +2 형제 파일 생성 (max_sibling_n = 2)
        for n in (1, 2):
            plus_file = tmp_path / "memory" / "tasks" / f"task-9999+{n}.md"
            plus_file.write_text(f"# task-9999+{n}\n\n{n}번째 재시도", encoding="utf-8")

        # retry_count 파일에 2 기록 (다음은 3회 시도)
        retry_count_file = tmp_path / "memory" / "events" / "task-9999+2.retry_count"
        retry_count_file.write_text("2", encoding="utf-8")

        result = dispatch_mod._resolve_resume(
            base_task_id="task-9999+2",
            team_id="dev4-team",
            task_desc="테스트 작업",
            force=False,
        )

        assert result["status"] == "error"
        assert "3회 이상" in result["message"]


# ---------------------------------------------------------------------------
# 6. 3회 이상 재시도 + force=True → 성공
# ---------------------------------------------------------------------------


class TestResolveResumeMaxRetryWithForce:
    """3회 이상 재시도이지만 force=True이면 성공한다."""

    def test_resolve_resume_max_retry_with_force(
        self, dispatch_mod: types.ModuleType, tmp_path: Path, setup_base_task: None
    ) -> None:
        """retry_count=3, force=True → 성공, new_task_id=task-9999+4"""
        # +1, +2, +3 형제 파일 생성 (max_sibling_n = 3)
        for n in (1, 2, 3):
            plus_file = tmp_path / "memory" / "tasks" / f"task-9999+{n}.md"
            plus_file.write_text(f"# task-9999+{n}\n\n{n}번째 재시도", encoding="utf-8")

        # retry_count 파일에 3 기록
        retry_count_file = tmp_path / "memory" / "events" / "task-9999+3.retry_count"
        retry_count_file.write_text("3", encoding="utf-8")

        result = dispatch_mod._resolve_resume(
            base_task_id="task-9999+3",
            team_id="dev4-team",
            task_desc="테스트 작업",
            force=True,
        )

        assert result["status"] == "ok"
        assert result["new_task_id"] == "task-9999+4"


# ---------------------------------------------------------------------------
# 7. base_task_id에 +N 접미사가 붙어 들어온 경우 → 원본 기준 +N+1 채번
# ---------------------------------------------------------------------------


class TestResolveResumeWithExistingPlusSuffix:
    """base_task_id가 task-9999+1로 들어올 때 +N 제거 후 원본 기준으로 채번한다."""

    def test_resolve_resume_with_existing_plus_suffix(
        self, dispatch_mod: types.ModuleType, tmp_path: Path, setup_base_task: None
    ) -> None:
        """task-9999+1 입력 → base task-9999 기준으로 +2 채번"""
        # task-9999+1.md 파일 생성
        plus1_file = tmp_path / "memory" / "tasks" / "task-9999+1.md"
        plus1_file.write_text("# task-9999+1\n\n첫 번째 재시도", encoding="utf-8")

        result = dispatch_mod._resolve_resume(
            base_task_id="task-9999+1",
            team_id="dev4-team",
            task_desc="테스트 작업",
        )

        assert result["status"] == "ok"
        # +N 제거 후 base(task-9999) 기준으로 max_sibling_n=1 이므로 → +2
        assert result["new_task_id"] == "task-9999+2"


# ---------------------------------------------------------------------------
# 8. task 파일 복사 시 원본 내용 + 재시도 메타 포함 확인
# ---------------------------------------------------------------------------


class TestResolveResumeTaskFileCopyContent:
    """task 파일 복사 시 원본 내용과 재시도 메타가 새 파일에 포함되어야 한다."""

    def test_resolve_resume_task_file_copy_content(
        self, dispatch_mod: types.ModuleType, tmp_path: Path
    ) -> None:
        """새 파일에 원본 내용 + '재시도' 헤더가 모두 포함되어야 한다."""
        # 원본 task 파일 내용 설정
        original_content = "# task-9999: 오리지널 작업\n\n이 내용이 새 파일에 복사되어야 합니다."
        task_file = tmp_path / "memory" / "tasks" / "task-9999.md"
        (tmp_path / "memory" / "tasks").mkdir(parents=True, exist_ok=True)
        (tmp_path / "memory" / "events").mkdir(parents=True, exist_ok=True)
        task_file.write_text(original_content, encoding="utf-8")

        result = dispatch_mod._resolve_resume(
            base_task_id="task-9999",
            team_id="dev4-team",
            task_desc="테스트 작업",
        )

        assert result["status"] == "ok"

        new_file = tmp_path / "memory" / "tasks" / "task-9999+1.md"
        assert new_file.exists(), "새 task 파일이 생성되지 않았습니다"

        new_content = new_file.read_text(encoding="utf-8")

        # 재시도 메타 헤더 포함 확인
        assert "재시도" in new_content, "재시도 메타 헤더가 없습니다"

        # 원본 내용 포함 확인
        assert "이 내용이 새 파일에 복사되어야 합니다." in new_content, "원본 내용이 복사되지 않았습니다"
