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 TestCheckApiCall:
    def test_safe_api_call(self, mock_api, guard):
        mock_api.post("/v2/check/api-call").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        result = guard.check_api_call("https://api.example.com/health", "GET")
        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/api-call").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        guard.check_api_call("https://api.example.com", "POST", headers={"X-Key": "abc"}, body="data")
        import json
        body = json.loads(route.calls[0].request.content)
        assert body["url"] == "https://api.example.com"
        assert body["method"] == "POST"
        assert body["headers"] == {"X-Key": "abc"}
        assert body["body"] == "data"

    def test_default_method(self, mock_api, guard):
        route = mock_api.post("/v2/check/api-call").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        guard.check_api_call("https://api.example.com/health")
        import json
        body = json.loads(route.calls[0].request.content)
        assert body["method"] == "GET"


class TestCheckQuery:
    def test_safe_query(self, mock_api, guard):
        mock_api.post("/v2/check/query").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        result = guard.check_query("SELECT * FROM users WHERE id = 1")
        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/query").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        guard.check_query("DROP TABLE users")
        import json
        body = json.loads(route.calls[0].request.content)
        assert body == {"query": "DROP TABLE users"}


class TestCheckCode:
    def test_safe_code(self, mock_api, guard):
        mock_api.post("/v2/check/code").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        result = guard.check_code("print('hello')")
        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/code").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        guard.check_code("print('hi')", language="python")
        import json
        body = json.loads(route.calls[0].request.content)
        assert body == {"code": "print('hi')", "language": "python"}

    def test_without_language(self, mock_api, guard):
        route = mock_api.post("/v2/check/code").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        guard.check_code("console.log('hi')")
        import json
        body = json.loads(route.calls[0].request.content)
        assert body == {"code": "console.log('hi')"}


class TestCheckMessage:
    def test_safe_message(self, mock_api, guard):
        mock_api.post("/v2/check/message").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        result = guard.check_message("notification: done")
        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/message").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        guard.check_message("hello", channel="email", to="user@example.com", subject="Test")
        import json
        body = json.loads(route.calls[0].request.content)
        assert body == {"body": "hello", "channel": "email", "to": "user@example.com", "subject": "Test"}

    def test_without_optional_params(self, mock_api, guard):
        route = mock_api.post("/v2/check/message").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        guard.check_message("test notification")
        import json
        body = json.loads(route.calls[0].request.content)
        assert body == {"body": "test notification"}


class TestCheckTransaction:
    def test_safe_transaction(self, mock_api, guard):
        mock_api.post("/v2/check/transaction").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        result = guard.check_transaction("check balance")
        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/transaction").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        guard.check_transaction("buy stock", amount=500.0, currency="USD", recipient="broker", type="trade")
        import json
        body = json.loads(route.calls[0].request.content)
        assert body == {"description": "buy stock", "amount": 500.0, "currency": "USD", "recipient": "broker", "type": "trade"}

    def test_without_optional_params(self, mock_api, guard):
        route = mock_api.post("/v2/check/transaction").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        guard.check_transaction("view receipt")
        import json
        body = json.loads(route.calls[0].request.content)
        assert body == {"description": "view receipt"}


class TestCheckAuth:
    def test_safe_auth(self, mock_api, guard):
        mock_api.post("/v2/check/auth").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        result = guard.check_auth("login")
        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/auth").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        guard.check_auth("grant permission", scope="admin:write", role="admin", target="user@example.com")
        import json
        body = json.loads(route.calls[0].request.content)
        assert body == {"action": "grant permission", "scope": "admin:write", "role": "admin", "target": "user@example.com"}

    def test_without_optional_params(self, mock_api, guard):
        route = mock_api.post("/v2/check/auth").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        guard.check_auth("logout")
        import json
        body = json.loads(route.calls[0].request.content)
        assert body == {"action": "logout"}


class TestCheckGit:
    def test_safe_git(self, mock_api, guard):
        mock_api.post("/v2/check/git").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        result = guard.check_git("git status")
        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/git").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        guard.check_git("git push --force", branch="main", remote="origin", files=[".github/workflows/ci.yml"])
        import json
        body = json.loads(route.calls[0].request.content)
        assert body == {
            "command": "git push --force",
            "branch": "main",
            "remote": "origin",
            "files": [".github/workflows/ci.yml"],
        }

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


class TestCheckUiAction:
    def test_safe_ui_action(self, mock_api, guard):
        mock_api.post("/v2/check/ui-action").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        result = guard.check_ui_action("navigate to homepage")
        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/ui-action").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        guard.check_ui_action("click delete", element="account-btn", url="https://app.com", application="chrome")
        import json
        body = json.loads(route.calls[0].request.content)
        assert body == {
            "action": "click delete",
            "element": "account-btn",
            "url": "https://app.com",
            "application": "chrome",
        }

    def test_without_optional_params(self, mock_api, guard):
        route = mock_api.post("/v2/check/ui-action").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        guard.check_ui_action("scroll down")
        import json
        body = json.loads(route.calls[0].request.content)
        assert body == {"action": "scroll down"}


class TestCheckInfra:
    def test_safe_infra(self, mock_api, guard):
        mock_api.post("/v2/check/infra").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        result = guard.check_infra("terraform plan")
        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/infra").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        guard.check_infra("terraform destroy", provider="aws", environment="production", resource="vpc")
        import json
        body = json.loads(route.calls[0].request.content)
        assert body == {
            "action": "terraform destroy",
            "provider": "aws",
            "environment": "production",
            "resource": "vpc",
        }

    def test_without_optional_params(self, mock_api, guard):
        route = mock_api.post("/v2/check/infra").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        guard.check_infra("kubectl get pods")
        import json
        body = json.loads(route.calls[0].request.content)
        assert body == {"action": "kubectl get pods"}


class TestCheckAgentComm:
    def test_safe_agent_comm(self, mock_api, guard):
        mock_api.post("/v2/check/agent-comm").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        result = guard.check_agent_comm("list active agents")
        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/agent-comm").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        guard.check_agent_comm("delegate task", agent_id="a1", target_agent="a2", tool="file_write")
        import json
        body = json.loads(route.calls[0].request.content)
        assert body == {"action": "delegate task", "agent_id": "a1", "target_agent": "a2", "tool": "file_write"}

    def test_without_optional_params(self, mock_api, guard):
        route = mock_api.post("/v2/check/agent-comm").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        guard.check_agent_comm("list agents")
        import json
        body = json.loads(route.calls[0].request.content)
        assert body == {"action": "list agents"}


class TestCheckDataPipeline:
    def test_safe_data_pipeline(self, mock_api, guard):
        mock_api.post("/v2/check/data-pipeline").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        result = guard.check_data_pipeline("describe model version")
        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/data-pipeline").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        guard.check_data_pipeline("deploy model", operation="deploy", dataset="training", model="gpt-4")
        import json
        body = json.loads(route.calls[0].request.content)
        assert body == {"action": "deploy model", "operation": "deploy", "dataset": "training", "model": "gpt-4"}

    def test_without_optional_params(self, mock_api, guard):
        route = mock_api.post("/v2/check/data-pipeline").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        guard.check_data_pipeline("describe model")
        import json
        body = json.loads(route.calls[0].request.content)
        assert body == {"action": "describe model"}


class TestCheckDocument:
    def test_safe_document(self, mock_api, guard):
        mock_api.post("/v2/check/document").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        result = guard.check_document("read document")
        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/document").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        guard.check_document("edit contract", operation="modify", content="legal terms", recipient="lawyer@firm.com")
        import json
        body = json.loads(route.calls[0].request.content)
        assert body == {"action": "edit contract", "operation": "modify", "content": "legal terms", "recipient": "lawyer@firm.com"}

    def test_without_optional_params(self, mock_api, guard):
        route = mock_api.post("/v2/check/document").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        guard.check_document("preview PDF")
        import json
        body = json.loads(route.calls[0].request.content)
        assert body == {"action": "preview PDF"}


class TestCheckIot:
    def test_safe_iot(self, mock_api, guard):
        mock_api.post("/v2/check/iot").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        result = guard.check_iot("read temperature sensor")
        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/iot").mock(
            return_value=httpx.Response(200, json=SAFE_RESPONSE)
        )
        guard.check_iot("unlock door", device_type="lock", device_id="lock-001", resource="front-entrance")
        import json
        body = json.loads(route.calls[0].request.content)
        assert body == {"command": "unlock door", "device_type": "lock", "device_id": "lock-001", "resource": "front-entrance"}

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


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
