"""Tests for the OpenAI Agents SDK integration."""

from __future__ import annotations

import json
from types import SimpleNamespace
from unittest.mock import MagicMock, patch

import httpx
import pytest
import respx

from vorim import Vorim
from vorim.integrations.openai_agents import (
    VorimToolDefinition,
    VorimToolRegistry,
    create_vorim_openai_agent,
    run_agent_loop,
)

from .conftest import (
    AGENT_ID,
    API_KEY,
    AUDIT_RESPONSE,
    BASE_URL,
    PERMISSION_CHECK_ALLOWED,
    PERMISSION_CHECK_DENIED,
    REGISTRATION_DATA,
)


def _mock_tool_call(call_id: str, name: str, arguments: dict) -> SimpleNamespace:
    return SimpleNamespace(
        id=call_id,
        type="function",
        function=SimpleNamespace(name=name, arguments=json.dumps(arguments)),
    )


class TestVorimToolRegistry:
    def _registry(self, vorim: Vorim) -> VorimToolRegistry:
        registry = VorimToolRegistry(vorim=vorim, agent_id=AGENT_ID)
        registry.add(
            name="search",
            description="Search documents",
            parameters={
                "type": "object",
                "properties": {"query": {"type": "string"}},
                "required": ["query"],
            },
            execute=lambda args: f"Results for {args['query']}",
            permission="agent:read",
        )
        return registry

    def test_to_openai_tools(self):
        vorim = Vorim(api_key=API_KEY, base_url=BASE_URL)
        registry = self._registry(vorim)
        tools = registry.to_openai_tools()
        assert len(tools) == 1
        assert tools[0]["type"] == "function"
        assert tools[0]["function"]["name"] == "search"
        assert "query" in tools[0]["function"]["parameters"]["properties"]
        vorim.close()

    def test_to_openai_tools_with_strict(self):
        vorim = Vorim(api_key=API_KEY, base_url=BASE_URL)
        registry = VorimToolRegistry(vorim=vorim, agent_id=AGENT_ID)
        registry.add(
            name="strict_tool",
            description="Strict tool",
            parameters={"type": "object", "properties": {}},
            execute=lambda args: "ok",
            strict=True,
        )
        tools = registry.to_openai_tools()
        assert tools[0]["function"]["strict"] is True
        vorim.close()

    @respx.mock
    def test_execute_tool_call_success(self):
        respx.post(f"{BASE_URL}/v1/agents/{AGENT_ID}/permissions/verify").mock(
            return_value=httpx.Response(200, json=PERMISSION_CHECK_ALLOWED)
        )
        respx.post(f"{BASE_URL}/v1/audit/events").mock(
            return_value=httpx.Response(200, json=AUDIT_RESPONSE)
        )
        vorim = Vorim(api_key=API_KEY, base_url=BASE_URL)
        registry = self._registry(vorim)

        tc = _mock_tool_call("call-1", "search", {"query": "AI papers"})
        results = registry.execute_tool_calls([tc])

        assert len(results) == 1
        assert results[0]["role"] == "tool"
        assert results[0]["tool_call_id"] == "call-1"
        assert results[0]["content"] == "Results for AI papers"
        vorim.close()

    @respx.mock
    def test_execute_tool_call_denied(self):
        respx.post(f"{BASE_URL}/v1/agents/{AGENT_ID}/permissions/verify").mock(
            return_value=httpx.Response(200, json=PERMISSION_CHECK_DENIED)
        )
        respx.post(f"{BASE_URL}/v1/audit/events").mock(
            return_value=httpx.Response(200, json=AUDIT_RESPONSE)
        )
        vorim = Vorim(api_key=API_KEY, base_url=BASE_URL)
        registry = self._registry(vorim)

        tc = _mock_tool_call("call-2", "search", {"query": "secret"})
        results = registry.execute_tool_calls([tc])

        assert len(results) == 1
        content = json.loads(results[0]["content"])
        assert "Permission denied" in content["error"]
        vorim.close()

    @respx.mock
    def test_execute_tool_call_error(self):
        respx.post(f"{BASE_URL}/v1/agents/{AGENT_ID}/permissions/verify").mock(
            return_value=httpx.Response(200, json=PERMISSION_CHECK_ALLOWED)
        )
        respx.post(f"{BASE_URL}/v1/audit/events").mock(
            return_value=httpx.Response(200, json=AUDIT_RESPONSE)
        )
        vorim = Vorim(api_key=API_KEY, base_url=BASE_URL)
        registry = VorimToolRegistry(vorim=vorim, agent_id=AGENT_ID)
        registry.add(
            name="failing_tool",
            description="Always fails",
            parameters={"type": "object", "properties": {}},
            execute=lambda args: (_ for _ in ()).throw(ValueError("boom")),
        )

        tc = _mock_tool_call("call-3", "failing_tool", {})
        results = registry.execute_tool_calls([tc])

        content = json.loads(results[0]["content"])
        assert "boom" in content["error"]
        vorim.close()

    def test_unknown_tool(self):
        vorim = Vorim(api_key=API_KEY, base_url=BASE_URL)
        registry = self._registry(vorim)

        tc = _mock_tool_call("call-4", "nonexistent", {})
        results = registry.execute_tool_calls([tc])

        content = json.loads(results[0]["content"])
        assert "Unknown tool" in content["error"]
        vorim.close()

    def test_invalid_json_args(self):
        vorim = Vorim(api_key=API_KEY, base_url=BASE_URL)
        registry = self._registry(vorim)

        tc = SimpleNamespace(
            id="call-5",
            type="function",
            function=SimpleNamespace(name="search", arguments="not-json{{{"),
        )
        results = registry.execute_tool_calls([tc])

        content = json.loads(results[0]["content"])
        assert "Invalid JSON" in content["error"]
        vorim.close()

    def test_add_all(self):
        vorim = Vorim(api_key=API_KEY, base_url=BASE_URL)
        registry = VorimToolRegistry(vorim=vorim, agent_id=AGENT_ID)
        defs = [
            VorimToolDefinition(
                name="tool_a",
                description="A",
                parameters={},
                execute=lambda a: "a",
            ),
            VorimToolDefinition(
                name="tool_b",
                description="B",
                parameters={},
                execute=lambda a: "b",
            ),
        ]
        registry.add_all(defs)
        tools = registry.to_openai_tools()
        assert len(tools) == 2
        vorim.close()


class TestCreateVorimOpenAIAgent:
    @respx.mock
    def test_create(self):
        respx.post(f"{BASE_URL}/v1/agents").mock(
            return_value=httpx.Response(200, json=REGISTRATION_DATA)
        )
        vorim = Vorim(api_key=API_KEY, base_url=BASE_URL)
        result = create_vorim_openai_agent(
            vorim,
            name="test-agent",
            capabilities=["search"],
            scopes=["agent:read", "agent:execute"],
            tools=[
                VorimToolDefinition(
                    name="search",
                    description="Search",
                    parameters={},
                    execute=lambda a: "ok",
                ),
            ],
        )
        assert result["agent_id"] == AGENT_ID
        assert result["private_key"] == "ed25519-private-key"
        assert isinstance(result["registry"], VorimToolRegistry)
        vorim.close()


class TestRunAgentLoop:
    @respx.mock
    def test_simple_loop(self):
        respx.post(f"{BASE_URL}/v1/agents/{AGENT_ID}/permissions/verify").mock(
            return_value=httpx.Response(200, json=PERMISSION_CHECK_ALLOWED)
        )
        respx.post(f"{BASE_URL}/v1/audit/events").mock(
            return_value=httpx.Response(200, json=AUDIT_RESPONSE)
        )

        vorim = Vorim(api_key=API_KEY, base_url=BASE_URL)
        registry = VorimToolRegistry(vorim=vorim, agent_id=AGENT_ID)
        registry.add(
            name="search",
            description="Search",
            parameters={"type": "object", "properties": {"q": {"type": "string"}}},
            execute=lambda args: f"Found: {args['q']}",
        )

        # Mock OpenAI client: first call returns tool_call, second returns stop
        call_count = 0

        def mock_create(**kwargs):
            nonlocal call_count
            call_count += 1
            if call_count == 1:
                return SimpleNamespace(
                    choices=[
                        SimpleNamespace(
                            finish_reason="tool_calls",
                            message=SimpleNamespace(
                                content=None,
                                tool_calls=[
                                    _mock_tool_call("tc-1", "search", {"q": "test"}),
                                ],
                            ),
                        )
                    ]
                )
            else:
                return SimpleNamespace(
                    choices=[
                        SimpleNamespace(
                            finish_reason="stop",
                            message=SimpleNamespace(content="Here are the results.", tool_calls=None),
                        )
                    ]
                )

        mock_openai = SimpleNamespace(
            chat=SimpleNamespace(
                completions=SimpleNamespace(create=mock_create)
            )
        )

        result = run_agent_loop(
            vorim=vorim,
            agent_id=AGENT_ID,
            openai_client=mock_openai,
            registry=registry,
            user_message="Find stuff",
            system_prompt="You are helpful.",
            max_iterations=5,
        )
        assert result == "Here are the results."
        vorim.close()
