"""
test_team_prompts.py

prompts/team_prompts.py 단위 테스트 (아르고스 작성)

테스트 항목:
- build_prompt(): 팀별 올바른 프롬프트 생성 (dev1/dev2/dev3)
- level별 차이 (normal / critical / security)
- project_id 유무에 따른 경로 차이
- 잘못된 team_id → ValueError 발생

주의: build_prompt()는 WORKSPACE_ROOT/memory/tasks/<task_id>.md 에
task_desc를 저장한다. monkeypatch + tmp_path로 Path 생성 경로를 오버라이드.
"""

import os
import sys
from pathlib import Path
from unittest.mock import MagicMock, patch

import pytest

# workspace를 sys.path에 추가하여 prompts 패키지 임포트
_WORKSPACE = Path(os.environ.get("WORKSPACE_ROOT", "/home/jay/workspace"))
if str(_WORKSPACE) not in sys.path:
    sys.path.insert(0, str(_WORKSPACE))

import prompts.team_prompts as tp

# ---------------------------------------------------------------------------
# fixture: build_prompt 내부의 파일 쓰기를 tmp_path로 격리
# ---------------------------------------------------------------------------


@pytest.fixture()
def patched_build_prompt(tmp_path, monkeypatch):
    """team_prompts.build_prompt에서 하드코딩된 경로를 tmp_path로 교체.

    실제 WORKSPACE/memory/tasks/ 에 파일을 쓰지 않도록
    Path.write_text와 mkdir를 tmp_path 기준으로 우회한다.
    """
    task_dir = tmp_path / "memory" / "tasks"
    task_dir.mkdir(parents=True, exist_ok=True)

    original_build = tp.build_prompt

    def _safe_build(team_id, task_id, task_desc, level="normal", project_id=None):
        """task 파일을 tmp_path에 저장한 뒤 나머지 프롬프트 생성 로직 실행"""
        # team 검증은 원본과 동일하게 수행
        team = tp.TEAM_INFO.get(team_id)
        if not team:
            raise ValueError(f"알 수 없는 팀 ID: {team_id}")

        # task 파일을 tmp_path에 저장 (하드코딩 경로 우회)
        task_file_path_real = str(tmp_path / "memory" / "tasks" / f"{task_id}.md")
        (tmp_path / "memory" / "tasks" / f"{task_id}.md").write_text(task_desc, encoding="utf-8")

        _ws = str(_WORKSPACE)
        short_desc = task_desc[:20]
        timer_start = f"python3 {_ws}/memory/task-timer.py start {task_id} " f'--team {team_id} --desc "{short_desc}"'
        timer_end = f"python3 {_ws}/memory/task-timer.py end {task_id}"
        report_path = f"{_ws}/memory/reports/{task_id}.md"

        if team["type"] == "direct":
            prompt = tp._build_direct_prompt(
                team,
                team_id,
                task_id,
                task_file_path_real,
                level,
                timer_start,
                timer_end,
                report_path,
                project_id=project_id,
            )
        elif team["type"] == "mcp":
            prompt = tp._build_mcp_prompt(
                team,
                team_id,
                task_id,
                task_file_path_real,
                level,
                timer_start,
                timer_end,
                report_path,
                project_id=project_id,
            )
        else:
            prompt = tp._build_glm_prompt(
                team,
                team_id,
                task_id,
                task_desc,
                level,
                timer_start,
                timer_end,
                report_path,
                project_id=project_id,
            )

        prompt += tp._build_three_docs_section(task_id, level)
        prompt += tp._build_verification_section(level, task_id)

        if level == "critical":
            prompt = "**[CRITICAL] 이 작업은 중요도 critical입니다. 품질 우선으로 신중하게 작업하세요.**\n\n" + prompt
        elif level == "security":
            prompt = "**[SECURITY] 이 작업은 보안 중요 작업입니다. 보안 최우선으로 작업하세요.**\n\n" + prompt

        return prompt

    monkeypatch.setattr(tp, "build_prompt", _safe_build)
    return _safe_build


# ---------------------------------------------------------------------------
# 1. 팀별 프롬프트 내용 검증
# ---------------------------------------------------------------------------


class TestBuildPromptTeamContent:
    """각 팀에 맞는 팀장/팀원 정보가 프롬프트에 포함되는지 확인"""

    def test_dev1_team_contains_hermes(self, patched_build_prompt):
        prompt = patched_build_prompt("dev1-team", "task-1.1", "로그인 페이지 개발")
        assert "헤르메스" in prompt

    def test_dev1_team_contains_all_members(self, patched_build_prompt):
        prompt = patched_build_prompt("dev1-team", "task-1.1", "로그인 페이지 개발")
        for member in ["불칸", "이리스", "아테나", "아르고스"]:
            assert member in prompt, f"dev1-team 프롬프트에 팀원 '{member}' 누락"

    def test_dev2_team_contains_odin(self, patched_build_prompt):
        prompt = patched_build_prompt("dev2-team", "task-2.1", "API 서버 구축")
        assert "오딘" in prompt

    def test_dev2_team_contains_all_members(self, patched_build_prompt):
        prompt = patched_build_prompt("dev2-team", "task-2.1", "API 서버 구축")
        for member in ["토르", "프레이야", "미미르", "헤임달"]:
            assert member in prompt, f"dev2-team 프롬프트에 팀원 '{member}' 누락"

    def test_dev3_team_contains_dagda(self, patched_build_prompt):
        prompt = patched_build_prompt("dev3-team", "task-3.1", "데이터 파이프라인 구축")
        assert "다그다" in prompt  # "다그다 (Dagda)"

    def test_dev8_team_contains_ra(self, patched_build_prompt):
        prompt = patched_build_prompt("dev8-team", "task-8.1", "데이터 파이프라인 구축")
        assert "라" in prompt  # "라 (Ra)" 또는 "라(Ra)"

    def test_dev8_team_contains_mcp_tools(self, patched_build_prompt):
        """dev8-team은 MCP 타입이므로 glm_backend 등 MCP tool 관련 키워드가 포함되어야 함"""
        prompt = patched_build_prompt("dev8-team", "task-8.1", "데이터 파이프라인 구축")
        assert "glm_backend" in prompt or "glm_frontend" in prompt or "MCP" in prompt

    def test_dev8_team_contains_team_id(self, patched_build_prompt):
        """dev8-team(GLM 타입)은 openclaw에 위임하므로 팀원 목록 대신 팀 ID가 포함됨"""
        prompt = patched_build_prompt("dev8-team", "task-8.1", "데이터 파이프라인 구축")
        assert "dev8-team" in prompt

    def test_prompt_contains_task_id(self, patched_build_prompt):
        prompt = patched_build_prompt("dev1-team", "task-42.1", "특정 태스크 테스트")
        assert "task-42.1" in prompt


# ---------------------------------------------------------------------------
# 2. level별 차이 확인
# ---------------------------------------------------------------------------


class TestBuildPromptLevels:
    """normal / critical / security 레벨별 프롬프트 차이 검증"""

    def test_normal_contains_selfcheck(self, patched_build_prompt):
        """normal: 셀프 QC 섹션만 포함"""
        prompt = patched_build_prompt("dev1-team", "task-1.1", "작업", level="normal")
        assert "검증 레벨:" in prompt

    def test_normal_does_not_contain_critical_marker(self, patched_build_prompt):
        """normal: [CRITICAL] 문구 없음"""
        prompt = patched_build_prompt("dev1-team", "task-1.1", "작업", level="normal")
        assert "[CRITICAL]" not in prompt

    def test_normal_does_not_contain_security_marker(self, patched_build_prompt):
        """normal: [SECURITY] 문구 없음"""
        prompt = patched_build_prompt("dev1-team", "task-1.1", "작업", level="normal")
        assert "[SECURITY]" not in prompt

    def test_critical_contains_critical_marker(self, patched_build_prompt):
        """critical: [CRITICAL] 문구 프롬프트 상단 포함"""
        prompt = patched_build_prompt("dev1-team", "task-1.1", "작업", level="critical")
        assert "[CRITICAL]" in prompt

    def test_critical_contains_maat_subagent(self, patched_build_prompt):
        """critical: 검증 레벨이 critical로 전달되는지 확인"""
        prompt = patched_build_prompt("dev1-team", "task-1.1", "작업", level="critical")
        assert "검증 레벨: critical" in prompt

    def test_critical_does_not_contain_security_marker(self, patched_build_prompt):
        """critical: [SECURITY] 문구 없음"""
        prompt = patched_build_prompt("dev1-team", "task-1.1", "작업", level="critical")
        assert "[SECURITY]" not in prompt

    def test_security_contains_security_marker(self, patched_build_prompt):
        """security: [SECURITY] 문구 프롬프트 상단 포함"""
        prompt = patched_build_prompt("dev1-team", "task-1.1", "작업", level="security")
        assert "[SECURITY]" in prompt

    def test_security_contains_maat_subagent(self, patched_build_prompt):
        """security: 검증 레벨이 security로 전달되는지 확인"""
        prompt = patched_build_prompt("dev1-team", "task-1.1", "작업", level="security")
        assert "검증 레벨: security" in prompt

    def test_security_contains_loki_subagent(self, patched_build_prompt):
        """security: 검증 레벨 security이고 QC-RULES.md 참조 포함"""
        prompt = patched_build_prompt("dev1-team", "task-1.1", "작업", level="security")
        assert "검증 레벨: security" in prompt and "QC-RULES.md" in prompt

    def test_security_contains_selfcheck(self, patched_build_prompt):
        """security: 셀프 QC도 포함 (critical 요소 모두 상속)"""
        prompt = patched_build_prompt("dev1-team", "task-1.1", "작업", level="security")
        assert "검증 레벨:" in prompt

    def test_critical_marker_at_start_of_prompt(self, patched_build_prompt):
        """critical: [CRITICAL] 문구가 프롬프트 맨 앞에 위치"""
        prompt = patched_build_prompt("dev1-team", "task-1.1", "작업", level="critical")
        assert "**[CRITICAL]" in prompt

    def test_security_marker_at_start_of_prompt(self, patched_build_prompt):
        """security: [SECURITY] 문구가 프롬프트 맨 앞에 위치"""
        prompt = patched_build_prompt("dev1-team", "task-1.1", "작업", level="security")
        assert "**[SECURITY]" in prompt


# ---------------------------------------------------------------------------
# 3. project_id 유무에 따른 경로 차이
# ---------------------------------------------------------------------------


class TestBuildPromptProjectId:
    """project_id 없음 → DIRECT-WORKFLOW.md 참조, project_id 있음 → project_id 값 포함"""

    def test_no_project_id_contains_system_task_message(self, patched_build_prompt):
        """project_id=None: DIRECT-WORKFLOW.md 참조 포함 (direct 팀)"""
        prompt = patched_build_prompt("dev1-team", "task-1.1", "작업 설명", project_id=None)
        assert "DIRECT-WORKFLOW.md" in prompt

    def test_project_id_contains_project_path(self, patched_build_prompt):
        """project_id='myproj': 'project_id: myproj' 포함 (direct 팀)"""
        prompt = patched_build_prompt("dev1-team", "task-1.1", "작업 설명", project_id="myproj")
        assert "project_id: myproj" in prompt

    def test_no_project_id_glm_team_contains_system_task(self, patched_build_prompt):
        """project_id=None, dev8-team(GLM): DIRECT-WORKFLOW.md 참조 포함"""
        prompt = patched_build_prompt("dev8-team", "task-8.1", "GLM 작업", project_id=None)
        assert "DIRECT-WORKFLOW.md" in prompt

    def test_project_id_glm_team_contains_project_path(self, patched_build_prompt):
        """project_id='testproj', dev8-team(GLM): 'project_id: testproj' 포함"""
        prompt = patched_build_prompt("dev8-team", "task-8.1", "GLM 작업", project_id="testproj")
        assert "project_id: testproj" in prompt

    def test_project_id_isolation_rule_present(self, patched_build_prompt):
        """project_id 지정 시 'project_id: sandbox' 포함"""
        prompt = patched_build_prompt("dev2-team", "task-2.1", "격리 테스트", project_id="sandbox")
        assert "project_id: sandbox" in prompt


# ---------------------------------------------------------------------------
# 4. 잘못된 team_id → ValueError
# ---------------------------------------------------------------------------


class TestBuildPromptInvalidTeamId:
    """알 수 없는 팀 ID 전달 시 ValueError가 발생하는지 검증"""

    def test_invalid_team_id_raises_value_error(self, patched_build_prompt):
        with pytest.raises(ValueError, match="알 수 없는 팀 ID"):
            patched_build_prompt("unknown-team", "task-1.1", "작업")

    def test_empty_team_id_raises_value_error(self, patched_build_prompt):
        with pytest.raises(ValueError):
            patched_build_prompt("", "task-1.1", "작업")

    def test_none_team_id_raises_value_error(self, patched_build_prompt):
        """None을 team_id로 전달하면 ValueError 또는 TypeError 발생"""
        with pytest.raises((ValueError, TypeError)):
            patched_build_prompt(None, "task-1.1", "작업")


# ---------------------------------------------------------------------------
# 5. TEAM_INFO 구조 검증
# ---------------------------------------------------------------------------


class TestTeamInfo:
    """TEAM_INFO 딕셔너리의 구조가 올바른지 확인"""

    @pytest.mark.parametrize(
        "team_id", ["dev1-team", "dev2-team", "dev3-team", "marketing", "consulting", "publishing", "design"]
    )
    def test_team_info_has_required_keys(self, team_id):
        team = tp.TEAM_INFO[team_id]
        assert "leader" in team
        assert "role" in team
        assert "type" in team
        assert "members" in team

    def test_dev3_team_type_is_direct(self):
        assert tp.TEAM_INFO["dev3-team"]["type"] == "direct"

    def test_dev8_team_type_is_mcp(self):
        assert tp.TEAM_INFO["dev8-team"]["type"] == "mcp"

    def test_dev1_dev2_team_type_is_direct(self):
        assert tp.TEAM_INFO["dev1-team"]["type"] == "direct"
        assert tp.TEAM_INFO["dev2-team"]["type"] == "direct"

    def test_marketing_type(self):
        assert tp.TEAM_INFO["marketing"]["type"] == "marketing"

    def test_consulting_type(self):
        assert tp.TEAM_INFO["consulting"]["type"] == "consulting"

    def test_publishing_type(self):
        assert tp.TEAM_INFO["publishing"]["type"] == "publishing"

    def test_design_type(self):
        assert tp.TEAM_INFO["design"]["type"] == "design"


# ---------------------------------------------------------------------------
# 6. 원본 build_prompt() 직접 호출 (lines 93-133 커버)
# ---------------------------------------------------------------------------


class TestBuildPromptOriginal:
    """원본 build_prompt() 함수 직접 호출 테스트 (lines 93-133 커버)

    patched_build_prompt가 아닌 원본 tp.build_prompt를 호출하여
    lines 93-133을 커버합니다. 파일 쓰기는 monkeypatch로 격리.
    """

    @pytest.fixture(autouse=True)
    def _patch_file_write(self, tmp_path, monkeypatch):
        """Path.write_text와 Path.mkdir을 tmp_path 기준으로 격리"""
        task_dir = tmp_path / "memory" / "tasks"
        task_dir.mkdir(parents=True, exist_ok=True)

        original_write_text = Path.write_text
        original_mkdir = Path.mkdir

        def patched_write_text(self_path, content, encoding=None):
            # /home/jay/workspace/memory/tasks/ 경로를 tmp_path로 리다이렉트
            path_str = str(self_path)
            if f"{_WORKSPACE}/memory/tasks/" in path_str:
                filename = self_path.name
                redirected = task_dir / filename
                return original_write_text(redirected, content, encoding=encoding)
            return original_write_text(self_path, content, encoding=encoding)

        def patched_mkdir(self_path, parents=False, exist_ok=False):
            path_str = str(self_path)
            if f"{_WORKSPACE}/memory/tasks" in path_str:
                return  # tmp_path에 이미 생성됨
            return original_mkdir(self_path, parents=parents, exist_ok=exist_ok)

        monkeypatch.setattr(Path, "write_text", patched_write_text)
        monkeypatch.setattr(Path, "mkdir", patched_mkdir)

    def test_direct_team_build_prompt(self):
        """dev1-team(direct): 원본 build_prompt가 올바른 프롬프트 생성"""
        import prompts.team_prompts as tp_mod

        # tp 모듈을 reimport하여 monkeypatch된 Path를 사용
        result = tp_mod.build_prompt("dev1-team", "task-test-1", "직접 호출 테스트")
        assert "헤르메스" in result
        assert "task-test-1" in result

    def test_mcp_team_build_prompt(self):
        """dev8-team(mcp): 원본 build_prompt가 MCP 프롬프트 생성"""
        result = tp.build_prompt("dev8-team", "task-test-2", "MCP 직접 호출")
        assert "라" in result or "Ra" in result
        assert "glm_backend" in result

    def test_critical_level_original(self):
        """critical 레벨: 원본 build_prompt에서 [CRITICAL] 상단 삽입"""
        result = tp.build_prompt("dev1-team", "task-test-3", "크리티컬 테스트", level="critical")
        assert "**[CRITICAL]" in result
        assert "검증 레벨: critical" in result

    def test_security_level_original(self):
        """security 레벨: 원본 build_prompt에서 [SECURITY] 상단 삽입"""
        result = tp.build_prompt("dev2-team", "task-test-4", "보안 테스트", level="security")
        assert "**[SECURITY]" in result
        assert "검증 레벨: security" in result

    def test_normal_level_no_markers(self):
        """normal 레벨: [CRITICAL], [SECURITY] 없음"""
        result = tp.build_prompt("dev1-team", "task-test-5", "일반 테스트", level="normal")
        assert "[CRITICAL]" not in result
        assert "[SECURITY]" not in result
        assert "검증 레벨:" in result

    def test_project_id_direct(self):
        """project_id 지정 시 project_id 값 포함"""
        result = tp.build_prompt("dev1-team", "task-test-6", "프로젝트 작업", project_id="myproj")
        assert "project_id: myproj" in result

    def test_project_id_glm(self):
        """dev8-team + project_id: GLM 프롬프트에 project_id 값 포함"""
        result = tp.build_prompt("dev8-team", "task-test-7", "GLM 프로젝트", project_id="testproj")
        assert "project_id: testproj" in result

    def test_invalid_team_raises_error(self):
        """잘못된 team_id → ValueError"""
        with pytest.raises(ValueError, match="알 수 없는 팀 ID"):
            tp.build_prompt("invalid-team", "task-1.1", "테스트")


# ---------------------------------------------------------------------------
# 7. _build_cowork_section() 직접 호출 테스트
# ---------------------------------------------------------------------------


class TestBuildCoworkSection:
    """_build_cowork_section() 함수 직접 테스트"""

    def test_dev1_team_cowork_contains_members(self):
        """dev1-team 코워크 섹션에 팀원들 포함"""
        result = tp._build_cowork_section("dev1-team")
        assert "불칸" in result
        assert "이리스" in result
        assert "아테나" in result
        assert "아르고스" in result

    def test_dev2_team_cowork_contains_members(self):
        """dev2-team 코워크 섹션에 팀원들 포함"""
        result = tp._build_cowork_section("dev2-team")
        assert "토르" in result
        assert "프레이야" in result

    def test_unknown_team_returns_empty_members(self):
        """TEAM_MEMBER_ROLES에 없는 팀은 빈 멤버 (dev3-team 등)"""
        result = tp._build_cowork_section("dev3-team")
        # dev3-team은 TEAM_MEMBER_ROLES에 없으므로 멤버 리스트가 비어있지만
        # 모델 선택 가이드 등은 포함 (P2-5: flag ON 시 "모델 가이드"로 변경됨)
        assert "모델 선택 가이드" in result or "모델 가이드" in result

    def test_cowork_contains_task_tool(self):
        """코워크 섹션에 Task tool 관련 내용 포함"""
        result = tp._build_cowork_section("dev1-team")
        assert "Task tool" in result


# ---------------------------------------------------------------------------
# 8. _build_verification_section() 직접 호출 테스트
# ---------------------------------------------------------------------------


class TestBuildVerificationSection:
    """_build_verification_section() 함수 직접 테스트"""

    def test_normal_returns_selfcheck_only(self):
        result = tp._build_verification_section("normal")
        assert "셀프 QC" in result
        assert "마아트 독립 검증 필수" not in result
        assert "로키 보안 감사" not in result

    def test_critical_returns_selfcheck_and_maat(self):
        result = tp._build_verification_section("critical")
        assert "셀프 QC" in result
        assert "검증 레벨: critical" in result

    def test_security_returns_all(self):
        result = tp._build_verification_section("security")
        assert "셀프 QC" in result
        assert "검증 레벨: security" in result

    def test_unknown_level_treated_as_security(self):
        """unknown level은 해당 level 문자열 그대로 전달"""
        result = tp._build_verification_section("unknown_level")
        assert "셀프 QC" in result
        assert "검증 레벨: unknown_level" in result


class TestTaskTypeQCSkip:
    """task_type 파라미터에 따른 QC 섹션 포함/제외 테스트 (task-202.1)"""

    def test_coding_type_includes_qc(self):
        """task_type='coding'이면 QC 섹션이 포함되어야 함"""
        result = tp.build_prompt("dev1-team", "task-test-001", "테스트 작업", level="normal", task_type="coding")
        assert "셀프 QC" in result
        assert "QC-RULES.md" in result

    def test_research_type_skips_qc(self):
        """task_type='research'이면 QC 섹션이 제외되어야 함"""
        result = tp.build_prompt("dev1-team", "task-test-002", "리서치 작업", level="normal", task_type="research")
        assert "검증 레벨:" not in result
        assert "QC-RULES.md를 읽고 따르세요" not in result

    def test_check_type_skips_qc(self):
        """task_type='check'이면 QC 섹션이 제외되어야 함"""
        result = tp.build_prompt("dev1-team", "task-test-003", "점검 작업", level="normal", task_type="check")
        assert "검증 레벨:" not in result
        assert "QC-RULES.md를 읽고 따르세요" not in result

    def test_default_type_is_coding(self):
        """task_type 미지정 시 기본값은 'coding' (QC 포함)"""
        result = tp.build_prompt("dev1-team", "task-test-004", "기본 작업", level="normal")
        assert "셀프 QC" in result
        assert "QC-RULES.md" in result

    def test_glm_team_coding_includes_qc(self):
        """GLM 팀(dev8)도 task_type='coding'이면 QC 포함"""
        result = tp.build_prompt("dev8-team", "task-test-005", "GLM 코딩 작업", level="normal", task_type="coding")
        assert "검증 레벨:" in result

    def test_glm_team_research_skips_qc(self):
        """GLM 팀(dev8)도 task_type='research'면 QC 제외"""
        result = tp.build_prompt("dev8-team", "task-test-006", "GLM 리서치 작업", level="normal", task_type="research")
        assert "검증 레벨:" not in result

    def test_critical_with_research_still_skips_qc(self):
        """level='critical'이어도 task_type='research'면 QC 제외"""
        result = tp.build_prompt("dev1-team", "task-test-007", "중요 리서치", level="critical", task_type="research")
        assert "검증 레벨:" not in result
        assert "마아트" not in result

    def test_security_with_research_still_skips_qc(self):
        """level='security'이어도 task_type='research'면 QC 제외"""
        result = tp.build_prompt("dev1-team", "task-test-008", "보안 리서치", level="security", task_type="research")
        assert "검증 레벨:" not in result
        assert "로키" not in result

    def test_research_type_still_has_critical_marker(self):
        """task_type='research'여도 level='critical'이면 CRITICAL 마커는 있어야 함"""
        result = tp.build_prompt("dev1-team", "task-test-009", "중요 리서치", level="critical", task_type="research")
        assert "[CRITICAL]" in result
        assert "검증 레벨:" not in result


# ---------------------------------------------------------------------------
# 9. 마케팅 프롬프트 테스트
# ---------------------------------------------------------------------------


class TestBuildMarketingPrompt:
    """마케팅 프롬프트 생성 테스트"""

    def test_marketing_prompt_contains_role(self):
        """마케팅 프롬프트에 '마케팅 팀장' 역할 포함"""
        result = tp.build_prompt("marketing", "task-mkt-1", "콘텐츠 작성")
        assert "마케팅 팀장" in result

    def test_marketing_prompt_contains_apollo(self):
        """마케팅 프롬프트가 팀 카드 파일(marketing.md)을 참조하는지 확인"""
        result = tp.build_prompt("marketing", "task-mkt-2", "블로그 포스트")
        assert "prompts/teams/marketing.md" in result

    def test_marketing_prompt_contains_peitho(self):
        """마케팅 프롬프트가 팀 카드 파일(marketing.md)을 참조하는지 확인"""
        result = tp.build_prompt("marketing", "task-mkt-3", "카피 작성")
        assert "prompts/teams/marketing.md" in result

    def test_marketing_prompt_contains_skills(self):
        """마케팅 프롬프트가 팀 카드 파일(marketing.md)을 참조하는지 확인"""
        result = tp.build_prompt("marketing", "task-mkt-4", "전략 수립")
        assert "prompts/teams/marketing.md" in result

    def test_marketing_prompt_contains_brand_info(self):
        """마케팅 프롬프트가 팀 카드 파일(marketing.md)을 참조하는지 확인"""
        result = tp.build_prompt("marketing", "task-mkt-5", "SNS 콘텐츠")
        assert "prompts/teams/marketing.md" in result

    def test_marketing_prompt_contains_task_id(self):
        """마케팅 프롬프트에 task_id 포함"""
        result = tp.build_prompt("marketing", "task-mkt-6", "작업")
        assert "task-mkt-6" in result

    def test_marketing_prompt_contains_workflow(self):
        """마케팅 프롬프트가 워크플로우 파일(LOGICAL-TEAM-WORKFLOW.md)을 참조하는지 확인"""
        result = tp.build_prompt("marketing", "task-mkt-7", "작업")
        assert "LOGICAL-TEAM-WORKFLOW.md" in result

    def test_marketing_with_coding_type_includes_qc(self):
        """마케팅 + task_type=coding이면 QC 포함"""
        result = tp.build_prompt("marketing", "task-mkt-8", "코딩 작업", task_type="coding")
        assert "셀프 QC" in result

    def test_marketing_with_research_type_skips_qc(self):
        """마케팅 + task_type=research면 QC 제외"""
        result = tp.build_prompt("marketing", "task-mkt-9", "리서치", task_type="research")
        assert "검증 레벨:" not in result


# ---------------------------------------------------------------------------
# 10. 컨설팅 프롬프트 테스트
# ---------------------------------------------------------------------------


class TestBuildConsultingPrompt:
    """컨설팅 프롬프트 생성 테스트"""

    def test_consulting_prompt_contains_role(self):
        """컨설팅 프롬프트에 '컨설팅 팀장' 역할 포함"""
        result = tp.build_prompt("consulting", "task-con-1", "보장 분석")
        assert "컨설팅 팀장" in result

    def test_consulting_prompt_contains_asclepius(self):
        """컨설팅 프롬프트가 팀 카드 파일(consulting.md)을 참조하는지 확인"""
        result = tp.build_prompt("consulting", "task-con-2", "보장 갭 분석")
        assert "prompts/teams/consulting.md" in result

    def test_consulting_prompt_contains_themis(self):
        """컨설팅 프롬프트가 팀 카드 파일(consulting.md)을 참조하는지 확인"""
        result = tp.build_prompt("consulting", "task-con-3", "약관 비교")
        assert "prompts/teams/consulting.md" in result

    def test_consulting_prompt_contains_skills(self):
        """컨설팅 프롬프트가 팀 카드 파일(consulting.md)을 참조하는지 확인"""
        result = tp.build_prompt("consulting", "task-con-4", "PDF 분석")
        assert "prompts/teams/consulting.md" in result

    def test_consulting_prompt_contains_task_id(self):
        """컨설팅 프롬프트에 task_id 포함"""
        result = tp.build_prompt("consulting", "task-con-5", "작업")
        assert "task-con-5" in result

    def test_consulting_prompt_contains_workflow(self):
        """컨설팅 프롬프트가 워크플로우 파일(LOGICAL-TEAM-WORKFLOW.md)을 참조하는지 확인"""
        result = tp.build_prompt("consulting", "task-con-6", "작업")
        assert "LOGICAL-TEAM-WORKFLOW.md" in result

    def test_consulting_prompt_contains_insurance_context(self):
        """컨설팅 프롬프트에 보험 관련 컨텍스트 포함"""
        result = tp.build_prompt("consulting", "task-con-7", "보험 분석")
        assert "보험" in result

    def test_consulting_with_coding_type_includes_qc(self):
        """컨설팅 + task_type=coding이면 QC 포함"""
        result = tp.build_prompt("consulting", "task-con-8", "코딩 작업", task_type="coding")
        assert "셀프 QC" in result


# ---------------------------------------------------------------------------
# 11. team_prompts.py import 시 ANU_KEY 없어도 크래시 안 남 (task-448.1)
# ---------------------------------------------------------------------------


class TestModuleLevelImportSafety:
    """team_prompts.py import 시 ANU_KEY 없어도 크래시 안 남 (task-448.1)"""

    def test_import_without_anu_key_no_crash(self):
        """환경변수 없는 상태에서 from prompts.team_prompts import TEAM_INFO 성공"""
        import importlib
        import types

        # 캐시된 모듈 제거
        mods_to_remove = [k for k in sys.modules if "team_prompts" in k]
        saved = {}
        for m in mods_to_remove:
            saved[m] = sys.modules.pop(m)

        try:
            env_without_key = {k: v for k, v in os.environ.items() if k != "COKACDIR_KEY_ANU"}
            with patch.dict(os.environ, env_without_key, clear=True):
                # 재import 시 EnvironmentError가 발생하지 않아야 함
                import prompts.team_prompts as tp_fresh

                assert hasattr(tp_fresh, "TEAM_INFO")
                assert "dev1-team" in tp_fresh.TEAM_INFO
        finally:
            # 원래 모듈 복원
            for m, mod in saved.items():
                sys.modules[m] = mod


# ---------------------------------------------------------------------------
# 12. 출판팀(publishing) 프롬프트 테스트
# ---------------------------------------------------------------------------


class TestBuildPublishingPrompt:
    """출판팀 프롬프트 생성 테스트"""

    def test_publishing_prompt_contains_role(self):
        """출판 프롬프트에 '토트' 또는 '출판 센터장' 포함"""
        result = tp.build_prompt("publishing", "task-pub-1", "집필 작업")
        assert "토트" in result or "Thoth" in result

    def test_publishing_prompt_contains_calliope(self):
        """출판 프롬프트가 팀 카드 파일(publishing.md)을 참조하는지 확인"""
        result = tp.build_prompt("publishing", "task-pub-2", "아웃라인 설계")
        assert "prompts/teams/publishing.md" in result

    def test_publishing_prompt_contains_erato(self):
        """출판 프롬프트가 팀 카드 파일(publishing.md)을 참조하는지 확인"""
        result = tp.build_prompt("publishing", "task-pub-3", "초안 작성")
        assert "prompts/teams/publishing.md" in result

    def test_publishing_prompt_contains_clio(self):
        """출판 프롬프트가 팀 카드 파일(publishing.md)을 참조하는지 확인"""
        result = tp.build_prompt("publishing", "task-pub-4", "팩트 검증")
        assert "prompts/teams/publishing.md" in result

    def test_publishing_prompt_contains_terpsichore(self):
        """출판 프롬프트가 팀 카드 파일(publishing.md)을 참조하는지 확인"""
        result = tp.build_prompt("publishing", "task-pub-5", "퇴고")
        assert "prompts/teams/publishing.md" in result

    def test_publishing_prompt_contains_polyhymnia(self):
        """출판 프롬프트가 팀 카드 파일(publishing.md)을 참조하는지 확인"""
        result = tp.build_prompt("publishing", "task-pub-6", "마케팅 카피")
        assert "prompts/teams/publishing.md" in result

    def test_publishing_prompt_contains_task_id(self):
        """출판 프롬프트에 task_id 포함"""
        result = tp.build_prompt("publishing", "task-pub-7", "작업")
        assert "task-pub-7" in result

    def test_publishing_prompt_contains_anti_hallucination(self):
        """출판 프롬프트가 팀 카드 파일(publishing.md)을 참조하는지 확인"""
        result = tp.build_prompt("publishing", "task-pub-8", "집필")
        assert "prompts/teams/publishing.md" in result

    def test_publishing_prompt_contains_6dim_review(self):
        """출판 프롬프트가 팀 카드 파일(publishing.md)을 참조하는지 확인"""
        result = tp.build_prompt("publishing", "task-pub-9", "검토")
        assert "prompts/teams/publishing.md" in result

    def test_publishing_prompt_contains_content_balance(self):
        """출판 프롬프트가 팀 카드 파일(publishing.md)을 참조하는지 확인"""
        result = tp.build_prompt("publishing", "task-pub-10", "밸런스")
        assert "prompts/teams/publishing.md" in result

    def test_publishing_with_coding_type_includes_qc(self):
        """출판 + task_type=coding이면 QC 포함"""
        result = tp.build_prompt("publishing", "task-pub-11", "코딩 작업", task_type="coding")
        assert "셀프 QC" in result

    def test_publishing_with_research_type_skips_qc(self):
        """출판 + task_type=research면 QC 제외"""
        result = tp.build_prompt("publishing", "task-pub-12", "리서치", task_type="research")
        assert "검증 레벨:" not in result

    def test_publishing_type_is_publishing(self):
        """TEAM_INFO에서 publishing의 type이 'publishing'"""
        assert tp.TEAM_INFO["publishing"]["type"] == "publishing"


# ---------------------------------------------------------------------------
# 12.5. 디자인팀(design) 프롬프트 테스트
# ---------------------------------------------------------------------------


class TestBuildDesignPrompt:
    """디자인팀 프롬프트 생성 테스트"""

    def test_design_prompt_contains_role(self):
        """디자인 프롬프트에 '아마테라스' 또는 '디자인 팀장' 포함"""
        result = tp.build_prompt("design", "task-des-1", "카드뉴스 제작")
        assert "아마테라스" in result or "Amaterasu" in result

    def test_design_prompt_contains_benzaiten(self):
        """디자인 프롬프트가 팀 카드 파일(design.md)을 참조하는지 확인"""
        result = tp.build_prompt("design", "task-des-2", "배너 제작")
        assert "prompts/teams/design.md" in result

    def test_design_prompt_contains_inari(self):
        """디자인 프롬프트가 팀 카드 파일(design.md)을 참조하는지 확인"""
        result = tp.build_prompt("design", "task-des-3", "하이브리드 이미지")
        assert "prompts/teams/design.md" in result

    def test_design_prompt_contains_kaguya(self):
        """디자인 프롬프트가 팀 카드 파일(design.md)을 참조하는지 확인"""
        result = tp.build_prompt("design", "task-des-4", "아트 디자인")
        assert "prompts/teams/design.md" in result

    def test_design_prompt_contains_skills(self):
        """디자인 프롬프트가 팀 카드 파일(design.md)을 참조하는지 확인"""
        result = tp.build_prompt("design", "task-des-5", "이미지 생성")
        assert "prompts/teams/design.md" in result

    def test_design_prompt_contains_task_id(self):
        """디자인 프롬프트에 task_id 포함"""
        result = tp.build_prompt("design", "task-des-6", "작업")
        assert "task-des-6" in result

    def test_design_prompt_contains_workflow(self):
        """디자인 프롬프트가 워크플로우 파일(LOGICAL-TEAM-WORKFLOW.md)을 참조하는지 확인"""
        result = tp.build_prompt("design", "task-des-7", "작업")
        assert "LOGICAL-TEAM-WORKFLOW.md" in result

    def test_design_prompt_contains_routing_rules(self):
        """디자인 프롬프트가 팀 카드 파일(design.md)을 참조하는지 확인"""
        result = tp.build_prompt("design", "task-des-8", "이미지")
        assert "prompts/teams/design.md" in result

    def test_design_with_coding_type_includes_qc(self):
        """디자인 + task_type=coding이면 QC 포함"""
        result = tp.build_prompt("design", "task-des-9", "코딩 작업", task_type="coding")
        assert "셀프 QC" in result

    def test_design_with_research_type_skips_qc(self):
        """디자인 + task_type=research면 QC 제외"""
        result = tp.build_prompt("design", "task-des-10", "리서치", task_type="research")
        assert "검증 레벨:" not in result


# ---------------------------------------------------------------------------
# 14. 팀 카드 파일 콘텐츠 검증 (경량화 리팩토링 후)
# ---------------------------------------------------------------------------


class TestCardFileContent:
    """팀 카드 파일이 기존 인라인 내용을 올바르게 포함하는지 검증"""

    _CARD_DIR = _WORKSPACE / "prompts/teams"
    _WORKFLOW_FILE = _WORKSPACE / "prompts/LOGICAL-TEAM-WORKFLOW.md"

    def test_marketing_card_contains_members(self):
        content = (self._CARD_DIR / "marketing.md").read_text(encoding="utf-8")
        assert "아폴론" in content
        assert "페이토" in content
        assert "에이레네" in content

    def test_marketing_card_contains_skills(self):
        content = (self._CARD_DIR / "marketing.md").read_text(encoding="utf-8")
        assert "content-strategy" in content
        assert "copywriting" in content
        assert "blog-writer" in content

    def test_marketing_card_contains_brand_info(self):
        content = (self._CARD_DIR / "marketing.md").read_text(encoding="utf-8")
        assert "서울대보험쌤" in content
        assert "서울대연금쌤" in content

    def test_consulting_card_contains_members(self):
        content = (self._CARD_DIR / "consulting.md").read_text(encoding="utf-8")
        assert "아스클레피오스" in content
        assert "테미스" in content

    def test_consulting_card_contains_skills(self):
        content = (self._CARD_DIR / "consulting.md").read_text(encoding="utf-8")
        assert "pdf" in content.lower()
        assert "supabase" in content.lower()

    def test_publishing_card_contains_members(self):
        content = (self._CARD_DIR / "publishing.md").read_text(encoding="utf-8")
        for name in ["칼리오페", "에라토", "클리오", "테르프시코레", "폴리뮤니아", "세쉬아트"]:
            assert name in content, f"publishing.md에 '{name}' 누락"

    def test_publishing_card_contains_quality_rules(self):
        content = (self._CARD_DIR / "publishing.md").read_text(encoding="utf-8")
        assert "반환각" in content
        assert "6차원" in content or "Fact Checker" in content
        assert "60%" in content

    def test_design_card_contains_members(self):
        content = (self._CARD_DIR / "design.md").read_text(encoding="utf-8")
        assert "벤자이텐" in content
        assert "이나리" in content
        assert "카구야" in content

    def test_design_card_contains_skills(self):
        content = (self._CARD_DIR / "design.md").read_text(encoding="utf-8")
        assert "satori-cardnews" in content
        assert "canvas-design" in content

    def test_design_card_contains_routing(self):
        content = (self._CARD_DIR / "design.md").read_text(encoding="utf-8")
        assert "포토리얼" in content or "라우팅" in content

    def test_workflow_file_contains_meeting_rules(self):
        content = self._WORKFLOW_FILE.read_text(encoding="utf-8")
        assert "에이전트 미팅 기록 규칙" in content

    def test_workflow_file_contains_subagent_rules(self):
        content = self._WORKFLOW_FILE.read_text(encoding="utf-8")
        assert "500자 이내" in content

    def test_workflow_file_contains_cross_org(self):
        content = self._WORKFLOW_FILE.read_text(encoding="utf-8")
        assert "마아트" in content
        assert "로키" in content

    def test_workflow_file_contains_finish_task(self):
        content = self._WORKFLOW_FILE.read_text(encoding="utf-8")
        assert "finish-task.sh" in content


# ---------------------------------------------------------------------------
# 13. 세션 경량화 규칙 포함 테스트 (TDD)
# ---------------------------------------------------------------------------


class TestSessionOptimizationInPrompts:
    """direct 및 GLM 팀 프롬프트에 세션 경량화 규칙이 포함되어 있는지 확인"""

    @pytest.fixture(autouse=True)
    def _patch_anu_key(self, monkeypatch):
        """ANU_KEY를 테스트용 값으로 패치"""
        monkeypatch.setattr(tp, "ANU_KEY", "test-anu-key")

    def test_direct_prompt_contains_session_optimization(self):
        """direct 팀 프롬프트가 DIRECT-WORKFLOW.md를 참조하는지 확인 (세션 경량화 규칙은 WORKFLOW-RULES.md에 있음)"""
        team = tp.TEAM_INFO["dev1-team"]
        result = tp._build_direct_prompt(
            team,
            "dev1-team",
            "task-test-session-1",
            "/tmp/task-test-session-1.md",
            "normal",
            "python3 /timer.py start",
            "python3 /timer.py end",
            "/tmp/report.md",
        )
        assert "DIRECT-WORKFLOW.md" in result, "direct 프롬프트에 'DIRECT-WORKFLOW.md' 미포함"

    def test_glm_prompt_contains_session_optimization(self):
        """GLM 팀 프롬프트에도 세션 경량화 규칙이 포함되어 있는지 확인"""
        team = tp.TEAM_INFO["dev8-team"]
        result = tp._build_glm_prompt(
            team,
            "dev8-team",
            "task-test-session-2",
            "GLM 세션 경량화 테스트",
            "normal",
            "python3 /timer.py start",
            "python3 /timer.py end",
            "/tmp/report.md",
        )
        assert "세션 경량화 규칙" in result, "GLM 프롬프트에 '세션 경량화 규칙' 미포함"
        assert "/compact" in result, "GLM 프롬프트에 '/compact' 미포함"
        assert "체크포인트" in result, "GLM 프롬프트에 '체크포인트' 미포함"


# ---------------------------------------------------------------------------
# N. 3문서 활용 지침 섹션 검증
# ---------------------------------------------------------------------------


class TestThreeDocsSection:
    """3문서 활용 지침 섹션이 level에 따라 올바르게 포함/미포함되는지 검증"""

    def test_three_docs_section_critical_included(self):
        """critical: 3문서 활용 지침 섹션 포함"""
        result = tp._build_three_docs_section("task-test", "critical")
        assert "3문서 활용 지침" in result

    def test_three_docs_section_security_included(self):
        """security: 3문서 활용 지침 섹션 포함"""
        result = tp._build_three_docs_section("task-test", "security")
        assert "3문서 활용 지침" in result

    def test_three_docs_section_normal_excluded(self):
        """normal: 3문서 활용 지침 섹션 미포함"""
        result = tp._build_three_docs_section("task-test", "normal")
        assert result == ""

    def test_three_docs_section_contains_plan_path(self):
        """3문서 섹션에 plan.md 경로 포함"""
        result = tp._build_three_docs_section("task-42", "critical")
        assert "memory/plans/tasks/task-42/plan.md" in result

    def test_three_docs_section_contains_context_notes_path(self):
        """3문서 섹션에 context-notes.md 경로 포함"""
        result = tp._build_three_docs_section("task-42", "critical")
        assert "memory/plans/tasks/task-42/context-notes.md" in result

    def test_three_docs_section_contains_checklist_path(self):
        """3문서 섹션에 checklist.md 경로 포함"""
        result = tp._build_three_docs_section("task-42", "critical")
        assert "memory/plans/tasks/task-42/checklist.md" in result

    def test_three_docs_section_contains_leader_duties(self):
        """3문서 섹션에 팀장 의무 항목 포함"""
        result = tp._build_three_docs_section("task-42", "critical")
        assert "팀장 의무" in result
        assert "plan.md" in result
        assert "context-notes.md" in result
        assert "checklist.md" in result

    def test_three_docs_section_empty_for_unknown_level(self):
        """알 수 없는 level: 빈 문자열 반환"""
        result = tp._build_three_docs_section("task-test", "unknown")
        assert result == ""


class TestThreeDocsInBuildPrompt:
    """build_prompt 통합 시 3문서 섹션이 올바르게 포함되는지 검증"""

    @pytest.fixture(autouse=True)
    def _patch_anu_key(self, monkeypatch):
        """ANU_KEY를 테스트용 값으로 패치"""
        monkeypatch.setattr(tp, "ANU_KEY", "test-anu-key")

    def test_critical_prompt_includes_three_docs(self, patched_build_prompt):
        """critical 레벨 프롬프트에 3문서 활용 지침 포함"""
        prompt = patched_build_prompt("dev1-team", "task-99", "테스트", level="critical")
        assert "3문서 활용 지침" in prompt

    def test_normal_prompt_excludes_three_docs(self, patched_build_prompt):
        """normal 레벨 프롬프트에 3문서 활용 지침 미포함"""
        prompt = patched_build_prompt("dev1-team", "task-99", "테스트", level="normal")
        assert "3문서 활용 지침" not in prompt

    def test_security_prompt_includes_three_docs(self, patched_build_prompt):
        """security 레벨 프롬프트에 3문서 활용 지침 포함"""
        prompt = patched_build_prompt("dev1-team", "task-99", "테스트", level="security")
        assert "3문서 활용 지침" in prompt


# ---------------------------------------------------------------------------
# 15. Codex 사전 검증 / Sanitize 게이트 실행 명령 테스트 (task-1925+1)
# ---------------------------------------------------------------------------


class TestCodexAndSanitizeGateInPrompt:
    """판정C-1/C-2: critical/security 레벨에서 Codex/Sanitize 실행 명령이 포함되는지 검증"""

    @pytest.fixture(autouse=True)
    def _patch_anu_key(self, monkeypatch):
        """ANU_KEY를 테스트용 값으로 패치"""
        monkeypatch.setattr(tp, "ANU_KEY", "test-anu-key")

    def test_critical_contains_codex_gate_command(self, patched_build_prompt):
        """critical: Codex 사전 검증 실행 명령이 프롬프트에 포함"""
        prompt = patched_build_prompt("dev1-team", "task-1925", "테스트", level="critical")
        assert "codex_gate_check.py" in prompt

    def test_critical_contains_sanitize_gate(self, patched_build_prompt):
        """critical: Sanitize 게이트 구체적 실행 방법이 프롬프트에 포함"""
        prompt = patched_build_prompt("dev1-team", "task-1925", "테스트", level="critical")
        assert "sanitize_text" in prompt

    def test_security_contains_codex_gate_command(self, patched_build_prompt):
        """security: Codex 사전 검증 실행 명령이 프롬프트에 포함"""
        prompt = patched_build_prompt("dev1-team", "task-1925", "테스트", level="security")
        assert "codex_gate_check.py" in prompt

    def test_security_contains_sanitize_gate(self, patched_build_prompt):
        """security: Sanitize 게이트 실행 방법이 프롬프트에 포함"""
        prompt = patched_build_prompt("dev1-team", "task-1925", "테스트", level="security")
        assert "sanitize_text" in prompt

    def test_normal_excludes_codex_gate(self, patched_build_prompt):
        """normal: Codex 사전 검증 명령 미포함"""
        prompt = patched_build_prompt("dev1-team", "task-1925", "테스트", level="normal")
        assert "codex_gate_check.py" not in prompt

    def test_normal_excludes_sanitize_gate_detail(self, patched_build_prompt):
        """normal: Sanitize 게이트 구체적 실행 방법 미포함"""
        prompt = patched_build_prompt("dev1-team", "task-1925", "테스트", level="normal")
        assert "sanitize_text" not in prompt

    def test_codex_command_contains_task_id(self, patched_build_prompt):
        """Codex 명령에 task_id가 포함되는지 확인"""
        prompt = patched_build_prompt("dev1-team", "task-1925", "테스트", level="critical")
        assert "task-1925" in prompt
        assert "codex_gate_check.py --task-id task-1925" in prompt


class TestL1SmokeTestRecordFormat:
    """판정B-2: L1 스모크테스트 결과 기록 형식이 프롬프트에 포함되는지 검증"""

    @pytest.fixture(autouse=True)
    def _patch_anu_key(self, monkeypatch):
        """ANU_KEY를 테스트용 값으로 패치"""
        monkeypatch.setattr(tp, "ANU_KEY", "test-anu-key")

    @pytest.fixture(autouse=True)
    def _patch_file_write(self, tmp_path, monkeypatch):
        """Path.write_text와 Path.mkdir을 tmp_path 기준으로 격리"""
        task_dir = tmp_path / "memory" / "tasks"
        task_dir.mkdir(parents=True, exist_ok=True)

        original_write_text = Path.write_text
        original_mkdir = Path.mkdir

        def patched_write_text(self_path, content, encoding=None):
            path_str = str(self_path)
            if f"{_WORKSPACE}/memory/tasks/" in path_str:
                filename = self_path.name
                redirected = task_dir / filename
                return original_write_text(redirected, content, encoding=encoding)
            return original_write_text(self_path, content, encoding=encoding)

        def patched_mkdir(self_path, parents=False, exist_ok=False):
            path_str = str(self_path)
            if f"{_WORKSPACE}/memory/tasks" in path_str:
                return
            return original_mkdir(self_path, parents=parents, exist_ok=exist_ok)

        monkeypatch.setattr(Path, "write_text", patched_write_text)
        monkeypatch.setattr(Path, "mkdir", patched_mkdir)

    def test_prompt_contains_l1_record_format(self):
        """모든 프롬프트에 L1 스모크테스트 결과 기록 형식 포함"""
        result = tp.build_prompt("dev1-team", "task-test-l1", "L1 테스트")
        assert "서버 재시작: [" in result
        assert "API 응답 확인: [" in result
        assert "스크린샷: [" in result

    def test_prompt_contains_incomplete_warning(self):
        """불완전 처리 경고 포함"""
        result = tp.build_prompt("dev1-team", "task-test-l1-2", "L1 테스트")
        assert "불완전 처리" in result
