"""
Auto Repair Service Tests - #8 自動升級決策
==========================================
測試自動修復服務層功能

版本: v1.0
建立: 2026-03-26 (台北時區)
建立者: Claude Code (#8 自動升級決策)
"""

import pytest

from src.models.incident import Incident, IncidentStatus, Severity, Signal
from src.models.playbook import (
    ActionType,
    Playbook,
    PlaybookStatus,
    RepairStep,
    RiskLevel,
    SymptomPattern,
)
from src.services.auto_repair_service import AutoRepairService
from src.utils.timezone import now_taipei


class MockPlaybookService:
    """Mock playbook service for testing"""

    def __init__(self):
        self._playbooks: dict[str, Playbook] = {}
        self._recommendations: list = []

    def add_playbook(self, playbook: Playbook):
        self._playbooks[playbook.playbook_id] = playbook

    def set_recommendations(self, recommendations: list):
        self._recommendations = recommendations

    async def get_recommendations(self, symptoms, top_k=3):
        return self._recommendations

    async def get_by_id(self, playbook_id: str):
        return self._playbooks.get(playbook_id)

    async def record_execution(self, playbook_id: str, success: bool):
        playbook = self._playbooks.get(playbook_id)
        if playbook:
            if success:
                playbook.success_count += 1
            else:
                playbook.failure_count += 1
        return playbook is not None


def create_test_incident(
    incident_id: str = "INC-TEST-001",
    severity: Severity = Severity.P2,
) -> Incident:
    """Create a test incident"""
    now = now_taipei()
    return Incident(
        incident_id=incident_id,
        status=IncidentStatus.INVESTIGATING,
        severity=severity,
        affected_services=["test-service"],
        signals=[
            Signal(
                alert_name="HighCPU",
                severity=severity,
                source="prometheus",
                fired_at=now,
                labels={"namespace": "prod"},
            ),
        ],
    )


def create_high_quality_playbook(
    playbook_id: str = "PB-TEST-001",
    risk_level: RiskLevel = RiskLevel.MEDIUM,
) -> Playbook:
    """Create a high quality playbook (success_rate >= 95%, count >= 10)"""
    return Playbook(
        playbook_id=playbook_id,
        name="HighCPU - test-service 修復劇本",
        description="High quality playbook for auto repair",
        status=PlaybookStatus.APPROVED,
        symptom_pattern=SymptomPattern(
            alert_names=["HighCPU"],
            affected_services=["test-service"],
            severity_range=["P2"],
        ),
        repair_steps=[
            RepairStep(
                step_number=1,
                action_type=ActionType.KUBECTL,
                command="kubectl rollout restart deployment/{target}",
                risk_level=risk_level,
            ),
        ],
        success_count=20,  # >= 10
        failure_count=1,  # success_rate = 95.2%
        ai_confidence=0.9,
    )


class MockPlaybookRecommendation:
    """Mock recommendation for testing"""

    def __init__(self, playbook: Playbook, similarity_score: float):
        self.playbook = playbook
        self.similarity_score = similarity_score


class TestAutoRepairService:
    """Auto Repair Service unit tests"""

    @pytest.fixture
    def mock_playbook_service(self):
        return MockPlaybookService()

    @pytest.fixture
    def service(self, mock_playbook_service):
        return AutoRepairService(playbook_service=mock_playbook_service)

    @pytest.mark.asyncio
    async def test_evaluate_blocks_p1_severity(self, service):
        """Test that P1 severity incidents are blocked"""
        incident = create_test_incident(severity=Severity.P1)
        decision = await service.evaluate_auto_repair(incident)

        assert decision.can_auto_repair is False
        assert decision.blocked_by == "HIGH_SEVERITY"

    @pytest.mark.asyncio
    async def test_evaluate_blocks_p0_severity(self, service):
        """Test that P0 severity incidents are blocked"""
        incident = create_test_incident(severity=Severity.P0)
        decision = await service.evaluate_auto_repair(incident)

        assert decision.can_auto_repair is False
        assert decision.blocked_by == "HIGH_SEVERITY"

    @pytest.mark.asyncio
    async def test_evaluate_no_playbook_match(self, service, mock_playbook_service):
        """Test when no playbook matches"""
        mock_playbook_service.set_recommendations([])
        incident = create_test_incident(severity=Severity.P2)

        decision = await service.evaluate_auto_repair(incident)

        assert decision.can_auto_repair is False
        assert decision.blocked_by == "NO_MATCH"

    @pytest.mark.asyncio
    async def test_evaluate_low_similarity(self, service, mock_playbook_service):
        """Test when similarity is too low"""
        playbook = create_high_quality_playbook()
        mock_playbook_service.add_playbook(playbook)
        mock_playbook_service.set_recommendations([
            MockPlaybookRecommendation(playbook, similarity_score=0.5)  # Below 0.7
        ])

        incident = create_test_incident(severity=Severity.P2)
        decision = await service.evaluate_auto_repair(incident)

        assert decision.can_auto_repair is False
        assert decision.blocked_by == "LOW_SIMILARITY"

    @pytest.mark.asyncio
    async def test_evaluate_not_high_quality(self, service, mock_playbook_service):
        """Test when playbook is not high quality"""
        playbook = Playbook(
            playbook_id="PB-LOW-QUALITY",
            name="Low quality playbook",
            description="Not enough executions",
            status=PlaybookStatus.APPROVED,
            symptom_pattern=SymptomPattern(
                alert_names=["HighCPU"],
                affected_services=["test-service"],
            ),
            repair_steps=[],
            success_count=5,  # < 10
            failure_count=0,
        )
        mock_playbook_service.add_playbook(playbook)
        mock_playbook_service.set_recommendations([
            MockPlaybookRecommendation(playbook, similarity_score=0.9)
        ])

        incident = create_test_incident(severity=Severity.P2)
        decision = await service.evaluate_auto_repair(incident)

        assert decision.can_auto_repair is False
        assert decision.blocked_by == "NOT_HIGH_QUALITY"

    @pytest.mark.asyncio
    async def test_evaluate_high_risk_blocked(self, service, mock_playbook_service):
        """Test when playbook contains HIGH risk actions"""
        playbook = create_high_quality_playbook(risk_level=RiskLevel.HIGH)
        mock_playbook_service.add_playbook(playbook)
        mock_playbook_service.set_recommendations([
            MockPlaybookRecommendation(playbook, similarity_score=0.9)
        ])

        incident = create_test_incident(severity=Severity.P2)
        decision = await service.evaluate_auto_repair(incident)

        assert decision.can_auto_repair is False
        assert decision.blocked_by == "HIGH_RISK"

    @pytest.mark.asyncio
    async def test_evaluate_critical_risk_blocked(self, service, mock_playbook_service):
        """Test when playbook contains CRITICAL risk actions"""
        playbook = create_high_quality_playbook(risk_level=RiskLevel.CRITICAL)
        mock_playbook_service.add_playbook(playbook)
        mock_playbook_service.set_recommendations([
            MockPlaybookRecommendation(playbook, similarity_score=0.9)
        ])

        incident = create_test_incident(severity=Severity.P2)
        decision = await service.evaluate_auto_repair(incident)

        assert decision.can_auto_repair is False
        assert decision.blocked_by == "HIGH_RISK"

    @pytest.mark.asyncio
    async def test_evaluate_success(self, service, mock_playbook_service):
        """Test successful auto repair evaluation"""
        playbook = create_high_quality_playbook(risk_level=RiskLevel.MEDIUM)
        mock_playbook_service.add_playbook(playbook)
        mock_playbook_service.set_recommendations([
            MockPlaybookRecommendation(playbook, similarity_score=0.85)
        ])

        incident = create_test_incident(severity=Severity.P2)
        decision = await service.evaluate_auto_repair(incident)

        assert decision.can_auto_repair is True
        assert decision.playbook is not None
        assert decision.playbook.playbook_id == playbook.playbook_id
        assert decision.blocked_by is None

    @pytest.mark.asyncio
    async def test_evaluate_low_risk_allowed(self, service, mock_playbook_service):
        """Test that LOW risk actions are allowed"""
        playbook = create_high_quality_playbook(risk_level=RiskLevel.LOW)
        mock_playbook_service.add_playbook(playbook)
        mock_playbook_service.set_recommendations([
            MockPlaybookRecommendation(playbook, similarity_score=0.85)
        ])

        incident = create_test_incident(severity=Severity.P2)
        decision = await service.evaluate_auto_repair(incident)

        assert decision.can_auto_repair is True
        assert decision.risk_level == RiskLevel.LOW

    @pytest.mark.asyncio
    async def test_is_high_quality_calculation(self):
        """Test is_high_quality property"""
        # High quality: APPROVED + 95%+ success rate + 10+ successes
        playbook = create_high_quality_playbook()
        assert playbook.is_high_quality is True
        assert playbook.success_rate >= 0.95
        assert playbook.success_count >= 10

    @pytest.mark.asyncio
    async def test_not_high_quality_low_success_rate(self):
        """Test playbook with low success rate is not high quality"""
        playbook = Playbook(
            playbook_id="PB-LOW-RATE",
            name="Low success rate",
            description="Too many failures",
            status=PlaybookStatus.APPROVED,
            symptom_pattern=SymptomPattern(
                alert_names=["Test"],
                affected_services=["test"],
            ),
            repair_steps=[],
            success_count=15,
            failure_count=5,  # 75% success rate
        )
        assert playbook.is_high_quality is False
        assert playbook.success_rate < 0.95
