"""
AnomalyCounter 整合測試
=======================
ADR-037: 監控增強架構 - 異常頻率統計

🔴🔴 遵循「禁止 Mock 測試鐵律」- 使用真實 Redis

建立: 2026-03-29 (台北時區) Claude Code
"""

import pytest

from src.services.anomaly_counter import AnomalyCounter, AnomalyFrequency


class TestAnomalyCounterHashSignature:
    """測試異常簽名 Hash 生成"""

    def test_same_input_same_hash(self):
        """相同輸入應產生相同 hash"""
        sig1 = {"alert_name": "PodCrash", "service": "api"}
        sig2 = {"alert_name": "PodCrash", "service": "api"}
        assert AnomalyCounter.hash_signature(sig1) == AnomalyCounter.hash_signature(sig2)

    def test_different_input_different_hash(self):
        """不同輸入應產生不同 hash"""
        sig1 = {"alert_name": "PodCrash", "service": "api"}
        sig2 = {"alert_name": "PodCrash", "service": "web"}
        assert AnomalyCounter.hash_signature(sig1) != AnomalyCounter.hash_signature(sig2)

    def test_ignores_extra_fields(self):
        """應忽略非關鍵欄位 (如 timestamp)"""
        sig1 = {"alert_name": "PodCrash", "service": "api"}
        sig2 = {"alert_name": "PodCrash", "service": "api", "timestamp": "2026-01-01"}
        assert AnomalyCounter.hash_signature(sig1) == AnomalyCounter.hash_signature(sig2)

    def test_alertname_alias(self):
        """應支援 alertname (Prometheus 格式) 別名"""
        sig1 = {"alert_name": "PodCrash", "service": "api"}
        sig2 = {"alertname": "PodCrash", "service": "api"}
        assert AnomalyCounter.hash_signature(sig1) == AnomalyCounter.hash_signature(sig2)

    def test_job_alias(self):
        """應支援 job (Prometheus 格式) 別名"""
        sig1 = {"alert_name": "PodCrash", "service": "api"}
        sig2 = {"alert_name": "PodCrash", "job": "api"}
        assert AnomalyCounter.hash_signature(sig1) == AnomalyCounter.hash_signature(sig2)


class TestAnomalyFrequencyToDict:
    """測試 AnomalyFrequency.to_dict()"""

    def test_to_dict_returns_all_fields(self):
        """to_dict 應返回所有欄位"""
        from datetime import datetime

        freq = AnomalyFrequency(
            anomaly_key="abc123",
            count_1h=3,
            count_24h=10,
            count_7d=50,
            count_30d=200,
            first_seen=datetime(2026, 3, 1),
            last_seen=datetime(2026, 3, 29),
            auto_repair_count=5,
            permanent_fix_applied=False,
            escalation_level="ESCALATE",
        )

        d = freq.to_dict()

        assert d["anomaly_key"] == "abc123"
        assert d["count_1h"] == 3
        assert d["count_24h"] == 10
        assert d["count_7d"] == 50
        assert d["count_30d"] == 200
        assert d["auto_repair_count"] == 5
        assert d["permanent_fix_applied"] is False
        assert d["escalation_level"] == "ESCALATE"


class TestAnomalyCounterEscalationLevel:
    """測試升級等級判斷"""

    def test_no_escalation_below_threshold(self):
        """低於閾值不應升級"""
        counter = AnomalyCounter.__new__(AnomalyCounter)
        assert counter._get_escalation_level(2) is None

    def test_repeat_at_threshold(self):
        """達到 REPEAT 閾值 (3 次)"""
        counter = AnomalyCounter.__new__(AnomalyCounter)
        assert counter._get_escalation_level(3) == "REPEAT"

    def test_escalate_at_threshold(self):
        """達到 ESCALATE 閾值 (5 次)"""
        counter = AnomalyCounter.__new__(AnomalyCounter)
        assert counter._get_escalation_level(5) == "ESCALATE"

    def test_permanent_fix_at_threshold(self):
        """達到 PERMANENT_FIX 閾值 (10 次)"""
        counter = AnomalyCounter.__new__(AnomalyCounter)
        assert counter._get_escalation_level(10) == "PERMANENT_FIX"

    def test_highest_level_wins(self):
        """超過最高閾值應返回最高等級"""
        counter = AnomalyCounter.__new__(AnomalyCounter)
        assert counter._get_escalation_level(100) == "PERMANENT_FIX"


# =============================================================================
# 整合測試 (需要真實 Redis)
# =============================================================================


@pytest.mark.asyncio
@pytest.mark.integration
class TestAnomalyCounterIntegration:
    """
    整合測試 - 需要真實 Redis

    執行方式: pytest -m integration tests/test_anomaly_counter.py
    """

    @pytest.fixture
    async def counter(self):
        """取得 AnomalyCounter 實例"""
        from src.core.redis_client import get_redis

        redis = get_redis()
        counter = AnomalyCounter(redis)

        # 清理測試資料
        test_key = "test_anomaly_key"
        await redis.delete(f"anomaly:timeline:{test_key}")
        await redis.delete(f"anomaly:repair_count:{test_key}")
        await redis.delete(f"anomaly:permanent_fix:{test_key}")
        await redis.delete(f"anomaly:metadata:{test_key}")
        await redis.delete(f"anomaly:repair_history:{test_key}")

        return counter

    async def test_record_anomaly_returns_frequency(self, counter):
        """record_anomaly 應返回頻率統計"""
        signature = {
            "alert_name": "TestAlert",
            "service": "test-service",
            "namespace": "test",
        }

        freq = await counter.record_anomaly(signature)

        assert freq.count_1h >= 1
        assert freq.count_24h >= 1
        assert freq.anomaly_key is not None

    async def test_record_anomaly_increments_count(self, counter):
        """多次記錄應遞增計數"""
        signature = {
            "alert_name": "TestAlert",
            "service": "test-service",
            "namespace": "test",
        }

        freq1 = await counter.record_anomaly(signature)
        _ = await counter.record_anomaly(signature)
        freq3 = await counter.record_anomaly(signature)

        assert freq3.count_1h == freq1.count_1h + 2
        assert freq3.count_24h == freq1.count_24h + 2

    async def test_record_repair_attempt(self, counter):
        """記錄修復嘗試"""
        anomaly_key = "test_repair_key"

        await counter.record_repair_attempt(anomaly_key, "restart_pod", True)
        await counter.record_repair_attempt(anomaly_key, "restart_pod", False)

        stats = await counter.get_repair_success_rate(anomaly_key, "restart_pod")

        assert stats["total"] == 2
        assert stats["success"] == 1
        assert stats["success_rate"] == 0.5

    async def test_should_skip_action_low_success_rate(self, counter):
        """低成功率動作應跳過"""
        anomaly_key = "test_skip_key"

        # 模擬多次失敗
        await counter.record_repair_attempt(anomaly_key, "bad_action", False)
        await counter.record_repair_attempt(anomaly_key, "bad_action", False)
        await counter.record_repair_attempt(anomaly_key, "bad_action", False)

        should_skip = await counter.should_skip_action(anomaly_key, "bad_action")

        assert should_skip is True
