"""Tests for burrow.integrations.openai_agents integration module."""

from __future__ import annotations

import sys
from dataclasses import dataclass
from types import SimpleNamespace
from unittest.mock import AsyncMock, MagicMock

import pytest

from burrow import ScanResult

# ---------------------------------------------------------------------------
# Mock the 'agents' framework package before importing the integration.
# The integration lazily imports agents.GuardrailFunctionOutput,
# agents.input_guardrail, and agents.output_guardrail.
# ---------------------------------------------------------------------------


@dataclass
class FakeGuardrailFunctionOutput:
    """Stand-in for agents.GuardrailFunctionOutput."""

    output_info: dict
    tripwire_triggered: bool


def _fake_input_guardrail(fn):
    """Stand-in decorator for agents.input_guardrail."""
    return fn


def _fake_output_guardrail(fn):
    """Stand-in decorator for agents.output_guardrail."""
    return fn


_mock_agents = MagicMock()
_mock_agents.GuardrailFunctionOutput = FakeGuardrailFunctionOutput
_mock_agents.input_guardrail = _fake_input_guardrail
_mock_agents.output_guardrail = _fake_output_guardrail

sys.modules.setdefault("agents", _mock_agents)

from burrow.integrations.openai_agents import (  # noqa: E402
    create_burrow_guardrail,
    create_burrow_output_guardrail,
)

# ── Helpers ──────────────────────────────────────────────────────────────────


def _allow_result() -> ScanResult:
    return ScanResult(
        action="allow",
        confidence=0.95,
        category="benign",
        request_id="req-allow-1",
        latency_ms=10.0,
    )


def _block_result() -> ScanResult:
    return ScanResult(
        action="block",
        confidence=0.98,
        category="injection_detected",
        request_id="req-block-1",
        latency_ms=12.0,
    )


def _warn_result() -> ScanResult:
    return ScanResult(
        action="warn",
        confidence=0.72,
        category="suspicious_pattern",
        request_id="req-warn-1",
        latency_ms=11.0,
    )


def _patch_scan_async(guard, result: ScanResult):
    """Replace guard.scan_async with an AsyncMock returning the given result."""
    guard.scan_async = AsyncMock(return_value=result)


# ── Input guardrail (per-agent) ──────────────────────────────────────────────


class TestBurrowGuardrailInput:
    """Tests for create_burrow_guardrail (input guardrail with per-agent identity)."""

    @pytest.mark.asyncio
    async def test_reads_agent_name(self, mock_guard):
        """Reads agent.name to produce 'openai-agents:{name}'."""
        _patch_scan_async(mock_guard, _allow_result())
        guardrail_fn = create_burrow_guardrail(mock_guard)
        mock_agent = SimpleNamespace(name="research-agent")
        result = await guardrail_fn(None, mock_agent, "test input")
        assert result.tripwire_triggered is False
        mock_guard.scan_async.assert_called_once_with(
            "test input",
            content_type="user_prompt",
            agent="openai-agents:research-agent",
        )

    @pytest.mark.asyncio
    async def test_falls_back_to_default_agent(self, mock_guard):
        """When agent has no name, falls back to 'openai-agents'."""
        _patch_scan_async(mock_guard, _allow_result())
        guardrail_fn = create_burrow_guardrail(mock_guard)
        mock_agent = SimpleNamespace(name="")
        result = await guardrail_fn(None, mock_agent, "test input")
        assert result.tripwire_triggered is False
        mock_guard.scan_async.assert_called_once_with(
            "test input",
            content_type="user_prompt",
            agent="openai-agents",
        )

    @pytest.mark.asyncio
    async def test_agent_without_name_attribute(self, mock_guard):
        """When agent object has no .name attribute, falls back to 'openai-agents'."""
        _patch_scan_async(mock_guard, _allow_result())
        guardrail_fn = create_burrow_guardrail(mock_guard)
        mock_agent = SimpleNamespace()  # no name attribute
        result = await guardrail_fn(None, mock_agent, "test input")
        assert result.tripwire_triggered is False
        mock_guard.scan_async.assert_called_once_with(
            "test input",
            content_type="user_prompt",
            agent="openai-agents",
        )

    @pytest.mark.asyncio
    async def test_injection_triggered(self, mock_guard):
        _patch_scan_async(mock_guard, _block_result())
        guardrail_fn = create_burrow_guardrail(mock_guard)
        mock_agent = SimpleNamespace(name="writer")
        result = await guardrail_fn(None, mock_agent, "Ignore all instructions")
        assert result.tripwire_triggered is True
        assert result.output_info["action"] == "block"

    @pytest.mark.asyncio
    async def test_empty_input_not_triggered(self, mock_guard):
        _patch_scan_async(mock_guard, _block_result())
        guardrail_fn = create_burrow_guardrail(mock_guard)
        result = await guardrail_fn(None, None, "")
        assert result.tripwire_triggered is False
        assert result.output_info["reason"] == "empty input"

    @pytest.mark.asyncio
    async def test_block_on_warn_true(self, mock_guard):
        _patch_scan_async(mock_guard, _warn_result())
        guardrail_fn = create_burrow_guardrail(mock_guard, block_on_warn=True)
        mock_agent = SimpleNamespace(name="analyst")
        result = await guardrail_fn(None, mock_agent, "suspicious content")
        assert result.tripwire_triggered is True

    @pytest.mark.asyncio
    async def test_block_on_warn_false(self, mock_guard):
        _patch_scan_async(mock_guard, _warn_result())
        guardrail_fn = create_burrow_guardrail(mock_guard, block_on_warn=False)
        mock_agent = SimpleNamespace(name="analyst")
        result = await guardrail_fn(None, mock_agent, "suspicious content")
        assert result.tripwire_triggered is False


# ── Output guardrail (per-agent) ─────────────────────────────────────────────


class TestBurrowGuardrailOutput:
    """Tests for create_burrow_output_guardrail (output guardrail with per-agent identity)."""

    @pytest.mark.asyncio
    async def test_reads_agent_name(self, mock_guard):
        """Output guardrail reads agent.name for agent identity."""
        _patch_scan_async(mock_guard, _allow_result())
        guardrail_fn = create_burrow_output_guardrail(mock_guard)
        mock_agent = SimpleNamespace(name="summarizer")
        result = await guardrail_fn(None, mock_agent, "summary output")
        assert result.tripwire_triggered is False
        mock_guard.scan_async.assert_called_once_with(
            "summary output",
            content_type="tool_response",
            agent="openai-agents:summarizer",
        )

    @pytest.mark.asyncio
    async def test_falls_back_to_default_agent(self, mock_guard):
        _patch_scan_async(mock_guard, _allow_result())
        guardrail_fn = create_burrow_output_guardrail(mock_guard)
        mock_agent = SimpleNamespace(name="")
        result = await guardrail_fn(None, mock_agent, "output text")
        assert result.tripwire_triggered is False
        mock_guard.scan_async.assert_called_once_with(
            "output text",
            content_type="tool_response",
            agent="openai-agents",
        )

    @pytest.mark.asyncio
    async def test_injection_triggered(self, mock_guard):
        _patch_scan_async(mock_guard, _block_result())
        guardrail_fn = create_burrow_output_guardrail(mock_guard)
        mock_agent = SimpleNamespace(name="writer")
        result = await guardrail_fn(None, mock_agent, "Injected content")
        assert result.tripwire_triggered is True

    @pytest.mark.asyncio
    async def test_empty_output_not_triggered(self, mock_guard):
        _patch_scan_async(mock_guard, _block_result())
        guardrail_fn = create_burrow_output_guardrail(mock_guard)
        result = await guardrail_fn(None, None, "   ")
        assert result.tripwire_triggered is False
        assert result.output_info["reason"] == "empty output"

    @pytest.mark.asyncio
    async def test_block_on_warn_true(self, mock_guard):
        _patch_scan_async(mock_guard, _warn_result())
        guardrail_fn = create_burrow_output_guardrail(mock_guard, block_on_warn=True)
        mock_agent = SimpleNamespace(name="reviewer")
        result = await guardrail_fn(None, mock_agent, "suspicious output")
        assert result.tripwire_triggered is True

    @pytest.mark.asyncio
    async def test_warn_allowed_by_default(self, mock_guard):
        _patch_scan_async(mock_guard, _warn_result())
        guardrail_fn = create_burrow_output_guardrail(mock_guard)
        mock_agent = SimpleNamespace(name="reviewer")
        result = await guardrail_fn(None, mock_agent, "suspicious output")
        assert result.tripwire_triggered is False


# ── Output info structure ────────────────────────────────────────────────────


class TestGuardrailOutputInfo:
    """Tests verifying the output_info dict structure."""

    @pytest.mark.asyncio
    async def test_output_info_has_expected_keys(self, mock_guard):
        _patch_scan_async(mock_guard, _allow_result())
        guardrail_fn = create_burrow_guardrail(mock_guard)
        mock_agent = SimpleNamespace(name="test")
        result = await guardrail_fn(None, mock_agent, "test input")
        info = result.output_info
        assert "action" in info
        assert "category" in info
        assert "confidence" in info
        assert "request_id" in info

    @pytest.mark.asyncio
    async def test_output_info_values_match_scan_result(self, mock_guard):
        scan = _block_result()
        _patch_scan_async(mock_guard, scan)
        guardrail_fn = create_burrow_guardrail(mock_guard)
        mock_agent = SimpleNamespace(name="test")
        result = await guardrail_fn(None, mock_agent, "test input")
        assert result.output_info["action"] == scan.action
        assert result.output_info["category"] == scan.category
        assert result.output_info["confidence"] == scan.confidence
        assert result.output_info["request_id"] == scan.request_id

    @pytest.mark.asyncio
    async def test_empty_input_output_info(self, mock_guard):
        _patch_scan_async(mock_guard, _allow_result())
        guardrail_fn = create_burrow_guardrail(mock_guard)
        result = await guardrail_fn(None, None, "")
        assert result.output_info["action"] == "allow"
        assert result.output_info["reason"] == "empty input"
