"""
test_dispatch_memory_check.py

dispatch._check_memory_before_dispatch() 단위 테스트 (모리건 작성)

테스트 항목:
- MEMORY.md 없을 때 graceful skip (debug 로그)
- ★ 항목 추출 → info 로그 확인
- 디자인 키워드 + dev 팀 → warning 로그
- 디자인 키워드 + non-dev 팀 → warning 없음
- 디자인 키워드 없음 + dev 팀 → warning 없음
- ★ 링크된 .md 파일 읽기 시도
"""

import logging
import os
import sys
import types
from pathlib import Path
from unittest.mock import patch

import pytest

# ---------------------------------------------------------------------------
# dispatch 모듈 로드 헬퍼
# ---------------------------------------------------------------------------


def _get_dispatch_module() -> types.ModuleType:
    """dispatch 모듈을 sys.modules에서 캐시 없이 로드한다."""
    workspace = Path(os.environ.get("WORKSPACE_ROOT", "/home/jay/workspace"))
    if str(workspace) not in sys.path:
        sys.path.insert(0, str(workspace))

    # 의존 패키지 먼저 로드
    # dispatch 재로드
    for mod_name in list(sys.modules.keys()):
        if mod_name == "dispatch":
            del sys.modules[mod_name]

    import dispatch as _dispatch  # type: ignore[import-not-found]

    return _dispatch


# ---------------------------------------------------------------------------
# fixture
# ---------------------------------------------------------------------------


@pytest.fixture()
def memory_dir(tmp_path) -> Path:
    """테스트용 memory 디렉토리 (실제 파일 시스템 격리)"""
    d = tmp_path / "memory"
    d.mkdir(parents=True, exist_ok=True)
    return d


@pytest.fixture()
def dispatch_mod(memory_dir):
    """_MEMORY_BASE_PATH가 memory_dir로 교체된 dispatch 모듈"""
    mod = _get_dispatch_module()
    setattr(mod, "_MEMORY_BASE_PATH", memory_dir)  # type: ignore[reportAttributeAccessIssue]
    return mod


# ---------------------------------------------------------------------------
# 테스트 케이스
# ---------------------------------------------------------------------------


class TestCheckMemoryBeforeDispatch:
    """_check_memory_before_dispatch() 동작 검증"""

    # 1. MEMORY.md 없을 때 → 에러 없이 return, debug 로그
    def test_memory_check_no_memory_file(self, dispatch_mod, caplog):
        """MEMORY.md가 없으면 예외 없이 반환하고 debug 로그를 남긴다."""
        # memory_dir 안에 MEMORY.md 없음 (디렉토리 자체도 없어도 됨)
        with caplog.at_level(logging.DEBUG, logger="dispatch"):
            dispatch_mod._check_memory_before_dispatch("dev1-team", "일반 작업입니다")

        # warning/error 로그 없음
        warning_records = [r for r in caplog.records if r.levelno >= logging.WARNING]
        assert len(warning_records) == 0

        # debug 로그가 최소 1개 있어야 함
        debug_records = [r for r in caplog.records if r.levelno == logging.DEBUG]
        assert len(debug_records) >= 1

    # 2. ★ 항목이 있는 MEMORY.md → info 로그 확인
    def test_memory_check_star_items_extracted(self, dispatch_mod, memory_dir, caplog):
        """★ 항목 2개가 있는 MEMORY.md → star_items 개수 관련 info 로그가 출력된다."""
        memory_md = memory_dir / "MEMORY.md"
        memory_md.write_text(
            "# 메모리\n"
            "- 일반 항목\n"
            "★ [팀 라우팅 가이드](feedback_design_team_routing_v2.md)\n"
            "★ [배포 체크리스트](deploy_checklist.md)\n",
            encoding="utf-8",
        )
        # 링크된 파일이 없어도 graceful하게 처리되어야 함
        with caplog.at_level(logging.DEBUG, logger="dispatch"):
            dispatch_mod._check_memory_before_dispatch("dev1-team", "일반 작업")

        info_records = [r for r in caplog.records if r.levelno == logging.INFO]
        assert len(info_records) >= 1, "★ 항목 추출 후 info 로그가 없음"

    # 3. 디자인 키워드 + dev 팀 → warning 로그
    def test_memory_check_design_keyword_dev_team_warning(self, dispatch_mod, memory_dir, caplog):
        """task_desc에 '배너' + team_id='dev1-team' 조합 → logger.warning 호출."""
        memory_md = memory_dir / "MEMORY.md"
        memory_md.write_text(
            "# 메모리\n★ [라우팅](feedback_design_team_routing_v2.md)\n",
            encoding="utf-8",
        )
        with caplog.at_level(logging.DEBUG, logger="dispatch"):
            dispatch_mod._check_memory_before_dispatch("dev1-team", "배너 제작 작업입니다")

        warning_records = [r for r in caplog.records if r.levelno >= logging.WARNING]
        assert len(warning_records) >= 1, "디자인 키워드 + dev 팀인데 warning 로그가 없음"

    # 4. 디자인 키워드 + non-dev 팀 → warning 없음
    def test_memory_check_design_keyword_non_dev_team_no_warning(self, dispatch_mod, memory_dir, caplog):
        """task_desc에 '배너' + team_id='design' → warning 로그 없음."""
        memory_md = memory_dir / "MEMORY.md"
        memory_md.write_text(
            "# 메모리\n★ [라우팅](feedback_design_team_routing_v2.md)\n",
            encoding="utf-8",
        )
        with caplog.at_level(logging.DEBUG, logger="dispatch"):
            dispatch_mod._check_memory_before_dispatch("design", "배너 제작 작업입니다")

        warning_records = [r for r in caplog.records if r.levelno >= logging.WARNING]
        assert len(warning_records) == 0, "non-dev 팀인데 warning 로그가 발생함"

    # 5. 디자인 키워드 없음 + dev 팀 → warning 없음
    def test_memory_check_no_design_keyword_no_warning(self, dispatch_mod, memory_dir, caplog):
        """디자인 키워드 없는 task_desc + team_id='dev1-team' → warning 로그 없음."""
        memory_md = memory_dir / "MEMORY.md"
        memory_md.write_text(
            "# 메모리\n★ [라우팅](feedback_design_team_routing_v2.md)\n",
            encoding="utf-8",
        )
        with caplog.at_level(logging.DEBUG, logger="dispatch"):
            dispatch_mod._check_memory_before_dispatch("dev1-team", "API 서버 버그 수정")

        warning_records = [r for r in caplog.records if r.levelno >= logging.WARNING]
        assert len(warning_records) == 0, "디자인 키워드가 없는데 warning 로그가 발생함"

    # 6. ★ 항목에 링크된 .md 파일이 존재 → 해당 파일 읽기 시도
    def test_memory_check_linked_files_read(self, dispatch_mod, memory_dir, caplog):
        """★ 항목에 링크된 .md 파일이 실제 존재하면 파일을 읽는다."""
        # linked 파일 생성
        linked_file = memory_dir / "deploy_checklist.md"
        linked_file.write_text("# 배포 체크리스트\n- 단계 1\n- 단계 2\n", encoding="utf-8")

        memory_md = memory_dir / "MEMORY.md"
        memory_md.write_text(
            "# 메모리\n★ [배포 체크리스트](deploy_checklist.md)\n",
            encoding="utf-8",
        )

        read_paths: list[Path] = []
        original_read = Path.read_text

        def _spy_read_text(self, *args, **kwargs):
            read_paths.append(self)
            return original_read(self, *args, **kwargs)

        with patch.object(Path, "read_text", _spy_read_text):
            with caplog.at_level(logging.DEBUG, logger="dispatch"):
                dispatch_mod._check_memory_before_dispatch("dev1-team", "배포 진행")

        read_names = [p.name for p in read_paths]
        assert "deploy_checklist.md" in read_names, "★ 항목에 링크된 deploy_checklist.md가 읽히지 않음"
