from types import SimpleNamespace
from unittest.mock import AsyncMock

import pytest

from src.services.approval_execution import ApprovalExecutionService


@pytest.mark.asyncio
async def test_playbook_authoring_record_only_approval_does_not_resolve_incident(monkeypatch):
    approval = SimpleNamespace(
        id="approval-playbook-authoring-1",
        action="PLAYBOOK_AUTHORING_RECORD_ONLY: promote diagnostic playbook",
        incident_id="INC-TEST-PLAYBOOK",
        metadata={
            "approval_kind": "adr100_playbook_authoring",
            "execution_kind": "playbook_authoring_record_only",
            "execution_authorized": False,
            "work_item_id": "verification:INC-TEST-PLAYBOOK:are-1",
            "playbook_id": "PB-1",
        },
    )
    incident_service = SimpleNamespace(resolve_incident=AsyncMock())
    update_execution_status = AsyncMock()
    timeline_add_event = AsyncMock()
    alert_completed = AsyncMock(return_value=None)

    monkeypatch.setattr(
        "src.services.approval_execution.get_approval_service",
        lambda: SimpleNamespace(update_execution_status=update_execution_status),
    )
    monkeypatch.setattr(
        "src.services.approval_execution.get_timeline_service",
        lambda: SimpleNamespace(add_event=timeline_add_event),
    )
    monkeypatch.setattr(
        "src.services.approval_execution.parse_operation_from_action",
        lambda _: SimpleNamespace(
            operation_type=None, resource_name=None, namespace=None
        ),
    )
    monkeypatch.setattr(
        "src.services.incident_service.get_incident_service",
        lambda: incident_service,
    )
    monkeypatch.setattr(
        "src.services.approval_execution.ApprovalExecutionService._push_execution_result_to_alert",
        AsyncMock(return_value=None),
    )
    monkeypatch.setattr(
        "src.services.approval_execution.ApprovalExecutionService._log_aol_completed",
        AsyncMock(return_value=None),
    )
    monkeypatch.setattr(
        "src.services.approval_execution.ApprovalExecutionService._log_alert_execution_started",
        AsyncMock(return_value=None),
    )
    monkeypatch.setattr(
        "src.services.approval_execution.ApprovalExecutionService._log_alert_execution_completed",
        alert_completed,
    )

    result = await ApprovalExecutionService().execute_approved_action(approval)

    assert result is True
    update_execution_status.assert_awaited_once_with(
        approval.id,
        success=True,
        execution_kind="playbook_authoring_record_only",
        repair_executed=False,
        repair_attempted=False,
    )
    assert "no runtime repair" in timeline_add_event.await_args.kwargs["title"]
    assert alert_completed.await_args.kwargs["execution_kind"] == (
        "playbook_authoring_record_only"
    )
    incident_service.resolve_incident.assert_not_awaited()


@pytest.mark.asyncio
async def test_no_action_execution_resolves_incident_once(monkeypatch):
    # Arrange
    approval = SimpleNamespace(
        id="approval-noaction-1",
        action="NO_ACTION: 先做觀察",
        incident_id="INC-TEST-001",
    )
    incident_service = SimpleNamespace(resolve_incident=AsyncMock())
    update_execution_status = AsyncMock()
    timeline_add_event = AsyncMock()
    alert_completed = AsyncMock(return_value=None)

    monkeypatch.setattr(
        "src.services.approval_execution.get_approval_service",
        lambda: SimpleNamespace(update_execution_status=update_execution_status),
    )
    monkeypatch.setattr(
        "src.services.approval_execution.get_timeline_service",
        lambda: SimpleNamespace(add_event=timeline_add_event),
    )
    monkeypatch.setattr(
        "src.services.approval_execution.parse_operation_from_action",
        lambda _: SimpleNamespace(
            operation_type=None, resource_name=None, namespace=None
        ),
    )
    monkeypatch.setattr(
        "src.services.incident_service.get_incident_service",
        lambda: incident_service,
    )
    monkeypatch.setattr(
        "src.services.approval_execution.ApprovalExecutionService._push_execution_result_to_alert",
        AsyncMock(return_value=None),
    )
    monkeypatch.setattr(
        "src.services.approval_execution.ApprovalExecutionService._log_aol_completed",
        AsyncMock(return_value=None),
    )
    monkeypatch.setattr(
        "src.services.approval_execution.ApprovalExecutionService._log_alert_execution_started",
        AsyncMock(return_value=None),
    )
    monkeypatch.setattr(
        "src.services.approval_execution.ApprovalExecutionService._log_alert_execution_completed",
        alert_completed,
    )

    # Act
    result = await ApprovalExecutionService().execute_approved_action(approval)

    # Assert
    assert result is True
    update_execution_status.assert_awaited_once_with(
        approval.id,
        success=True,
        execution_kind="no_action",
        repair_executed=False,
        repair_attempted=False,
    )
    assert "未執行修復" in timeline_add_event.await_args.kwargs["title"]
    assert alert_completed.await_args.kwargs["execution_kind"] == "no_action"
    assert alert_completed.await_args.kwargs["output"]["repair_executed"] is False
    incident_service.resolve_incident.assert_awaited_once_with("INC-TEST-001")


@pytest.mark.asyncio
async def test_no_action_execution_returns_true_when_resolve_raises(monkeypatch):
    """resolve_incident 拋錯時，NO_ACTION 仍須 return True。

    契約：NO_ACTION 是「純觀察類」成功完成（line 207-208 註解明說避免污染
    auto_execute KPI）。resolve 失敗只該 warning log，不該讓 result 退化成 False。
    """
    approval = SimpleNamespace(
        id="approval-noaction-2",
        action="NO_ACTION: 觀察",
        incident_id="INC-TEST-002",
    )
    incident_service = SimpleNamespace(
        resolve_incident=AsyncMock(side_effect=RuntimeError("redis down"))
    )
    update_execution_status = AsyncMock()

    monkeypatch.setattr(
        "src.services.approval_execution.get_approval_service",
        lambda: SimpleNamespace(update_execution_status=update_execution_status),
    )
    monkeypatch.setattr(
        "src.services.approval_execution.get_timeline_service",
        lambda: SimpleNamespace(add_event=AsyncMock()),
    )
    monkeypatch.setattr(
        "src.services.approval_execution.parse_operation_from_action",
        lambda _: SimpleNamespace(
            operation_type=None, resource_name=None, namespace=None
        ),
    )
    monkeypatch.setattr(
        "src.services.incident_service.get_incident_service",
        lambda: incident_service,
    )
    monkeypatch.setattr(
        "src.services.approval_execution.ApprovalExecutionService._push_execution_result_to_alert",
        AsyncMock(return_value=None),
    )
    monkeypatch.setattr(
        "src.services.approval_execution.ApprovalExecutionService._log_aol_completed",
        AsyncMock(return_value=None),
    )
    monkeypatch.setattr(
        "src.services.approval_execution.ApprovalExecutionService._log_alert_execution_started",
        AsyncMock(return_value=None),
    )
    monkeypatch.setattr(
        "src.services.approval_execution.ApprovalExecutionService._log_alert_execution_completed",
        AsyncMock(return_value=None),
    )

    result = await ApprovalExecutionService().execute_approved_action(approval)

    assert result is True
    update_execution_status.assert_awaited_once_with(
        approval.id,
        success=True,
        execution_kind="no_action",
        repair_executed=False,
        repair_attempted=False,
    )
    incident_service.resolve_incident.assert_awaited_once_with("INC-TEST-002")
