"""Unit tests for resource template support in the registry.

Templates are URI patterns with `{name}` placeholders (RFC 6570 simple
expansion); the gateway routes incoming `read_resource(uri)` calls through
the first template whose regex matches the URI.
"""

import pytest

from ..models import Backend, ResourceDescriptor, ResourceTemplateDescriptor
from ..registry import Registry, _template_to_regex


@pytest.fixture
def backend_a():
    return Backend(id="a", url="http://a.local/sse")


@pytest.fixture
def backend_b():
    return Backend(id="b", url="http://b.local/sse")


@pytest.fixture
def registry():
    return Registry()


# ---- _template_to_regex --------------------------------------------------------


def test_regex_single_placeholder():
    r = _template_to_regex("tracked://pubkey/{hex}")
    assert r.match("tracked://pubkey/abcdef")
    assert r.match("tracked://pubkey/x")
    # Trailing path = additional segment, not part of `{hex}` (placeholder
    # stops at `/`).
    assert not r.match("tracked://pubkey/abc/extra")
    assert not r.match("tracked://pubkey/")
    assert not r.match("other://pubkey/abc")


def test_regex_multiple_placeholders():
    r = _template_to_regex("foo://{a}/bar/{b}")
    assert r.match("foo://1/bar/2")
    assert not r.match("foo://1/baz/2")  # literal mismatch


def test_regex_escapes_literal_special_chars():
    r = _template_to_regex("foo://x.y/{n}")
    assert r.match("foo://x.y/abc")
    # `.` is escaped — must not match anything-in-its-place.
    assert not r.match("foo://xZy/abc")


# ---- register / resolve --------------------------------------------------------


def test_register_and_resolve_template(registry, backend_a):
    registry.add_backend(backend_a)
    registry.register_resource_template(
        backend_a,
        ResourceTemplateDescriptor(
            uri_template="tracked://pubkey/{hex}",
            name="pubkey-status",
            mime_type="application/json",
        ),
    )

    backend, original = registry.resolve_resource_or_template("tracked://pubkey/abc")
    assert backend == backend_a
    # Public URI passes through unchanged — backend matches the placeholder
    # itself.
    assert original == "tracked://pubkey/abc"


def test_resolve_prefers_exact_resource_over_template(registry, backend_a, backend_b):
    registry.add_backend(backend_a)
    registry.add_backend(backend_b)
    # Template on backend A claims everything under `tracked://pubkey/...`
    registry.register_resource_template(
        backend_a,
        ResourceTemplateDescriptor(
            uri_template="tracked://pubkey/{hex}",
            name="generic",
        ),
    )
    # Backend B publishes a specific URI under that prefix.
    registry.register_resource(
        backend_b,
        ResourceDescriptor(uri="tracked://pubkey/special", name="special"),
    )

    # Exact match wins, regardless of registration order.
    backend, original = registry.resolve_resource_or_template("tracked://pubkey/special")
    assert backend == backend_b
    assert original == "tracked://pubkey/special"

    # Other URIs still go to the template.
    backend, original = registry.resolve_resource_or_template("tracked://pubkey/anything")
    assert backend == backend_a


def test_resolve_unknown_uri_raises(registry, backend_a):
    registry.add_backend(backend_a)
    registry.register_resource_template(
        backend_a,
        ResourceTemplateDescriptor(uri_template="tracked://pubkey/{hex}", name="x"),
    )
    with pytest.raises(KeyError):
        registry.resolve_resource_or_template("foo://bar")


def test_template_collision_different_backends_raises(registry, backend_a, backend_b):
    registry.add_backend(backend_a)
    registry.add_backend(backend_b)
    registry.register_resource_template(
        backend_a,
        ResourceTemplateDescriptor(uri_template="x://{n}", name="a"),
    )
    with pytest.raises(RuntimeError, match="collision"):
        registry.register_resource_template(
            backend_b,
            ResourceTemplateDescriptor(uri_template="x://{n}", name="b"),
        )


def test_template_re_register_same_backend_replaces(registry, backend_a):
    """Reconnect/refresh re-registers templates for the same backend; the
    second call must not throw — it should simply replace the entry."""
    registry.add_backend(backend_a)
    registry.register_resource_template(
        backend_a,
        ResourceTemplateDescriptor(uri_template="x://{n}", name="v1"),
    )
    registry.register_resource_template(
        backend_a,
        ResourceTemplateDescriptor(uri_template="x://{n}", name="v2"),
    )
    templates = registry.list_resource_templates()
    assert len(templates) == 1
    assert templates[0].descriptor.name == "v2"


def test_remove_backend_clears_templates(registry, backend_a, backend_b):
    registry.add_backend(backend_a)
    registry.add_backend(backend_b)
    registry.register_resource_template(
        backend_a,
        ResourceTemplateDescriptor(uri_template="a://{n}", name="ta"),
    )
    registry.register_resource_template(
        backend_b,
        ResourceTemplateDescriptor(uri_template="b://{n}", name="tb"),
    )

    registry.remove_backend("a")
    rest = registry.list_resource_templates()
    assert [m.descriptor.name for m in rest] == ["tb"]


def test_remove_backend_resource_templates_only(registry, backend_a):
    """`remove_backend_resource_templates` (used at disconnect) clears only
    templates — regular resources stay so reconnect can replay them."""
    registry.add_backend(backend_a)
    registry.register_resource(
        backend_a, ResourceDescriptor(uri="static://x", name="x"),
    )
    registry.register_resource_template(
        backend_a, ResourceTemplateDescriptor(uri_template="t://{n}", name="t"),
    )
    registry.remove_backend_resource_templates("a")
    assert registry.list_resource_templates() == []
    assert len(registry.list_resources()) == 1


def test_first_match_wins_on_overlapping_templates(registry, backend_a, backend_b):
    """If two templates compete for a URI (e.g. one is a strict prefix of
    the other), first-registered wins — predictable, no implicit specificity
    ranking."""
    registry.add_backend(backend_a)
    registry.add_backend(backend_b)
    registry.register_resource_template(
        backend_a,
        ResourceTemplateDescriptor(uri_template="x://{n}", name="ta"),
    )
    # backend_b registers something that ALSO matches x://foo. Different
    # template string — no collision — but URI ambiguous.
    registry.register_resource_template(
        backend_b,
        ResourceTemplateDescriptor(uri_template="x://{m}", name="tb"),
    )
    backend, _ = registry.resolve_resource_or_template("x://foo")
    assert backend == backend_a
