"""Formatter registry for the Phase-1+ mesh-message dispatch path.

Phase-1: earthquake_event registered (quake.py).
All other categories still fall through to the legacy compose_mesh_message
Mode-B path.

Usage (future phases):
    from meshai.notifications.formatters import register

    @register("earthquake_event")
    def _fmt_quake(event, *, now: float, budget: int) -> str:
        ...
"""

from typing import Callable, Optional

# Populated by per-category formatter modules imported below.
FORMATTERS: dict[str, Callable] = {}


def register(category: str, fn: Callable) -> Callable:
    """Register a formatter callable for a category (or family toggle name).

    Decorates or calls directly:
        register("earthquake_event", my_fn)
        @register("earthquake_event")
        def my_fn(...): ...
    """
    FORMATTERS[category] = fn
    return fn


def get_formatter(category: str) -> Optional[Callable]:
    """Return the formatter for *category*, or None if none is registered.

    Resolution order:
      1. Direct category match in FORMATTERS.
      2. Family/toggle fallback: look up the category's toggle name via
         get_toggle(), then check FORMATTERS for that toggle key.
      3. None — caller falls through to legacy Mode-B composition.
    """
    fn = FORMATTERS.get(category)
    if fn is not None:
        return fn
    # Family fallback — mirrors _category_label()'s toggle lookup in composer.py.
    try:
        from meshai.notifications.categories import get_toggle
        tog = get_toggle(category)
        if tog:
            fn = FORMATTERS.get(tog)
            if fn is not None:
                return fn
    except Exception:
        pass
    return None


# ── Phase-1 registrations ────────────────────────────────────────────────────
# Import triggers the @register decorator (or explicit register() call) in
# each formatter module.  Add one import per migrated category.

from meshai.notifications.formatters import quake as _quake_fmt_mod  # noqa: E402,F401
register("earthquake_event", _quake_fmt_mod.format)

from meshai.notifications.formatters import avalanche as _avy_fmt_mod  # noqa: E402,F401
register("avalanche_warning", _avy_fmt_mod.format)
register("avalanche_watch", _avy_fmt_mod.format)

# SWPC: geomagnetic_storm (swpc_kindex / native G-scale), rf_propagation_alert
# (swpc_alerts flare / native R-scale), and solar_radiation_storm (native
# S-scale proton/radiation-storm events). All three share one formatter that
# dispatches internally (geomag/flare on `driver`, proton on `scale_code`
# prefix "S" — see formatters/swpc.py for why).
from meshai.notifications.formatters import swpc as _swpc_fmt_mod  # noqa: E402,F401
register("geomagnetic_storm", _swpc_fmt_mod.format)
register("rf_propagation_alert", _swpc_fmt_mod.format)
register("solar_radiation_storm", _swpc_fmt_mod.format)

# Phase-2: NWS weather alerts (weather_warning + weather_statement +
# weather_watch + weather_advisory).  All four categories share the same
# NWS formatter; category controls the event-type string / emoji, not
# the render path.
from meshai.notifications.formatters import nws as _nws_fmt_mod  # noqa: E402,F401
register("weather_warning",  _nws_fmt_mod.format)
register("weather_statement", _nws_fmt_mod.format)
register("weather_watch",    _nws_fmt_mod.format)
register("weather_advisory", _nws_fmt_mod.format)

# Phase-2: incident / roads categories.
# One formatter handles all four; event.category drives the render path.
from meshai.notifications.formatters import incident as _incident_fmt_mod  # noqa: E402,F401
register("work_zone",          _incident_fmt_mod.format)
register("road_incident",      _incident_fmt_mod.format)
register("road_closure",       _incident_fmt_mod.format)
register("traffic_congestion", _incident_fmt_mod.format)

# Phase-3: USGS NWIS stream-gauge hydro. The Central nwis path maps every
# `central.hydro.*` envelope to the flat category `stream_flow` (see
# central.consumer.map_category / category_from_subject). Native env/usgs.py
# categories (stream_flood_warning / stream_high_water) reuse the same
# formatter — env/usgs.py's to_event() now populates event.data with the same
# canonical schema (gauge_name/threshold_state/stage_ft/flow_cfs/lat/lon) the
# Central path writes, so the wire renders identically for both sources. Forced
# onto the live render path unconditionally via cutover.NATIVE_ALWAYS_DECIDE
# (see gating/__init__.py note) since Central never emits these two strings.
from meshai.notifications.formatters import hydro as _hydro_fmt_mod  # noqa: E402,F401
register("stream_flow", _hydro_fmt_mod.format)
register("stream_flood_warning", _hydro_fmt_mod.format)
register("stream_high_water", _hydro_fmt_mod.format)

# Phase-3b: WFIGS wildfire. The Central wfigs_handler emits THREE explicit
# categories, all under toggle "fire": `wildfire_declared` (first-sight New,
# cases i/ii), `wildfire_incident` (growth/containment Update, case iii — the
# envelope-derived category with no override), and `wildfire_closed` (tombstone
# all-clear). One formatter handles all three; event.category selects the wire
# shape (wildfire_closed -> all-clear, else incident/growth). Registered under
# the explicit strings — NOT the "fire" toggle — so FIRMS categories
# (wildfire_hotspot / new_ignition / unattributed_hotspot_cluster /
# wildfire_growth) are NOT captured by the family fallback. FIRMS is deferred.
from meshai.notifications.formatters import fire as _fire_fmt_mod  # noqa: E402,F401
register("wildfire_declared", _fire_fmt_mod.format)
register("wildfire_incident", _fire_fmt_mod.format)
register("wildfire_closed",   _fire_fmt_mod.format)

# Phase-3c: FIRMS fusion broadcasts. `wildfire_spotting` / `wildfire_halted`
# have their own terse wires -> formatters/firms.py, registered below.
# `wildfire_growth` is NOT registered here (chore/ripout-2dii): its events are
# always fully precomposed (env/firms.py sets `_meshai_precomposed=True` +
# `title=<wire>`), and it is not in `cutover.NATIVE_ALWAYS_DECIDE`, so
# compose_mesh_message's formatter-invocation branch
# (notifications/renderers/composer.py:343-347) can never reach a registered
# formatter for this category live -- the precomposed-title bypass always wins
# first. wildfire_growth's live wire comes from `env.fire_render._render`,
# called directly by `env.fire_fusion._handle_pass_boundary`. A prior
# registration here (reusing the fire formatter) was dead code kept from the
# Phase-3c migration bake and has been removed; see TestRegistration in
# tests/test_firms_refactor.py for the regression guard.
from meshai.notifications.formatters import firms as _firms_fmt_mod  # noqa: E402,F401
register("wildfire_spotting", _firms_fmt_mod.format)
register("wildfire_halted",   _firms_fmt_mod.format)

# IPAWS civil alerts (env/ipaws.py). One headline-forward formatter handles all
# six emergency_* categories; event.category selects only the leading emoji.
from meshai.notifications.formatters import ipaws as _ipaws_fmt_mod  # noqa: E402,F401
register("emergency_evacuation", _ipaws_fmt_mod.format)
register("emergency_civil",      _ipaws_fmt_mod.format)
register("emergency_amber",      _ipaws_fmt_mod.format)
register("emergency_law",        _ipaws_fmt_mod.format)
register("emergency_911_outage", _ipaws_fmt_mod.format)
register("emergency_hazmat",     _ipaws_fmt_mod.format)
