"""Tests for CacheResource."""

from __future__ import annotations

from unittest.mock import AsyncMock

import pytest

from mcpgateway_sdk._exceptions import NotFoundError
from mcpgateway_sdk.resources.cache import CacheResource


@pytest.fixture
def http() -> AsyncMock:
    """Mock HTTPClient."""
    return AsyncMock()


@pytest.fixture
def cache(http: AsyncMock) -> CacheResource:
    """CacheResource with mocked HTTP."""
    return CacheResource(http)


class TestEndpointsDict:
    """Tests for _endpoints class variable."""

    def test_endpoints_has_get(self) -> None:
        assert CacheResource._endpoints["get"] == ("GET", "/api/v1/sdk/cache/{key}")

    def test_endpoints_has_set(self) -> None:
        assert CacheResource._endpoints["set"] == ("PUT", "/api/v1/sdk/cache/{key}")

    def test_endpoints_has_exactly_two_entries(self) -> None:
        assert len(CacheResource._endpoints) == 2


class TestGet:
    """Tests for CacheResource.get."""

    async def test_sends_get_with_key_in_path(self, cache: CacheResource, http: AsyncMock) -> None:
        # Arrange
        http.request.return_value = {"value": "cached-data"}

        # Act
        await cache.get("my-key")

        # Assert
        http.request.assert_awaited_once_with("GET", "/api/v1/sdk/cache/my-key")

    async def test_returns_value_from_response(self, cache: CacheResource, http: AsyncMock) -> None:
        # Arrange
        http.request.return_value = {"value": {"foo": "bar", "count": 42}}

        # Act
        result = await cache.get("my-key")

        # Assert
        assert result == {"foo": "bar", "count": 42}

    async def test_returns_string_value(self, cache: CacheResource, http: AsyncMock) -> None:
        # Arrange
        http.request.return_value = {"value": "simple-string"}

        # Act
        result = await cache.get("text-key")

        # Assert
        assert result == "simple-string"

    async def test_returns_none_on_not_found(self, cache: CacheResource, http: AsyncMock) -> None:
        # Arrange
        http.request.side_effect = NotFoundError("Cache key not found")

        # Act
        result = await cache.get("missing-key")

        # Assert
        assert result is None

    async def test_returns_none_value(self, cache: CacheResource, http: AsyncMock) -> None:
        # Arrange — the server stores None explicitly
        http.request.return_value = {"value": None}

        # Act
        result = await cache.get("null-key")

        # Assert
        assert result is None


class TestSet:
    """Tests for CacheResource.set."""

    async def test_sends_put_with_key_value_and_ttl(
        self, cache: CacheResource, http: AsyncMock
    ) -> None:
        # Arrange
        http.request.return_value = None

        # Act
        await cache.set("my-key", {"foo": "bar"}, ttl=600)

        # Assert
        http.request.assert_awaited_once_with(
            "PUT",
            "/api/v1/sdk/cache/my-key",
            json={"value": {"foo": "bar"}, "ttl": 600},
        )

    async def test_default_ttl_is_300(self, cache: CacheResource, http: AsyncMock) -> None:
        # Arrange
        http.request.return_value = None

        # Act
        await cache.set("my-key", "data")

        # Assert
        body = http.request.call_args.kwargs["json"]
        assert body["ttl"] == 300

    async def test_returns_none(self, cache: CacheResource, http: AsyncMock) -> None:
        # Arrange
        http.request.return_value = None

        # Act
        result = await cache.set("my-key", "data")

        # Assert
        assert result is None

    async def test_set_with_string_value(self, cache: CacheResource, http: AsyncMock) -> None:
        # Arrange
        http.request.return_value = None

        # Act
        await cache.set("text-key", "hello world", ttl=60)

        # Assert
        http.request.assert_awaited_once_with(
            "PUT",
            "/api/v1/sdk/cache/text-key",
            json={"value": "hello world", "ttl": 60},
        )

    async def test_set_with_list_value(self, cache: CacheResource, http: AsyncMock) -> None:
        # Arrange
        http.request.return_value = None

        # Act
        await cache.set("list-key", [1, 2, 3])

        # Assert
        body = http.request.call_args.kwargs["json"]
        assert body["value"] == [1, 2, 3]
