"""Smoke tests for :mod:`mgf.common._identity`.

Pins the public ``app_name`` / ``app_version`` API plus the
namespace-package shape. Behaviour-level tests for every
observability module that READS the identity cache live under
``tests/unit/observability/``.

The legacy ``configure()`` identity-setter was removed in v0.33.0
per its AP-12 deprecation cycle. Identity is now set via
:func:`mgf.common.bootstrap` (transactional wiring) or
:func:`mgf.common.bootstrap_for_test` (test-friendly). The
underlying ``_set_identity`` helper is what these tests use to
keep test scope tight (no wiring side-effects).

These tests run with ``filterwarnings = error`` (per
``pyproject.toml``) so any unraised warning (resource leak, pydantic
deprecation, etc.) fails the build immediately.
"""

from __future__ import annotations

import warnings
from typing import TYPE_CHECKING

import mgf.common
from mgf.common import _identity

if TYPE_CHECKING:
    import pytest


def test_set_identity_sets_app_name_and_version() -> None:
    _identity._set_identity(app_name="testapp", app_version="9.9.9")
    assert mgf.common.app_name() == "testapp"
    assert mgf.common.app_version() == "9.9.9"


def test_set_identity_replaces_previous_value() -> None:
    _identity._set_identity(app_name="first", app_version="1.0")
    _identity._set_identity(app_name="second", app_version="2.0")
    assert mgf.common.app_name() == "second"
    assert mgf.common.app_version() == "2.0"


def test_default_identity_constants_exist() -> None:
    """Pin that the module declares the placeholder defaults at
    import time. We can't assert their VALUE here because a prior
    test may have called ``_set_identity`` and mutated the module
    state — pytest order is not isolation. The observability test
    suite uses a per-test save/restore fixture (in its conftest)
    when it needs to set a specific app name without leaking."""
    assert hasattr(_identity, "_APP_NAME")
    assert hasattr(_identity, "_APP_VERSION")
    # Both are str — type pinned so a refactor that accidentally
    # changes them to None / int is caught.
    assert isinstance(_identity._APP_NAME, str)
    assert isinstance(_identity._APP_VERSION, str)


def test_module_version_is_present() -> None:
    """The package itself carries a version string, distinct from
    the consumer-app version set via ``bootstrap(app_version=...)``."""
    assert isinstance(mgf.common.__version__, str)
    assert len(mgf.common.__version__) > 0


def test_unconfigured_warning_fires_once_on_app_name(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    """FEEDBACK API-08: reading identity before bootstrap() emits
    ``UnconfiguredWarning`` exactly once per process.

    Reset the module flags via monkeypatch so this test sees the
    pristine state. ``filterwarnings = ["error"]`` (pyproject) would
    otherwise make ``warnings.warn`` raise — we use ``catch_warnings``
    to capture without raising.
    """
    # Reset all four pieces of identity state so the test sees a
    # pristine "never configured" world even if a prior test in this
    # module called _set_identity.
    monkeypatch.setattr(_identity, "_CONFIGURED", False)
    monkeypatch.setattr(_identity, "_UNCONFIGURED_WARNED", False)
    monkeypatch.setattr(_identity, "_APP_NAME", "app")
    monkeypatch.setattr(_identity, "_APP_VERSION", "unknown")

    with warnings.catch_warnings(record=True) as caught:
        warnings.simplefilter("always", _identity.UnconfiguredWarning)
        # First read: warning fires.
        first = mgf.common.app_name()
        # Second read (different func): warning DOES NOT fire (one-shot).
        _ = mgf.common.app_version()
        # Third read (same func): also no fire.
        _ = mgf.common.app_name()

    assert first == "app", "should return the placeholder default"
    matching = [w for w in caught if issubclass(w.category, _identity.UnconfiguredWarning)]
    assert len(matching) == 1, (
        f"FEEDBACK API-08: expected exactly one UnconfiguredWarning, got {len(matching)}"
    )
    msg = str(matching[0].message).lower()
    assert "bootstrap" in msg


def test_unconfigured_warning_does_not_fire_after_set_identity(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    """Once identity is set, ``app_name()`` MUST be silent."""
    monkeypatch.setattr(_identity, "_CONFIGURED", False)
    monkeypatch.setattr(_identity, "_UNCONFIGURED_WARNED", False)

    _identity._set_identity(app_name="silent-test", app_version="0.0.0")

    with warnings.catch_warnings(record=True) as caught:
        warnings.simplefilter("always", _identity.UnconfiguredWarning)
        _ = mgf.common.app_name()
        _ = mgf.common.app_version()

    matching = [w for w in caught if issubclass(w.category, _identity.UnconfiguredWarning)]
    assert matching == [], (
        f"FEEDBACK API-08: configured identity MUST silence the warning; "
        f"got {len(matching)} warnings"
    )


def test_unconfigured_warning_is_user_warning_subclass() -> None:
    """The warning category MUST be a UserWarning subclass so it
    shows by default but does not break test harnesses with
    ``filterwarnings = ['error']`` against the project's own
    categories. See API-08 rationale.
    """
    assert issubclass(_identity.UnconfiguredWarning, UserWarning)


def test_namespace_package_shape() -> None:
    """``mgf`` is a PEP 420 namespace package — it has no
    ``__init__.py`` of its own. ``mgf.common`` is a regular
    subpackage. This test pins the shape so a refactor that
    accidentally drops the namespace property is caught."""
    import mgf

    # The hallmark of a PEP 420 namespace package: ``__file__`` is
    # absent (no real ``__init__.py`` to point at).
    assert not hasattr(mgf, "__file__") or mgf.__file__ is None
