"""The feature catalogue — content pins, read from the notebook by tag.

The catalogue is the master's authored content: it lives in the notebook's
``topic-bank`` cell, and these tests exec that cell rather than importing a
module. The published RATES are not here — they are the vendor's record, in
``genesyscalc/ratecard.py``, pinned by ``test_rate_card.py``.
"""

from __future__ import annotations

from typing import Any

from genesyscalc.ratecard import METERS

EXPECTED_KEYS = (
    "agent_copilot_named",
    "speech_and_text_analytics_named",
    "bots_voice",
    "virtual_agent",
    "agentic_virtual_agent",
    "ai_summary_and_insights",
    "ai_scoring",
    "bots_digital",
    "ai_translate",
    "predictive_routing",
    "whatsapp_messaging",
    "genesys_cloud_copilot",
    "predictive_engagement",
)


def test_catalogue_size(features: tuple[Any, ...]) -> None:
    assert len(features) == 13


def test_catalogue_keys_and_order(features: tuple[Any, ...]) -> None:
    """Keys are stable identities — widgets, notes and the export key off them."""
    assert tuple(f.key for f in features) == EXPECTED_KEYS


def test_keys_are_unique(features: tuple[Any, ...]) -> None:
    assert len({f.key for f in features}) == len(features)


def test_every_catalogue_key_is_a_published_meter(features: tuple[Any, ...]) -> None:
    """The cross-layer tie: a rename here silently unprices a feature."""
    for f in features:
        assert f.key in METERS, f"{f.key} is not a published Genesys meter"


def test_every_feature_carries_its_content(features: tuple[Any, ...]) -> None:
    for f in features:
        assert f.title.strip(), f"{f.key} has no title"
        assert f.description.strip(), f"{f.key} has no description"
        assert f.ask.strip(), f"{f.key} has no sizing question"
        assert f.ask.strip().endswith(("?", ")")), (
            f"{f.key}: `ask` should read as a question to put to the client"
        )


def test_titles_are_distinct(features: tuple[Any, ...]) -> None:
    """Titles become widget labels; duplicates collide in the sidebar."""
    titles = [f.title for f in features]
    assert len(set(titles)) == len(titles)


def test_default_scenario_is_a_coherent_starting_point(
    features: tuple[Any, ...],
) -> None:
    """Headless nbconvert takes every widget at its seed, so the defaults
    must form a scenario the gate can pass — and a plausible one to show."""
    default_on = {f.key for f in features if f.default_on}
    assert len(default_on) == 8
    assert "agent_copilot_named" in default_on
    assert "bots_voice" in default_on
    # Copilot on by default means the summary line must be visible but free —
    # that pairing is the published exclusion the calculator demonstrates.
    assert "ai_summary_and_insights" in default_on


def test_catalogue_uses_a_schema_not_loose_dicts(features: tuple[Any, ...]) -> None:
    """Content is structured, so the notebook and tests agree on its shape."""
    first = features[0]
    assert type(first).__name__ == "Feature"
    assert {"key", "title", "description", "ask", "default_on"} <= set(
        type(first).__dataclass_fields__
    )


def test_catalogue_cell_is_self_contained(catalogue_ns: dict[str, Any]) -> None:
    """It execs with no setup cell — the conftest fixture proves it, but pin
    the contract explicitly so a stray dependency is caught here."""
    assert "FEATURES" in catalogue_ns
    assert "Feature" in catalogue_ns
