"""
审批策略测试。
"""

from __future__ import annotations

import pytest

from cmdc.approval import (
    ApprovalPolicy,
    AutoApprove,
    AutoReject,
    CallableApproval,
    PromptApproval,
    make_policy,
)
from cmdc.types import Event


def make_approval_event(tool_name: str = "Shell") -> Event:
    return Event(
        type="approval_required",
        data={
            "approvalId": "apr_001",
            "toolName": tool_name,
            "args": {"command": "ls"},
            "hint": "测试提示",
            "requestedAt": None,
        },
    )


class TestAutoApprove:
    def test_always_true(self) -> None:
        policy = AutoApprove()
        event = make_approval_event()
        assert policy.decide(event) is True

    def test_multiple_events(self) -> None:
        policy = AutoApprove()
        for tool in ["Shell", "WriteFile", "EditFile"]:
            assert policy.decide(make_approval_event(tool)) is True


class TestAutoReject:
    def test_always_false(self) -> None:
        policy = AutoReject()
        event = make_approval_event()
        assert policy.decide(event) is False


class TestCallableApproval:
    def test_approve_non_shell(self) -> None:
        policy = CallableApproval(lambda e: e.data.get("toolName") != "Shell")
        assert policy.decide(make_approval_event("Shell")) is False
        assert policy.decide(make_approval_event("ReadFile")) is True

    def test_approve_based_on_args(self) -> None:
        def safe_check(event: Event) -> bool:
            cmd = event.data.get("args", {}).get("command", "")
            return "rm" not in cmd

        policy = CallableApproval(safe_check)
        safe_event = Event(
            type="approval_required",
            data={"toolName": "Shell", "args": {"command": "ls -la"}, "approvalId": "apr_001"},
        )
        dangerous_event = Event(
            type="approval_required",
            data={"toolName": "Shell", "args": {"command": "rm -rf /"}, "approvalId": "apr_002"},
        )

        assert policy.decide(safe_event) is True
        assert policy.decide(dangerous_event) is False


class TestMakePolicy:
    def test_auto_approve_string(self) -> None:
        policy = make_policy("auto_approve")
        assert isinstance(policy, AutoApprove)

    def test_auto_reject_string(self) -> None:
        policy = make_policy("auto_reject")
        assert isinstance(policy, AutoReject)

    def test_prompt_string(self) -> None:
        policy = make_policy("prompt")
        assert isinstance(policy, PromptApproval)

    def test_unknown_string_raises(self) -> None:
        with pytest.raises(ValueError, match="未知审批策略"):
            make_policy("invalid_policy")

    def test_policy_instance_passthrough(self) -> None:
        original = AutoApprove()
        result = make_policy(original)
        assert result is original

    def test_callable_wrapped(self) -> None:
        fn = lambda e: True
        policy = make_policy(fn)
        assert isinstance(policy, CallableApproval)
        assert policy.decide(make_approval_event()) is True

    def test_invalid_type_raises(self) -> None:
        with pytest.raises(TypeError):
            make_policy(42)  # type: ignore
