"""Tests for ARIAToolInterceptor."""

from __future__ import annotations

import concurrent.futures

import httpx
import pytest
from agent_os.integrations.base import ToolCallRequest

from aria_agentkit.policy.interceptor import ARIAToolInterceptor

_TOKEN = "test-token"
_PDP_URL = "http://localhost:8001/access/v1/evaluation"
_APP = "test-app"


class TestInterceptorAllow:
    def test_allow_returns_true(self, mock_pdp_allow: httpx.Client) -> None:
        interceptor = ARIAToolInterceptor(
            pdp_url=_PDP_URL,
            pdp_application=_APP,
            token=_TOKEN,
            http_client=mock_pdp_allow,
        )
        result = interceptor.intercept(
            ToolCallRequest(
                tool_name="web_search",
                arguments={"query": "hello"},
                call_id="c1",
                agent_id="a1",
            )
        )
        assert result.allowed is True
        assert result.audit_entry is not None
        assert result.audit_entry["decision"] is True


class TestInterceptorDeny:
    def test_deny_returns_false(self, mock_pdp_deny: httpx.Client) -> None:
        interceptor = ARIAToolInterceptor(
            pdp_url=_PDP_URL,
            pdp_application=_APP,
            token=_TOKEN,
            http_client=mock_pdp_deny,
        )
        result = interceptor.intercept(
            ToolCallRequest(
                tool_name="file_delete",
                arguments={"path": "/etc/passwd"},
                call_id="c2",
                agent_id="a1",
            )
        )
        assert result.allowed is False

    def test_deny_with_constraints_clamps_args(
        self, mock_pdp_deny_with_constraints: httpx.Client
    ) -> None:
        interceptor = ARIAToolInterceptor(
            pdp_url=_PDP_URL,
            pdp_application=_APP,
            token=_TOKEN,
            http_client=mock_pdp_deny_with_constraints,
        )
        result = interceptor.intercept(
            ToolCallRequest(
                tool_name="sql_query",
                arguments={"query": "SELECT *", "max_rows": 999},
                call_id="c3",
                agent_id="a1",
            )
        )
        assert result.allowed is False
        assert result.modified_arguments is not None
        assert result.modified_arguments["max_rows"] == 100
        assert result.reason == "max_rows exceeded"


class TestInterceptorFailClosed:
    def test_pdp_error_denies_when_fail_closed(self, mock_pdp_error: httpx.Client) -> None:
        interceptor = ARIAToolInterceptor(
            pdp_url=_PDP_URL,
            pdp_application=_APP,
            token=_TOKEN,
            http_client=mock_pdp_error,
            fail_closed=True,
        )
        result = interceptor.intercept(
            ToolCallRequest(tool_name="anything", arguments={}, agent_id="a1")
        )
        assert result.allowed is False
        assert "fail-closed" in (result.reason or "")

    def test_pdp_error_raises_when_not_fail_closed(self, mock_pdp_error: httpx.Client) -> None:
        interceptor = ARIAToolInterceptor(
            pdp_url=_PDP_URL,
            pdp_application=_APP,
            token=_TOKEN,
            http_client=mock_pdp_error,
            fail_closed=False,
        )
        with pytest.raises(httpx.HTTPStatusError):
            interceptor.intercept(
                ToolCallRequest(tool_name="anything", arguments={}, agent_id="a1")
            )


class TestInterceptorAuditEntry:
    def test_audit_entry_contains_authzen_request(self, mock_pdp_allow: httpx.Client) -> None:
        interceptor = ARIAToolInterceptor(
            pdp_url=_PDP_URL,
            pdp_application=_APP,
            token=_TOKEN,
            http_client=mock_pdp_allow,
        )
        result = interceptor.intercept(
            ToolCallRequest(
                tool_name="web_search",
                arguments={"q": "test"},
                call_id="c-audit",
                agent_id="agent-audit",
            )
        )
        entry = result.audit_entry
        assert entry is not None
        assert "authzen_request" in entry
        assert entry["authzen_request"]["action"]["name"] == "tool.invoke"
        assert "original_args_hash" in entry
        assert "evaluation_ms" in entry

    def test_audit_entry_authzen_request_matches_mapping(
        self, mock_pdp_allow: httpx.Client
    ) -> None:
        """The AuthZEN request in the audit entry must be the same one sent to the PDP."""
        interceptor = ARIAToolInterceptor(
            pdp_url=_PDP_URL,
            pdp_application=_APP,
            token=_TOKEN,
            http_client=mock_pdp_allow,
        )
        result = interceptor.intercept(
            ToolCallRequest(
                tool_name="sql_query",
                arguments={"q": "test"},
                call_id="c-wire",
                agent_id="agent-wire",
                metadata={"server_name": "prod-gw", "user_id": "bob"},
            )
        )
        entry = result.audit_entry
        assert entry is not None
        req = entry["authzen_request"]
        assert req["subject"]["id"] == "auth:agent:agentmesh:agent-wire"
        assert req["subject"]["properties"]["bound_to_user_id"] == "bob"
        assert req["resource"]["id"] == "mcp://prod-gw/sql_query"
        assert req["action"]["name"] == "tool.invoke"


class TestInterceptorConcurrency:
    @pytest.mark.slow
    def test_50_concurrent_evaluations(self, mock_pdp_allow: httpx.Client) -> None:
        interceptor = ARIAToolInterceptor(
            pdp_url=_PDP_URL,
            pdp_application=_APP,
            token=_TOKEN,
            http_client=mock_pdp_allow,
        )

        def evaluate(i: int) -> bool:
            r = interceptor.intercept(
                ToolCallRequest(
                    tool_name=f"tool_{i}",
                    arguments={"index": i},
                    call_id=f"call-{i}",
                    agent_id=f"agent-{i}",
                )
            )
            return r.allowed

        with concurrent.futures.ThreadPoolExecutor(max_workers=50) as pool:
            futures = [pool.submit(evaluate, i) for i in range(50)]
            results = [f.result() for f in futures]

        assert all(results)
        assert len(results) == 50


class TestInterceptorContextManager:
    def test_context_manager(self, mock_pdp_allow: httpx.Client) -> None:
        with ARIAToolInterceptor(
            pdp_url=_PDP_URL,
            pdp_application=_APP,
            token=_TOKEN,
            http_client=mock_pdp_allow,
        ) as interceptor:
            result = interceptor.intercept(
                ToolCallRequest(tool_name="t", arguments={}, agent_id="a")
            )
            assert result.allowed is True


class TestInterceptorRepr:
    def test_repr_contains_url_and_app(self, mock_pdp_allow: httpx.Client) -> None:
        interceptor = ARIAToolInterceptor(
            pdp_url=_PDP_URL,
            pdp_application=_APP,
            token=_TOKEN,
            http_client=mock_pdp_allow,
        )
        r = repr(interceptor)
        assert "ARIAToolInterceptor" in r
        assert _PDP_URL in r
        assert _APP in r
        assert _TOKEN not in r
