from __future__ import annotations

import pytest
import respx
import httpx

from surfinguard import Guard, CheckResult, Policy, RiskLevel
from surfinguard.exceptions import NotAllowedError, AuthenticationError, RateLimitError

from conftest import (
    SAFE_RESPONSE,
    CAUTION_RESPONSE,
    DANGER_RESPONSE,
    HEALTH_RESPONSE,
)


class TestGuardInit:
    def test_local_only_raises(self):
        with pytest.raises(NotImplementedError, match="Local-only mode"):
            Guard(api_key="sg_test_0123456789abcdef0123456789abcdef", local_only=True)

    def test_context_manager(self, mock_api):
        with Guard(
            api_key="sg_test_0123456789abcdef0123456789abcdef",
            base_url="https://test.surfinguard.com",
        ) as g:
            assert g is not None


class TestCheckUrl:
    def test_safe_url(self, mock_api, guard):
        mock_api.post("/v2/check/url").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        result = guard.check_url("https://google.com")
        assert isinstance(result, CheckResult)
        assert result.allow is True
        assert result.level == RiskLevel.SAFE
        assert result.score == 0

    def test_sends_correct_payload(self, mock_api, guard):
        route = mock_api.post("/v2/check/url").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        guard.check_url("https://example.com")
        assert route.called
        request = route.calls[0].request
        import json
        body = json.loads(request.content)
        assert body == {"url": "https://example.com"}


class TestCheckCommand:
    def test_safe_command(self, mock_api, guard):
        mock_api.post("/v2/check/command").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        result = guard.check_command("ls -la")
        assert result.allow is True
        assert result.level == RiskLevel.SAFE

    def test_sends_correct_payload(self, mock_api, guard):
        route = mock_api.post("/v2/check/command").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        guard.check_command("git status")
        import json
        body = json.loads(route.calls[0].request.content)
        assert body == {"command": "git status"}


class TestCheckText:
    def test_safe_text(self, mock_api, guard):
        mock_api.post("/v2/check/text").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        result = guard.check_text("Write a sorting algorithm")
        assert result.allow is True

    def test_sends_correct_payload(self, mock_api, guard):
        route = mock_api.post("/v2/check/text").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        guard.check_text("Hello world")
        import json
        body = json.loads(route.calls[0].request.content)
        assert body == {"text": "Hello world"}


class TestCheckFileRead:
    def test_safe_file_read(self, mock_api, guard):
        mock_api.post("/v2/check/file").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        result = guard.check_file_read("src/index.ts")
        assert result.allow is True

    def test_sends_correct_payload(self, mock_api, guard):
        route = mock_api.post("/v2/check/file").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        guard.check_file_read("/etc/passwd")
        import json
        body = json.loads(route.calls[0].request.content)
        assert body == {"path": "/etc/passwd", "operation": "read"}


class TestCheckFileWrite:
    def test_safe_file_write(self, mock_api, guard):
        mock_api.post("/v2/check/file").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        result = guard.check_file_write("src/helper.ts")
        assert result.allow is True

    def test_file_write_with_content(self, mock_api, guard):
        route = mock_api.post("/v2/check/file").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        guard.check_file_write("/tmp/test.txt", content="hello")
        import json
        body = json.loads(route.calls[0].request.content)
        assert body == {"path": "/tmp/test.txt", "operation": "write", "content": "hello"}


class TestUniversalCheck:
    def test_check_with_metadata(self, mock_api, guard):
        route = mock_api.post("/v2/check").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        guard.check("command", "ls", source="test")
        import json
        body = json.loads(route.calls[0].request.content)
        assert body["type"] == "command"
        assert body["value"] == "ls"
        assert body["metadata"] == {"source": "test"}


class TestHealth:
    def test_health(self, mock_api, guard):
        mock_api.get("/v2/health").mock(
            return_value=httpx.Response(200, json=HEALTH_RESPONSE)
        )
        result = guard.health()
        assert result.ok is True
        assert result.version == "2.2.0"
        assert len(result.analyzers) == 5


class TestPolicyEnforcement:
    def test_moderate_allows_safe(self, mock_api, guard):
        mock_api.post("/v2/check/url").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        result = guard.check_url("https://google.com")
        assert result.allow is True

    def test_moderate_allows_caution(self, mock_api, guard):
        mock_api.post("/v2/check/url").mock(
            return_value=httpx.Response(200, json=CAUTION_RESPONSE)
        )
        result = guard.check_url("https://suspicious.example.com")
        assert result.level == RiskLevel.CAUTION

    def test_moderate_blocks_danger(self, mock_api, guard):
        mock_api.post("/v2/check/command").mock(
            return_value=httpx.Response(200, json=DANGER_RESPONSE)
        )
        with pytest.raises(NotAllowedError) as exc_info:
            guard.check_command("rm -rf /")
        assert exc_info.value.result.score == 9

    def test_strict_allows_safe(self, mock_api, strict_guard):
        mock_api.post("/v2/check/url").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        result = strict_guard.check_url("https://google.com")
        assert result.allow is True

    def test_strict_blocks_caution(self, mock_api, strict_guard):
        mock_api.post("/v2/check/url").mock(
            return_value=httpx.Response(200, json=CAUTION_RESPONSE)
        )
        with pytest.raises(NotAllowedError):
            strict_guard.check_url("https://suspicious.example.com")

    def test_strict_blocks_danger(self, mock_api, strict_guard):
        mock_api.post("/v2/check/command").mock(
            return_value=httpx.Response(200, json=DANGER_RESPONSE)
        )
        with pytest.raises(NotAllowedError):
            strict_guard.check_command("rm -rf /")

    def test_permissive_allows_safe(self, mock_api, permissive_guard):
        mock_api.post("/v2/check/url").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        result = permissive_guard.check_url("https://google.com")
        assert result.allow is True

    def test_permissive_allows_caution(self, mock_api, permissive_guard):
        mock_api.post("/v2/check/url").mock(
            return_value=httpx.Response(200, json=CAUTION_RESPONSE)
        )
        result = permissive_guard.check_url("https://suspicious.example.com")
        assert result.level == RiskLevel.CAUTION

    def test_permissive_allows_danger(self, mock_api, permissive_guard):
        mock_api.post("/v2/check/command").mock(
            return_value=httpx.Response(200, json=DANGER_RESPONSE)
        )
        result = permissive_guard.check_command("rm -rf /")
        assert result.level == RiskLevel.DANGER
