"""The scope service in isolation (design D2, D3).

Unit-level: these assert the *shape* of the SQL fragment, not what a query returns. The
behavioural proof — that a Host actually cannot read another owner's guest by id — needs a
real database and lives in `test_scoping_integration.py`.

Worth having both. This file catches the regression where `visible_events` starts returning
`true()` for a Host, which no amount of endpoint testing would notice until it was in front
of a customer.
"""

import uuid

import pytest
from sqlalchemy import true
from sqlalchemy.sql.elements import True_

from app.models.enums import AdminRole, AuthMethod
from app.services import scope
from app.services.auth import CurrentAdmin


def _admin(role: AdminRole) -> CurrentAdmin:
    return CurrentAdmin(
        id=uuid.uuid4(),
        email=f"{role}@example.com",
        name=None,
        role=role,
        auth_method=AuthMethod.GOOGLE,
    )


SUPER = _admin(AdminRole.SUPER_ADMIN)
HOST = _admin(AdminRole.HOST)


def test_a_super_admin_is_unscoped() -> None:
    assert scope.is_unscoped(SUPER)
    assert not scope.is_unscoped(HOST)


@pytest.mark.parametrize(
    "fragment",
    [scope.visible_events, scope.guest_in_scope, scope.invitation_in_scope],
)
def test_super_admin_fragments_impose_no_restriction(fragment: object) -> None:
    """`true()` rather than a comparison, so both roles run one code path (design D2).

    If this ever became a real predicate, a Super Admin would silently stop seeing events
    nobody owns — and the point of `true()` is that there is no second branch to maintain.
    """
    assert isinstance(fragment(SUPER), True_)  # type: ignore[operator]
    assert str(fragment(SUPER)) == str(true())  # type: ignore[operator]


@pytest.mark.parametrize(
    "fragment",
    [scope.visible_events, scope.guest_in_scope, scope.invitation_in_scope],
)
def test_host_fragments_are_real_predicates(fragment: object) -> None:
    rendered = str(fragment(HOST))  # type: ignore[operator]
    assert not isinstance(fragment(HOST), True_)  # type: ignore[operator]
    assert "owner_admin_id" in rendered


def test_a_hosts_event_fragment_compares_against_their_own_id() -> None:
    compiled = scope.visible_events(HOST).compile(compile_kwargs={"literal_binds": True})
    # `.hex` because SQLAlchemy renders a bound UUID undashed.
    assert HOST.id.hex in str(compiled)
    assert SUPER.id.hex not in str(compiled)


def test_guest_scope_is_derived_from_the_event() -> None:
    """A guest carries no owner of its own, so the fragment must reach through `event`."""
    rendered = str(scope.guest_in_scope(HOST))
    assert "guest.event_id IN" in rendered
    assert "owner_admin_id" in rendered


def test_out_of_scope_is_404_not_403() -> None:
    """A 403 on a real id and a 404 on a fake one is a measurable difference (design D3).

    Across a range of ids that difference tells a Host how many other customers exist and,
    from the events it confirms, when their weddings are.
    """
    assert scope.not_found().status_code == 404


def test_system_wide_settings_are_403_and_say_why() -> None:
    """The opposite case: a single wedding-wide record whose existence is not a secret."""
    scope.require_system_wide(SUPER)  # does not raise

    with pytest.raises(Exception) as exc:
        scope.require_system_wide(HOST)
    assert getattr(exc.value, "status_code", None) == 403
    assert "Super Admin" in str(getattr(exc.value, "detail", ""))
