"""
Phase 25: Auto-Harvesting 測試
================================
ADR-052 / Phase 25 P1: NemotronRunbookGenerator + AntiPattern Gate

測試策略: Source code inspection (禁止 Mock，禁止 import 模組)
- 驗證 runbook_generator.py 實作結構
- 驗證 auto_repair_service.py fire-and-forget 模式
- 驗證 knowledge.py 模型欄位
- 驗證 knowledge_service.py check_anti_pattern 邏輯

建立時間: 2026-04-05 (台北時區)
建立者: Claude Code (Phase 25 Auto-Harvesting 測試)
"""

from pathlib import Path
from types import SimpleNamespace

# Source file paths
_BASE = Path(__file__).parent.parent / "src"
_RUNBOOK_GEN = _BASE / "services" / "runbook_generator.py"
_AUTO_REPAIR = _BASE / "services" / "auto_repair_service.py"
_PLAYBOOK = _BASE / "models" / "playbook.py"
_KNOWLEDGE = _BASE / "models" / "knowledge.py"
_KNOWLEDGE_SVC = _BASE / "services" / "knowledge_service.py"


# =============================================================================
# TestRunbookGeneratorModule — 模組存在與結構
# =============================================================================

class TestRunbookGeneratorModule:
    """驗證 runbook_generator.py 模組存在並有正確結構"""

    def test_runbook_generator_module_exists(self):
        """src/services/runbook_generator.py 存在"""
        assert _RUNBOOK_GEN.exists(), f"找不到 {_RUNBOOK_GEN}"

    def test_runbook_generator_class_exists(self):
        """NemotronRunbookGenerator class 已定義"""
        source = _RUNBOOK_GEN.read_text()
        assert "class NemotronRunbookGenerator" in source

    def test_generate_runbook_method_exists(self):
        """generate_runbook() 方法存在"""
        source = _RUNBOOK_GEN.read_text()
        assert "async def generate_runbook" in source or "def generate_runbook" in source

    def test_generate_anti_pattern_method_exists(self):
        """generate_anti_pattern() 方法存在"""
        source = _RUNBOOK_GEN.read_text()
        assert "async def generate_anti_pattern" in source or "def generate_anti_pattern" in source

    def test_runbook_uses_auto_runbook_type(self):
        """runbook_generator.py 使用 'auto_runbook' entry type"""
        source = _RUNBOOK_GEN.read_text()
        assert "auto_runbook" in source

    def test_anti_pattern_uses_anti_pattern_type(self):
        """runbook_generator.py 使用 'anti_pattern' entry type"""
        source = _RUNBOOK_GEN.read_text()
        assert "anti_pattern" in source

    def test_runbook_status_is_draft(self):
        """成功修復 → AUTO_RUNBOOK 狀態為 DRAFT（需人工審核）"""
        source = _RUNBOOK_GEN.read_text()
        assert "DRAFT" in source or "draft" in source

    def test_anti_pattern_status_is_published(self):
        """失敗修復 → ANTI_PATTERN 狀態為 PUBLISHED（直接發布）"""
        source = _RUNBOOK_GEN.read_text()
        assert "PUBLISHED" in source or "published" in source

    def test_runbook_uses_nvidia_chat(self):
        """runbook_generator.py 使用 nvidia provider"""
        source = _RUNBOOK_GEN.read_text()
        assert "nvidia" in source

    def test_minimal_fallback_exists(self):
        """Nemotron 失敗時有 fallback 邏輯"""
        source = _RUNBOOK_GEN.read_text()
        assert "fallback" in source

    def test_runbook_review_card_is_structured_html(self):
        """Telegram Runbook 審核訊息必須是可掃描治理卡片，不直接傾倒 Markdown 原文"""
        from src.services.runbook_generator import format_runbook_review_card

        incident = SimpleNamespace(
            incident_id="INC-20260506-E54736",
            affected_services=["node-exporter-110"],
        )
        content = (
            "## 症狀描述\n"
            "Incident INC-20260506-E54736，受影響服務：node-exporter-110\n\n"
            "## 執行步驟\n"
            "- Step 1: ssh{host} echo '=== LOAD ===' -> FAILED: Unsupported scheme\n"
        )

        card = format_runbook_review_card(incident, "ff5eff01-7243-44bf", content)

        assert "<b>RUNBOOK REVIEW｜待審核</b>" in card
        assert "<code>INC-20260506-E54736</code>" in card
        assert "🧾 <b>內容摘要</b>" in card
        assert "placeholder 或不支援的執行步驟" in card
        assert "## 症狀描述" not in card
        assert "ssh{host}" not in card


# =============================================================================
# TestAutoRepairService — fire-and-forget 與 GC 防洩漏
# =============================================================================

class TestAutoRepairService:
    """驗證 auto_repair_service.py 的 fire-and-forget 模式與 GC 防洩漏"""

    def test_fire_and_forget_pattern(self):
        """auto_repair_service.py 使用 asyncio.create_task() 實現 fire-and-forget"""
        source = _AUTO_REPAIR.read_text()
        assert "create_task" in source

    def test_pending_tasks_gc_prevention(self):
        """auto_repair_service.py 持有 _pending_tasks 防止 Task 被 GC 回收"""
        source = _AUTO_REPAIR.read_text()
        assert "_pending_tasks" in source

    def test_anti_pattern_gate_in_auto_repair(self):
        """auto_repair_service.py 有 check_anti_pattern 攔截呼叫（AntiPattern Gate）"""
        source = _AUTO_REPAIR.read_text()
        assert "check_anti_pattern" in source


# =============================================================================
# TestKnowledgeModels — 知識庫模型結構
# =============================================================================

class TestKnowledgeModels:
    """驗證 models/knowledge.py 的 EntryType 與 KnowledgeEntry 欄位"""

    def test_auto_runbook_entrytype_value(self):
        """EntryType 有 AUTO_RUNBOOK = 'auto_runbook'"""
        source = _KNOWLEDGE.read_text()
        assert 'AUTO_RUNBOOK' in source
        assert '"auto_runbook"' in source or "'auto_runbook'" in source

    def test_anti_pattern_entrytype_value(self):
        """EntryType 有 ANTI_PATTERN = 'anti_pattern'"""
        source = _KNOWLEDGE.read_text()
        assert 'ANTI_PATTERN' in source
        assert '"anti_pattern"' in source or "'anti_pattern'" in source

    def test_knowledge_entry_has_symptoms_hash(self):
        """KnowledgeEntry 有 symptoms_hash 欄位（AntiPattern 閉環用）"""
        source = _KNOWLEDGE.read_text()
        assert "symptoms_hash" in source


# =============================================================================
# TestPlaybookModel — 症狀 hash 計算
# =============================================================================

class TestPlaybookModel:
    """驗證 models/playbook.py 的 compute_hash 方法"""

    def test_symptom_hash_compute_method(self):
        """SymptomPattern 有 compute_hash() 方法"""
        source = _PLAYBOOK.read_text()
        assert "compute_hash" in source


# =============================================================================
# TestKnowledgeService — check_anti_pattern 查詢
# =============================================================================

class TestKnowledgeService:
    """驗證 knowledge_service.py 的 check_anti_pattern 方法"""

    def test_check_anti_pattern_in_knowledge_service(self):
        """knowledge_service.py 有 check_anti_pattern 方法"""
        source = _KNOWLEDGE_SVC.read_text()
        assert "check_anti_pattern" in source
