"""test_todo_sync.py

task-637.1 TDD RED Phase — utils/todo_sync.py 단위 테스트 (아르고스 작성)

테스트 항목:
1. test_sync_task_completion_match: sub_item.task_id 매칭 → done=True
2. test_sync_task_completion_no_match: 매칭 없음 → 변경 없음
3. test_sync_task_completion_already_done: 이미 done=True → 변경 없음
4. test_sync_issue_all_done: 모든 sub_items done → issue.status="done" + completed_at 기록
5. test_retroactive_sync: 다건 completed 태스크 일괄 동기화
6. test_link_task_to_issue: 수동 task_id ↔ issue 연결
7. test_link_task_to_issue_with_sub_item_title: sub_item_title 지정 연결
8. test_backup_created: todo.json 수정 전 .bak 파일 생성
9. test_no_reverse_done: done=True → done=False 되돌리기 불가 (단방향)
10. test_linked_tasks_match: issue.linked_tasks에 task_id 포함 시 매칭

격리: tmp_path + monkeypatch 로 TODO_FILE / TIMERS_FILE 경로 오버라이드
"""

import json
import os
import sys
from pathlib import Path

import pytest

# ---------------------------------------------------------------------------
# sys.path 설정
# ---------------------------------------------------------------------------

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

# ---------------------------------------------------------------------------
# todo_sync 임포트 (아직 미구현 — ImportError = RED phase 허용)
# ---------------------------------------------------------------------------

import utils.todo_sync as todo_sync  # pyright: ignore[reportMissingImports]

# ---------------------------------------------------------------------------
# 공통 헬퍼: 임시 파일 생성
# ---------------------------------------------------------------------------

_TODO_TEMPLATE: dict = {
    "version": "1.0",
    "issues": [
        {
            "id": "issue-007",
            "status": "pending",
            "completed_at": None,
            "linked_tasks": ["task-434.1"],
            "sub_items": [
                {"title": "Phase 0: 환경 셋업", "done": False, "task_id": None},
                {"title": "Phase 1 POC", "done": False, "task_id": "task-500.1"},
            ],
        }
    ],
}

_TIMERS_TEMPLATE: dict = {
    "tasks": {
        "task-500.1": {
            "task_id": "task-500.1",
            "status": "completed",
        }
    }
}


def _write_todo(tmp_path: Path, data: dict | None = None) -> Path:
    """tmp_path/memory/todo.json 을 생성하고 경로를 반환."""
    mem = tmp_path / "memory"
    mem.mkdir(parents=True, exist_ok=True)
    todo_file = mem / "todo.json"
    todo_file.write_text(json.dumps(data or _TODO_TEMPLATE, ensure_ascii=False), encoding="utf-8")
    return todo_file


def _write_timers(tmp_path: Path, data: dict | None = None) -> Path:
    """tmp_path/memory/task-timers.json 을 생성하고 경로를 반환."""
    mem = tmp_path / "memory"
    mem.mkdir(parents=True, exist_ok=True)
    timers_file = mem / "task-timers.json"
    timers_file.write_text(json.dumps(data or _TIMERS_TEMPLATE, ensure_ascii=False), encoding="utf-8")
    return timers_file


def _patch_paths(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> tuple[Path, Path]:
    """todo_sync 모듈의 TODO_FILE / TIMERS_FILE 을 tmp_path 기반으로 패치."""
    todo_file = _write_todo(tmp_path)
    timers_file = _write_timers(tmp_path)
    monkeypatch.setattr(todo_sync, "TODO_FILE", todo_file)
    monkeypatch.setattr(todo_sync, "TIMERS_FILE", timers_file)
    return todo_file, timers_file


# ---------------------------------------------------------------------------
# 1. test_sync_task_completion_match
# ---------------------------------------------------------------------------


def test_sync_task_completion_match(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """sub_item에 task_id가 있고 해당 task_id로 sync → done=True."""
    todo_file, _ = _patch_paths(monkeypatch, tmp_path)

    result = todo_sync.sync_task_completion("task-500.1")

    assert isinstance(result, dict), "반환값은 dict이어야 함"
    assert result["updated"] == 1, "한 개의 sub_item이 업데이트되어야 함"
    assert len(result["details"]) >= 1, "details 리스트에 항목이 있어야 함"

    saved = json.loads(todo_file.read_text(encoding="utf-8"))
    phase1 = saved["issues"][0]["sub_items"][1]
    assert phase1["task_id"] == "task-500.1"
    assert phase1["done"] is True, "Phase 1 POC sub_item이 done=True여야 함"


# ---------------------------------------------------------------------------
# 2. test_sync_task_completion_no_match
# ---------------------------------------------------------------------------


def test_sync_task_completion_no_match(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """매칭되는 sub_item이 없는 task_id → 변경 없음."""
    todo_file, _ = _patch_paths(monkeypatch, tmp_path)
    original = todo_file.read_text(encoding="utf-8")

    result = todo_sync.sync_task_completion("task-999.9")

    assert result["updated"] == 0, "매칭 없으면 updated=0 이어야 함"

    saved = todo_file.read_text(encoding="utf-8")
    assert json.loads(saved) == json.loads(original), "파일 내용이 변경되면 안 됨"


# ---------------------------------------------------------------------------
# 3. test_sync_task_completion_already_done
# ---------------------------------------------------------------------------


def test_sync_task_completion_already_done(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """이미 done=True인 sub_item → updated=0, 파일 불변."""
    already_done_todo: dict = {
        "version": "1.0",
        "issues": [
            {
                "id": "issue-010",
                "status": "pending",
                "completed_at": None,
                "linked_tasks": [],
                "sub_items": [
                    {"title": "이미 완료", "done": True, "task_id": "task-300.1"},
                ],
            }
        ],
    }
    todo_file = _write_todo(tmp_path, already_done_todo)
    _write_timers(tmp_path)
    monkeypatch.setattr(todo_sync, "TODO_FILE", todo_file)
    monkeypatch.setattr(todo_sync, "TIMERS_FILE", tmp_path / "memory" / "task-timers.json")

    result = todo_sync.sync_task_completion("task-300.1")

    assert result["updated"] == 0, "이미 done=True면 updated=0 이어야 함"
    saved = json.loads(todo_file.read_text(encoding="utf-8"))
    assert saved["issues"][0]["sub_items"][0]["done"] is True


# ---------------------------------------------------------------------------
# 4. test_sync_issue_all_done
# ---------------------------------------------------------------------------


def test_sync_issue_all_done(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """모든 sub_items가 done이면 issue.status='done' + completed_at 기록."""
    one_left_todo: dict = {
        "version": "1.0",
        "issues": [
            {
                "id": "issue-020",
                "status": "in_progress",
                "completed_at": None,
                "linked_tasks": [],
                "sub_items": [
                    {"title": "Step A", "done": True, "task_id": "task-100.1"},
                    {"title": "Step B", "done": False, "task_id": "task-101.1"},
                ],
            }
        ],
    }
    todo_file = _write_todo(tmp_path, one_left_todo)
    _write_timers(tmp_path)
    monkeypatch.setattr(todo_sync, "TODO_FILE", todo_file)
    monkeypatch.setattr(todo_sync, "TIMERS_FILE", tmp_path / "memory" / "task-timers.json")

    result = todo_sync.sync_task_completion("task-101.1")

    assert result["updated"] == 1

    saved = json.loads(todo_file.read_text(encoding="utf-8"))
    issue = saved["issues"][0]
    assert issue["status"] == "done", "모든 sub_items done → issue.status='done'"
    assert issue["completed_at"] is not None, "completed_at이 기록되어야 함"


# ---------------------------------------------------------------------------
# 5. test_retroactive_sync
# ---------------------------------------------------------------------------


def test_retroactive_sync(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """다건 completed 태스크를 전수 스캔하여 todo.json 일괄 동기화."""
    multi_todo: dict = {
        "version": "1.0",
        "issues": [
            {
                "id": "issue-030",
                "status": "pending",
                "completed_at": None,
                "linked_tasks": [],
                "sub_items": [
                    {"title": "Phase A", "done": False, "task_id": "task-200.1"},
                    {"title": "Phase B", "done": False, "task_id": "task-201.1"},
                    {"title": "Phase C", "done": False, "task_id": None},
                ],
            }
        ],
    }
    multi_timers: dict = {
        "tasks": {
            "task-200.1": {"task_id": "task-200.1", "status": "completed"},
            "task-201.1": {"task_id": "task-201.1", "status": "completed"},
            "task-202.1": {"task_id": "task-202.1", "status": "in_progress"},
        }
    }
    todo_file = _write_todo(tmp_path, multi_todo)
    timers_file = _write_timers(tmp_path, multi_timers)
    monkeypatch.setattr(todo_sync, "TODO_FILE", todo_file)
    monkeypatch.setattr(todo_sync, "TIMERS_FILE", timers_file)

    result = todo_sync.retroactive_sync()

    assert isinstance(result, dict), "반환값은 dict이어야 함"
    assert result["total_synced"] == 2, "완료된 태스크 2개가 동기화되어야 함"
    assert len(result["details"]) >= 2

    saved = json.loads(todo_file.read_text(encoding="utf-8"))
    sub_items = saved["issues"][0]["sub_items"]
    assert sub_items[0]["done"] is True, "Phase A done=True"
    assert sub_items[1]["done"] is True, "Phase B done=True"
    assert sub_items[2]["done"] is False, "Phase C는 task_id 없어서 그대로"


# ---------------------------------------------------------------------------
# 6. test_link_task_to_issue
# ---------------------------------------------------------------------------


def test_link_task_to_issue(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """수동으로 task_id를 issue에 연결: linked_tasks에 추가."""
    todo_file, _ = _patch_paths(monkeypatch, tmp_path)

    result = todo_sync.link_task_to_issue("task-600.1", "issue-007")

    assert isinstance(result, dict)
    assert result["linked"] is True, "linked=True 이어야 함"
    assert "message" in result

    saved = json.loads(todo_file.read_text(encoding="utf-8"))
    issue = saved["issues"][0]
    assert "task-600.1" in issue["linked_tasks"], "linked_tasks에 추가되어야 함"


# ---------------------------------------------------------------------------
# 7. test_link_task_to_issue_with_sub_item_title
# ---------------------------------------------------------------------------


def test_link_task_to_issue_with_sub_item_title(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """sub_item_title 지정 시 해당 sub_item.task_id에도 연결."""
    todo_file, _ = _patch_paths(monkeypatch, tmp_path)

    result = todo_sync.link_task_to_issue(
        "task-601.1",
        "issue-007",
        sub_item_title="Phase 0: 환경 셋업",
    )

    assert result["linked"] is True

    saved = json.loads(todo_file.read_text(encoding="utf-8"))
    issue = saved["issues"][0]
    # linked_tasks에 추가
    assert "task-601.1" in issue["linked_tasks"]
    # sub_item.task_id에도 설정
    phase0 = next(s for s in issue["sub_items"] if s["title"] == "Phase 0: 환경 셋업")
    assert phase0["task_id"] == "task-601.1", "sub_item.task_id가 설정되어야 함"


# ---------------------------------------------------------------------------
# 8. test_backup_created
# ---------------------------------------------------------------------------


def test_backup_created(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """todo.json 수정 전 .bak 파일 생성 확인."""
    todo_file, _ = _patch_paths(monkeypatch, tmp_path)

    todo_sync.sync_task_completion("task-500.1")

    bak_file = todo_file.parent / (todo_file.name + ".bak")
    assert bak_file.exists(), "todo.json.bak 파일이 생성되어야 함"
    # .bak 내용이 원래 내용과 동일해야 함 (수정 전 백업)
    original_issues = json.loads(bak_file.read_text(encoding="utf-8"))["issues"]
    assert original_issues[0]["sub_items"][1]["done"] is False, "백업은 수정 전 상태여야 함"


# ---------------------------------------------------------------------------
# 9. test_no_reverse_done
# ---------------------------------------------------------------------------


def test_no_reverse_done(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """done=True → done=False 되돌리기 불가 (단방향 보장)."""
    all_done_todo: dict = {
        "version": "1.0",
        "issues": [
            {
                "id": "issue-040",
                "status": "done",
                "completed_at": "2026-03-10T10:00:00",
                "linked_tasks": ["task-400.1"],
                "sub_items": [
                    {"title": "완료된 항목", "done": True, "task_id": "task-400.1"},
                ],
            }
        ],
    }
    todo_file = _write_todo(tmp_path, all_done_todo)
    _write_timers(tmp_path)
    monkeypatch.setattr(todo_sync, "TODO_FILE", todo_file)
    monkeypatch.setattr(todo_sync, "TIMERS_FILE", tmp_path / "memory" / "task-timers.json")

    # 이미 done=True인 항목을 다시 sync해도 False로 되돌리면 안 됨
    todo_sync.sync_task_completion("task-400.1")

    saved = json.loads(todo_file.read_text(encoding="utf-8"))
    assert saved["issues"][0]["sub_items"][0]["done"] is True, "done=True는 되돌릴 수 없어야 함"
    assert saved["issues"][0]["status"] == "done", "issue.status도 되돌리면 안 됨"


# ---------------------------------------------------------------------------
# 10. test_linked_tasks_match
# ---------------------------------------------------------------------------


def test_linked_tasks_match(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """issue.linked_tasks에 task_id 포함 시 sub_items 없어도 매칭 처리."""
    linked_todo: dict = {
        "version": "1.0",
        "issues": [
            {
                "id": "issue-050",
                "status": "pending",
                "completed_at": None,
                "linked_tasks": ["task-434.1"],
                "sub_items": [
                    # task_id가 None이지만 issue.linked_tasks에 task-434.1 포함
                    {"title": "연결된 태스크 작업", "done": False, "task_id": None},
                ],
            }
        ],
    }
    todo_file = _write_todo(tmp_path, linked_todo)
    _write_timers(tmp_path)
    monkeypatch.setattr(todo_sync, "TODO_FILE", todo_file)
    monkeypatch.setattr(todo_sync, "TIMERS_FILE", tmp_path / "memory" / "task-timers.json")

    result = todo_sync.sync_task_completion("task-434.1")

    # linked_tasks 매칭이므로 updated >= 1 이거나 details에 매칭 정보가 있어야 함
    assert (
        result["updated"] >= 1 or len(result["details"]) >= 1
    ), "linked_tasks 매칭 시 업데이트 또는 details 기록이 있어야 함"

    saved = json.loads(todo_file.read_text(encoding="utf-8"))
    sub = saved["issues"][0]["sub_items"][0]
    assert sub["done"] is True, "linked_tasks 매칭 시 sub_item done=True여야 함"
