"""Tests for REMOTE_HTTP tool execution via RemoteToolExecutor."""

import os
from unittest import mock
from unittest.mock import AsyncMock, MagicMock, patch

import pytest

from qremotex.config import ExecutorConfig
from qremotex.execution.tool_executor import RemoteToolExecutor


def _make_config(**overrides: str) -> ExecutorConfig:
    env = {
        "QRAPTOR_GATEWAY_URL": "wss://gw.qraptor.io/ws/executor",
        "QRAPTOR_API_KEY": "qrx_test_key123",
        "EXEC_TIMEOUT_SEC": "10",
    }
    env.update(overrides)
    with mock.patch.dict(os.environ, env, clear=False):
        return ExecutorConfig()


class TestRemoteHttpExecution:
    """REMOTE_HTTP tool execution tests."""

    @pytest.mark.asyncio
    async def test_missing_http_config(self) -> None:
        cfg = _make_config()
        ex = RemoteToolExecutor(cfg)

        result = await ex.execute(
            tool_key="project.crm_api",
            tool_params={"contact_id": "c-123"},
            tool_definition={"execution_target_kind": "REMOTE_HTTP"},
            context={},
        )

        assert result["success"] is False
        assert "missing http_config" in result["error"]

    @pytest.mark.asyncio
    async def test_missing_url_in_http_config(self) -> None:
        cfg = _make_config()
        ex = RemoteToolExecutor(cfg)

        result = await ex.execute(
            tool_key="project.crm_api",
            tool_params={},
            tool_definition={
                "execution_target_kind": "REMOTE_HTTP",
                "http_config": {"method": "GET"},
            },
            context={},
        )

        assert result["success"] is False
        assert "missing http_config.url" in result["error"]

    @pytest.mark.asyncio
    async def test_http_get_json_response(self) -> None:
        """Test REMOTE_HTTP GET with JSON response."""
        cfg = _make_config()
        ex = RemoteToolExecutor(cfg)

        mock_resp = AsyncMock()
        mock_resp.status = 200
        mock_resp.headers = {"content-type": "application/json; charset=utf-8"}
        mock_resp.json = AsyncMock(return_value={"name": "John", "id": 1})
        mock_resp.__aenter__ = AsyncMock(return_value=mock_resp)
        mock_resp.__aexit__ = AsyncMock(return_value=None)

        mock_session = AsyncMock()
        mock_session.request = MagicMock(return_value=mock_resp)
        mock_session.__aenter__ = AsyncMock(return_value=mock_session)
        mock_session.__aexit__ = AsyncMock(return_value=None)

        with patch("qremotex.execution.tool_executor.aiohttp.ClientSession", return_value=mock_session):
            result = await ex.execute(
                tool_key="project.crm_api",
                tool_params={},
                tool_definition={
                    "execution_target_kind": "REMOTE_HTTP",
                    "http_config": {
                        "url": "https://crm.internal/api/contacts/1",
                        "method": "GET",
                        "headers": {"Authorization": "Bearer token123"},
                    },
                    "timeout_seconds": 10,
                },
                context={"run_id": "r1"},
            )

        assert result["success"] is True
        assert result["result"]["status_code"] == 200
        assert result["result"]["body"]["name"] == "John"

    @pytest.mark.asyncio
    async def test_http_post_with_json_body(self) -> None:
        """Test REMOTE_HTTP POST sends tool_params as JSON body."""
        cfg = _make_config()
        ex = RemoteToolExecutor(cfg)

        mock_resp = AsyncMock()
        mock_resp.status = 201
        mock_resp.headers = {"content-type": "application/json"}
        mock_resp.json = AsyncMock(return_value={"created": True})
        mock_resp.__aenter__ = AsyncMock(return_value=mock_resp)
        mock_resp.__aexit__ = AsyncMock(return_value=None)

        mock_session = AsyncMock()
        mock_session.request = MagicMock(return_value=mock_resp)
        mock_session.__aenter__ = AsyncMock(return_value=mock_session)
        mock_session.__aexit__ = AsyncMock(return_value=None)

        with patch("qremotex.execution.tool_executor.aiohttp.ClientSession", return_value=mock_session):
            result = await ex.execute(
                tool_key="project.internal_api",
                tool_params={"name": "Test", "value": 42},
                tool_definition={
                    "execution_target_kind": "REMOTE_HTTP",
                    "http_config": {
                        "url": "https://api.internal/resources",
                        "method": "POST",
                    },
                    "timeout_seconds": 10,
                },
                context={},
            )

        assert result["success"] is True
        assert result["result"]["status_code"] == 201

        # Verify JSON body was sent
        mock_session.request.assert_called_once()
        call_kwargs = mock_session.request.call_args
        assert call_kwargs[1].get("json") == {"name": "Test", "value": 42}

    @pytest.mark.asyncio
    async def test_http_text_response(self) -> None:
        """Test REMOTE_HTTP with non-JSON response body."""
        cfg = _make_config()
        ex = RemoteToolExecutor(cfg)

        mock_resp = AsyncMock()
        mock_resp.status = 200
        mock_resp.headers = {"content-type": "text/plain"}
        mock_resp.text = AsyncMock(return_value="plain text response")
        mock_resp.__aenter__ = AsyncMock(return_value=mock_resp)
        mock_resp.__aexit__ = AsyncMock(return_value=None)

        mock_session = AsyncMock()
        mock_session.request = MagicMock(return_value=mock_resp)
        mock_session.__aenter__ = AsyncMock(return_value=mock_session)
        mock_session.__aexit__ = AsyncMock(return_value=None)

        with patch("qremotex.execution.tool_executor.aiohttp.ClientSession", return_value=mock_session):
            result = await ex.execute(
                tool_key="project.legacy_api",
                tool_params={},
                tool_definition={
                    "execution_target_kind": "REMOTE_HTTP",
                    "http_config": {
                        "url": "https://legacy.internal/status",
                        "method": "GET",
                    },
                    "timeout_seconds": 5,
                },
                context={},
            )

        assert result["success"] is True
        assert result["result"]["body"] == "plain text response"

    @pytest.mark.asyncio
    async def test_http_blocked_by_blocklist(self) -> None:
        """REMOTE_HTTP tool blocked by blocklist should return error without making HTTP call."""
        cfg = _make_config(BLOCKED_TOOL_KEYS="project.blocked_api")
        ex = RemoteToolExecutor(cfg)

        result = await ex.execute(
            tool_key="project.blocked_api",
            tool_params={},
            tool_definition={
                "execution_target_kind": "REMOTE_HTTP",
                "http_config": {
                    "url": "https://internal/secret",
                    "method": "GET",
                },
            },
            context={},
        )

        assert result["success"] is False
        assert "blocked" in result["error"]

    @pytest.mark.asyncio
    async def test_http_form_body_mode(self) -> None:
        """Test body_mode=form sends data as form-encoded."""
        cfg = _make_config()
        ex = RemoteToolExecutor(cfg)

        mock_resp = AsyncMock()
        mock_resp.status = 200
        mock_resp.headers = {"content-type": "application/json"}
        mock_resp.json = AsyncMock(return_value={"ok": True})
        mock_resp.__aenter__ = AsyncMock(return_value=mock_resp)
        mock_resp.__aexit__ = AsyncMock(return_value=None)

        mock_session = AsyncMock()
        mock_session.request = MagicMock(return_value=mock_resp)
        mock_session.__aenter__ = AsyncMock(return_value=mock_session)
        mock_session.__aexit__ = AsyncMock(return_value=None)

        with patch("qremotex.execution.tool_executor.aiohttp.ClientSession", return_value=mock_session):
            result = await ex.execute(
                tool_key="project.form_api",
                tool_params={"field1": "val1"},
                tool_definition={
                    "execution_target_kind": "REMOTE_HTTP",
                    "http_config": {
                        "url": "https://api.internal/form",
                        "method": "POST",
                        "body_mode": "form",
                    },
                    "timeout_seconds": 5,
                },
                context={},
            )

        assert result["success"] is True
        call_kwargs = mock_session.request.call_args
        assert call_kwargs[1].get("data") == {"field1": "val1"}
        assert "json" not in call_kwargs[1]
