"""Consumer contract: nextcloud-mcp-server -> astrolabe OCS capabilities.

The MCP server reads which content sources an admin has approved for semantic
search via :func:`nextcloud_mcp_server.capabilities.allowed_doc_types`, which
calls :meth:`NextcloudClient.capabilities` →
``GET /ocs/v2.php/cloud/capabilities`` and parses
``ocs.data.capabilities.astrolabe.semantic_search.enabled_doc_types`` (the
``OCA\\Astrolabe\\Capabilities`` provider on the astrolabe side).

This pact pins the request shape and the two states the consumer branches on:

- some sources approved -> ``enabled_doc_types`` is a non-empty list, parsed to
  the corresponding frozenset (the search/scan/purge gates restrict to it)
- every source disabled  -> ``enabled_doc_types`` is ``[]``, parsed to an empty
  frozenset (an all-disabled admin config — distinct from "no restriction")

The "no astrolabe block" / fail-open (``None``) case is internal defensive
handling for older astrolabe versions, not a contract obligation of the current
provider, so it is covered by a unit test (``tests/unit/test_capabilities.py``)
rather than a pact interaction.

Only the capability block astrolabe owns is pinned — the real OCS response also
carries many other apps' capabilities, which pact ignores (unspecified keys are
allowed in the provider response).

See ADR-029 for the overall contract-testing architecture.
"""

import pytest
from httpx import BasicAuth

from nextcloud_mcp_server.capabilities import allowed_doc_types, clear_cache
from nextcloud_mcp_server.client import NextcloudClient

pytestmark = pytest.mark.contract


def _ocs_capabilities(enabled_doc_types: list[str]) -> dict:
    """Minimal OCS envelope carrying just astrolabe's semantic_search block.

    Intentionally omits the rest of a real OCS response (other apps'
    capabilities, ``meta.statuscode``/``message``, etc.): Pact V4 allows extra
    provider-side keys, so pinning only the block this consumer reads keeps the
    contract focused on what astrolabe owns without coupling to Nextcloud-core
    envelope fields.
    """
    return {
        "ocs": {
            "meta": {"status": "ok"},
            "data": {
                "capabilities": {
                    "astrolabe": {
                        "semantic_search": {
                            "enabled_doc_types": enabled_doc_types,
                        }
                    }
                }
            },
        }
    }


async def test_capabilities_report_admin_approved_doc_types(consumer_pact):
    """Approved sources are returned and parsed into the allow-set."""
    clear_cache()
    (
        consumer_pact.upon_receiving("a request for OCS capabilities")
        .given("astrolabe has approved file and note for semantic search")
        .with_request("GET", "/ocs/v2.php/cloud/capabilities")
        .with_header("OCS-APIRequest", "true")
        .will_respond_with(200)
        .with_body(
            _ocs_capabilities(["file", "note"]),
            content_type="application/json",
        )
    )

    with consumer_pact.serve() as srv:
        client = NextcloudClient(
            base_url=str(srv.url),
            username="admin",
            auth=BasicAuth("admin", "app-password"),
        )
        allowed = await allowed_doc_types(client, "admin")

    assert allowed == frozenset({"file", "note"})


async def test_capabilities_report_all_sources_disabled(consumer_pact):
    """An empty allow-set (admin disabled everything) is distinct from None."""
    clear_cache()
    (
        consumer_pact.upon_receiving(
            "a request for OCS capabilities with every source disabled"
        )
        .given("astrolabe has disabled all sources for semantic search")
        .with_request("GET", "/ocs/v2.php/cloud/capabilities")
        .with_header("OCS-APIRequest", "true")
        .will_respond_with(200)
        .with_body(
            _ocs_capabilities([]),
            content_type="application/json",
        )
    )

    with consumer_pact.serve() as srv:
        client = NextcloudClient(
            base_url=str(srv.url),
            username="admin",
            auth=BasicAuth("admin", "app-password"),
        )
        allowed = await allowed_doc_types(client, "admin")

    # Present-but-empty => empty frozenset (restrict everything), NOT None.
    assert allowed == frozenset()
