from __future__ import annotations

import pytest
import respx
import httpx

from surfinguard import Guard, Policy
from surfinguard.exceptions import NotAllowedError

from conftest import SAFE_RESPONSE, DANGER_RESPONSE


class TestProtectDecorator:
    @pytest.fixture()
    def guard(self):
        return Guard(
            api_key="sg_test_0123456789abcdef0123456789abcdef",
            base_url="https://test.surfinguard.com",
            policy=Policy.MODERATE,
        )

    def test_allows_safe_command(self, mock_api, guard):
        mock_api.post("/v2/check").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )

        @guard.protect("command")
        def run_command(cmd: str) -> str:
            return f"executed: {cmd}"

        result = run_command("ls -la")
        assert result == "executed: ls -la"

    def test_blocks_dangerous_command(self, mock_api, guard):
        mock_api.post("/v2/check").mock(
            return_value=httpx.Response(200, json=DANGER_RESPONSE)
        )

        @guard.protect("command")
        def run_command(cmd: str) -> str:
            return f"executed: {cmd}"

        with pytest.raises(NotAllowedError):
            run_command("rm -rf /")

    def test_checks_first_positional_arg(self, mock_api, guard):
        route = mock_api.post("/v2/check").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )

        @guard.protect("url")
        def open_url(url: str, timeout: int = 30) -> str:
            return url

        open_url("https://example.com", timeout=10)
        import json
        body = json.loads(route.calls[0].request.content)
        assert body["value"] == "https://example.com"
        assert body["type"] == "url"

    def test_checks_first_keyword_arg(self, mock_api, guard):
        route = mock_api.post("/v2/check").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )

        @guard.protect("command")
        def run_cmd(*, cmd: str) -> str:
            return cmd

        run_cmd(cmd="echo hello")
        import json
        body = json.loads(route.calls[0].request.content)
        assert body["value"] == "echo hello"

    def test_preserves_function_name(self, mock_api, guard):
        @guard.protect("command")
        def my_function(cmd: str) -> str:
            return cmd

        assert my_function.__name__ == "my_function"

    def test_default_action_type_is_command(self, mock_api, guard):
        route = mock_api.post("/v2/check").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )

        @guard.protect()
        def execute(cmd: str) -> str:
            return cmd

        execute("ls")
        import json
        body = json.loads(route.calls[0].request.content)
        assert body["type"] == "command"

    def test_no_args_skips_check(self, mock_api, guard):
        route = mock_api.post("/v2/check").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )

        @guard.protect("command")
        def no_args() -> str:
            return "done"

        result = no_args()
        assert result == "done"
        assert not route.called

    def test_return_value_preserved(self, mock_api, guard):
        mock_api.post("/v2/check").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )

        @guard.protect("command")
        def compute(cmd: str) -> dict:
            return {"output": cmd, "status": "ok"}

        result = compute("echo test")
        assert result == {"output": "echo test", "status": "ok"}
