"""Tests for Phase 2a coverage-bbox wiring.

Verifies that:
1. Each of the four simple adapters respects the `coverage` kwarg — derived
   scope wins when coverage is provided, config field is the fallback when None.
2. EnvironmentalStore._coverage_for returns a dict for a valid bbox and None
   for an empty bbox.
"""

from __future__ import annotations

from unittest.mock import MagicMock

import pytest

from meshai.coverage import resolve_adapter_coverage

# ---------------------------------------------------------------------------
# Shared reference bbox (Magic Valley / south-central Idaho)
# ---------------------------------------------------------------------------
IDAHO_BOX = [-116.5, 42.0, -112.0, 44.0]


# ---------------------------------------------------------------------------
# Helpers — derive the expected coverage dicts from the module itself
# ---------------------------------------------------------------------------

def _cov(adapter_name: str, bbox: list = None) -> dict | None:
    return resolve_adapter_coverage(adapter_name, bbox or IDAHO_BOX, "native")


# ===========================================================================
# usgs_quake
# ===========================================================================

def _quake_cfg(bbox=None):
    cfg = MagicMock()
    cfg.feed_url = "https://example.test/feed.geojson"
    cfg.min_magnitude = 2.5
    cfg.bbox = bbox if bbox is not None else [-115.5, 42.0, -110.0, 45.2]
    cfg.region = "magic_valley"
    cfg.tick_seconds = 300
    return cfg


def test_usgs_quake_coverage_wins():
    """When coverage is provided, adapter._bbox comes from coverage['bbox']."""
    from meshai.env.usgs_quake import USGSQuakeAdapter
    cov = _cov("usgs_quake")
    assert cov is not None
    adapter = USGSQuakeAdapter(_quake_cfg(), coverage=cov)
    assert adapter._bbox == cov["bbox"]


def test_usgs_quake_fallback_to_config():
    """When coverage=None, adapter._bbox falls back to config.bbox."""
    from meshai.env.usgs_quake import USGSQuakeAdapter
    cfg = _quake_cfg(bbox=[-115.5, 42.0, -110.0, 45.2])
    adapter = USGSQuakeAdapter(cfg, coverage=None)
    assert adapter._bbox == cfg.bbox


# ===========================================================================
# roads511
# ===========================================================================

def _roads_cfg(bbox=None):
    cfg = MagicMock()
    cfg.api_key = ""
    cfg.base_url = ""
    cfg.endpoints = ["/get/event"]
    cfg.bbox = bbox if bbox is not None else [-115.0, 42.5, -111.0, 44.0]
    cfg.tick_seconds = 300
    return cfg


def test_roads511_coverage_wins():
    """When coverage is provided, adapter._bbox comes from coverage['bbox']."""
    from meshai.env.roads511 import Roads511Adapter
    cov = _cov("roads511")
    assert cov is not None
    adapter = Roads511Adapter(_roads_cfg(), coverage=cov)
    assert adapter._bbox == cov["bbox"]


def test_roads511_fallback_to_config():
    """When coverage=None, adapter._bbox falls back to config.bbox."""
    from meshai.env.roads511 import Roads511Adapter
    cfg = _roads_cfg(bbox=[-115.0, 42.5, -111.0, 44.0])
    adapter = Roads511Adapter(cfg, coverage=None)
    assert adapter._bbox == cfg.bbox


# ===========================================================================
# wzdx
# ===========================================================================

def _wzdx_cfg(states=None, bbox=None):
    cfg = MagicMock()
    cfg.base_url = ""
    cfg.registry_url = "https://example.test/registry.json"
    cfg.registry_ttl = 21600
    cfg.tick_seconds = 300
    cfg.states = states if states is not None else ["ID"]
    cfg.bbox = bbox if bbox is not None else []
    return cfg


def test_wzdx_coverage_wins():
    """When coverage is provided, both _states and _bbox come from coverage."""
    from meshai.env.wzdx import WZDxAdapter
    cov = _cov("wzdx")
    assert cov is not None
    assert "states" in cov
    assert "bbox" in cov
    adapter = WZDxAdapter(_wzdx_cfg(), coverage=cov)
    assert adapter._states == cov["states"]
    assert adapter._bbox == cov["bbox"]


def test_wzdx_fallback_to_config():
    """When coverage=None, _states and _bbox fall back to config fields."""
    from meshai.env.wzdx import WZDxAdapter
    cfg = _wzdx_cfg(states=["ID", "MT"], bbox=[-116.0, 42.0, -111.0, 45.0])
    adapter = WZDxAdapter(cfg, coverage=None)
    assert "ID" in adapter._states
    assert "MT" in adapter._states
    assert adapter._bbox == cfg.bbox


def test_wzdx_empty_coverage_states_wins():
    """Empty derived states list still wins over config (coverage governs)."""
    from meshai.env.wzdx import WZDxAdapter
    # Provide a bbox that maps to no states (unlikely but logically possible)
    empty_states_cov = {"states": [], "bbox": [-1.0, 0.0, 1.0, 1.0]}
    cfg = _wzdx_cfg(states=["ID"])
    adapter = WZDxAdapter(cfg, coverage=empty_states_cov)
    assert adapter._states == []  # coverage governs even when empty


# ===========================================================================
# avalanche
# ===========================================================================

def _avy_cfg(center_ids=None):
    cfg = MagicMock()
    cfg.center_ids = center_ids if center_ids is not None else ["SNFAC"]
    cfg.tick_seconds = 1800
    cfg.season_months = [12, 1, 2, 3, 4]
    return cfg


def test_avalanche_coverage_wins():
    """When coverage is provided, adapter._center_ids comes from coverage['center_ids']."""
    from meshai.env.avalanche import AvalancheAdapter
    cov = _cov("avalanche")
    assert cov is not None
    assert "center_ids" in cov
    adapter = AvalancheAdapter(_avy_cfg(), coverage=cov)
    assert adapter._center_ids == cov["center_ids"]


def test_avalanche_fallback_to_config():
    """When coverage=None, adapter._center_ids falls back to config.center_ids."""
    from meshai.env.avalanche import AvalancheAdapter
    cfg = _avy_cfg(center_ids=["SNFAC", "PAC"])
    adapter = AvalancheAdapter(cfg, coverage=None)
    assert adapter._center_ids == ["SNFAC", "PAC"]


def test_avalanche_empty_coverage_center_ids_wins():
    """Empty derived center_ids list wins over config (coverage governs)."""
    from meshai.env.avalanche import AvalancheAdapter
    # A bbox in the Pacific would yield no centers
    no_center_cov = {"center_ids": []}
    cfg = _avy_cfg(center_ids=["SNFAC"])
    adapter = AvalancheAdapter(cfg, coverage=no_center_cov)
    assert adapter._center_ids == []  # coverage governs even when empty


# ===========================================================================
# EnvironmentalStore._coverage_for
# ===========================================================================

def _make_env_cfg(enabled=False):
    """Minimal EnvironmentalConfig mock with all adapters disabled."""
    env_cfg = MagicMock()
    for attr in ("nws", "swpc", "ducting", "fires", "avalanche", "usgs",
                 "usgs_quake", "traffic", "roads511", "wzdx", "satpass", "firms"):
        sub = MagicMock()
        sub.enabled = False
        sub.feed_source = "central"
        setattr(env_cfg, attr, sub)
    env_cfg.nws_zones = []
    return env_cfg


def test_coverage_for_returns_dict_for_valid_bbox():
    """_coverage_for returns a dict when coverage_bbox is a valid box."""
    from meshai.env.store import EnvironmentalStore
    env_cfg = _make_env_cfg()
    store = EnvironmentalStore(config=env_cfg, coverage_bbox=IDAHO_BOX)
    result = store._coverage_for("usgs_quake")
    assert isinstance(result, dict)
    assert "bbox" in result
    assert result["bbox"] == IDAHO_BOX


def test_coverage_for_returns_none_for_empty_bbox():
    """_coverage_for returns None when coverage_bbox is empty."""
    from meshai.env.store import EnvironmentalStore
    env_cfg = _make_env_cfg()
    store = EnvironmentalStore(config=env_cfg, coverage_bbox=[])
    result = store._coverage_for("usgs_quake")
    assert result is None


def test_coverage_for_avalanche_returns_center_ids():
    """_coverage_for('avalanche') returns center_ids for a valid Idaho box."""
    from meshai.env.store import EnvironmentalStore
    env_cfg = _make_env_cfg()
    store = EnvironmentalStore(config=env_cfg, coverage_bbox=IDAHO_BOX)
    result = store._coverage_for("avalanche")
    assert result is not None
    assert "center_ids" in result
    assert isinstance(result["center_ids"], list)
    # SNFAC (Sawtooth) covers south-central Idaho — should be in the result
    assert "SNFAC" in result["center_ids"]


def test_coverage_for_wzdx_returns_states_and_bbox():
    """_coverage_for('wzdx') returns both states and bbox."""
    from meshai.env.store import EnvironmentalStore
    env_cfg = _make_env_cfg()
    store = EnvironmentalStore(config=env_cfg, coverage_bbox=IDAHO_BOX)
    result = store._coverage_for("wzdx")
    assert result is not None
    assert "states" in result and "bbox" in result
    assert "ID" in result["states"]
