"""Unit tests for handler/client.py"""

from unittest.mock import patch, MagicMock
_mock_env = MagicMock()
_mock_env.register.proxy_socket = "/var/run/ieops/proxy.sock"
_mock_env.register.brslet_socket = "/var/run/ieops/brslet.sock"
with (
    patch("gmi_ieops.handler.client.uvicorn_logger", MagicMock()),
    patch("gmi_ieops.handler.client.env", _mock_env),
):
    from gmi_ieops.handler.client import _build_unix_url, Payload, Response, _get_socket_paths
    import gmi_ieops.handler.client as _mod
_mod.env = _mock_env


class TestClient:

    def test_build_unix_url(self):
        url = _build_unix_url("/var/run/ieops/proxy.sock", "register")
        assert url == "http+unix://%2Fvar%2Frun%2Fieops%2Fproxy.sock/register"
        url2 = _build_unix_url("/tmp/test.sock", "api/model/register")
        assert url2.startswith("http+unix://") and url2.endswith("/api/model/register")
        assert "%2F" in url2

    def test_response(self):
        r = Response(message="ok", data={"k": "v"})
        assert r.message == "ok" and r.data == {"k": "v"}
        assert "ok" in repr(r)
        assert Response(message="e").data is None

    def test_socket_paths(self):
        paths = _get_socket_paths()
        assert paths["proxy"] == "/var/run/ieops/proxy.sock"
        assert paths["brslet"] == "/var/run/ieops/brslet.sock"

    def test_payload(self):
        p: Payload = {"path": "api/test", "payload": {"key": "value"}}
        assert p["path"] == "api/test"


class TestClientBoundary:
    """Edge case tests for client parameter clamping.
    
    Uses mock session to verify clamping without real Unix sockets.
    """

    def test_post_negative_max_retries(self):
        """post(max_retries=-1) should still attempt once, not return None."""
        from gmi_ieops.handler.client import post
        mock_session = MagicMock()
        mock_resp = MagicMock()
        mock_resp.status_code = 200
        mock_resp.json.return_value = {"message": "ok", "data": None}
        mock_session.post.return_value = mock_resp

        with patch.object(_mod, "_get_sync_session", return_value=mock_session):
            result = post({"path": "test", "payload": {}}, max_retries=-1)
        assert result is not None
        assert result.message == "ok"
        mock_session.post.assert_called_once()

    def test_post_negative_timeout_clamped(self):
        """post(timeout=-5) should clamp to 0.1, not crash."""
        from gmi_ieops.handler.client import post
        mock_session = MagicMock()
        mock_resp = MagicMock()
        mock_resp.status_code = 200
        mock_resp.json.return_value = {"message": "ok", "data": None}
        mock_session.post.return_value = mock_resp

        with patch.object(_mod, "_get_sync_session", return_value=mock_session):
            result = post({"path": "test", "payload": {}}, timeout=-5.0)
        assert result is not None
        # Verify timeout was clamped to 0.1
        call_kwargs = mock_session.post.call_args
        assert call_kwargs[1]["timeout"] == 0.1

    def test_get_negative_params(self):
        """get() with negative max_retries/timeout should not crash."""
        from gmi_ieops.handler.client import get
        mock_session = MagicMock()
        mock_resp = MagicMock()
        mock_resp.status_code = 200
        mock_resp.json.return_value = {"message": "ok", "data": None}
        mock_session.get.return_value = mock_resp

        with patch.object(_mod, "_get_sync_session", return_value=mock_session):
            result = get("test", timeout=-1.0, max_retries=-5)
        assert result is not None
        mock_session.get.assert_called_once()

    def test_delete_negative_params(self):
        """delete() with negative max_retries/timeout should not crash."""
        from gmi_ieops.handler.client import delete
        mock_session = MagicMock()
        mock_resp = MagicMock()
        mock_resp.status_code = 200
        mock_resp.json.return_value = {"message": "ok", "data": None}
        mock_session.delete.return_value = mock_resp

        with patch.object(_mod, "_get_sync_session", return_value=mock_session):
            result = delete("test", timeout=0.0, max_retries=-1)
        assert result is not None
        mock_session.delete.assert_called_once()
