import asyncio
import time
from collections import deque
from types import SimpleNamespace


class _DummyBreaker:
    def check_and_record(self, _priority):
        return True, "ok"


class _FakeMeshtasticTransport:
    NAME = "meshtastic"

    def __init__(self, can_reach: bool = True, send_ok: bool = True):
        self._can_reach = can_reach
        self._send_ok = send_ok
        self.sent = []

    def can_reach(self, _envelope):
        return self._can_reach

    def send(self, envelope, _credentials):
        from services.mesh.mesh_router import TransportResult

        self.sent.append(envelope)
        return TransportResult(self._send_ok, self.NAME, "sent")


class _FakeMeshRouter:
    def __init__(self, meshtastic):
        self.meshtastic = meshtastic
        self.breakers = {"meshtastic": _DummyBreaker()}
        self.route_called = False

    def route(self, _envelope, _credentials):
        self.route_called = True
        return []


def _valid_body(**overrides):
    body = {
        "destination": "!a0cc7a80",
        "message": "hello mesh",
        "sender_id": "!sb_sender",
        "node_id": "!sb_sender",
        "public_key": "pub",
        "public_key_algo": "Ed25519",
        "signature": "sig",
        "sequence": 1,
        "protocol_version": "1",
        "channel": "LongFast",
        "priority": "normal",
        "ephemeral": False,
        "transport_lock": "meshtastic",
        "credentials": {"mesh_region": "US"},
    }
    body.update(overrides)
    return body


def test_meshtastic_transport_lock_stays_on_public_direct_path(monkeypatch):
    import main
    from services.mesh import mesh_router as mesh_router_mod
    from services.sigint_bridge import sigint_grid
    from httpx import ASGITransport, AsyncClient

    fake_meshtastic = _FakeMeshtasticTransport(can_reach=True, send_ok=True)
    fake_router = _FakeMeshRouter(fake_meshtastic)
    fake_bridge = SimpleNamespace(messages=deque(maxlen=10))

    monkeypatch.setattr(main, "_verify_signed_write", lambda **_: (True, "ok"))
    monkeypatch.setattr(main, "_check_throttle", lambda *_: (True, "ok"))
    monkeypatch.setattr(mesh_router_mod, "mesh_router", fake_router)
    monkeypatch.setattr(sigint_grid, "mesh", fake_bridge)

    async def _run():
        async with AsyncClient(transport=ASGITransport(app=main.app), base_url="http://test") as ac:
            response = await ac.post("/api/mesh/send", json=_valid_body())
            return response.json()

    result = asyncio.run(_run())

    assert result["ok"] is True
    assert result["routed_via"] == "meshtastic"
    assert "public node-targeted path" in result["route_reason"]
    assert fake_router.route_called is False
    assert len(fake_meshtastic.sent) == 1
    assert fake_meshtastic.sent[0].destination == "!a0cc7a80"
    assert fake_bridge.messages[0]["from"] == "!0779e8b8"


def test_meshtastic_transport_lock_does_not_fallback_when_unreachable(monkeypatch):
    import main
    from services.mesh import mesh_router as mesh_router_mod
    from httpx import ASGITransport, AsyncClient

    fake_meshtastic = _FakeMeshtasticTransport(can_reach=False, send_ok=False)
    fake_router = _FakeMeshRouter(fake_meshtastic)

    monkeypatch.setattr(main, "_verify_signed_write", lambda **_: (True, "ok"))
    monkeypatch.setattr(main, "_check_throttle", lambda *_: (True, "ok"))
    monkeypatch.setattr(mesh_router_mod, "mesh_router", fake_router)

    async def _run():
        async with AsyncClient(transport=ASGITransport(app=main.app), base_url="http://test") as ac:
            response = await ac.post("/api/mesh/send", json=_valid_body(message="x" * 10))
            return response.json()

    result = asyncio.run(_run())

    assert result["ok"] is False
    assert result["routed_via"] == ""
    assert fake_router.route_called is False
    assert fake_meshtastic.sent == []
    assert result["results"] == [
        {
            "ok": False,
            "transport": "meshtastic",
            "detail": "Message exceeds Meshtastic payload limit",
        }
    ]


def test_meshtastic_transport_lock_allows_two_messages_per_minute(monkeypatch):
    import main

    node_id = "!sb_meshrate"
    now = time.time()
    main._node_throttle[node_id] = {
        "last_send": now - 31,
        "daily_count": 0,
        "daily_reset": now,
        "first_seen": now,
    }

    ok_first, _reason_first = main._check_throttle(node_id, "normal", "meshtastic")
    ok_second, reason_second = main._check_throttle(node_id, "normal", "meshtastic")

    assert ok_first is True
    assert ok_second is False
    assert "1 message per 30s" in reason_second


def test_private_trust_tier_skips_public_transports(monkeypatch):
    from services.mesh import mesh_router
    from services.mesh.mesh_router import MeshEnvelope, MeshRouter, Priority, TransportResult

    monkeypatch.setattr(mesh_router, "_supervisor_verified_trust_tier", lambda: "private_strong")

    class _FakeTransport:
        def __init__(self, name):
            self.NAME = name
            self.sent = []

        def can_reach(self, _envelope):
            return True

        def send(self, envelope, _credentials):
            self.sent.append(envelope)
            return TransportResult(True, self.NAME, "sent")

    router = MeshRouter()
    router.aprs = _FakeTransport("aprs")
    router.meshtastic = _FakeTransport("meshtastic")
    router.internet = _FakeTransport("internet")
    router.transports = [router.aprs, router.meshtastic, router.internet]

    envelope = MeshEnvelope(
        sender_id="!sb_sender",
        destination="broadcast",
        priority=Priority.NORMAL,
        payload="private payload",
        trust_tier="private_strong",
    )

    results = router.route(envelope, {})

    assert [r.transport for r in results] == ["policy"]
    assert router.aprs.sent == []
    assert router.meshtastic.sent == []
    assert len(router.internet.sent) == 0


def test_private_route_recognizes_tor_arti_and_falls_back_to_internet(monkeypatch):
    from services.mesh import mesh_router
    from services.mesh.mesh_router import MeshEnvelope, MeshRouter, Priority, TransportResult

    monkeypatch.setattr(mesh_router, "_supervisor_verified_trust_tier", lambda: "private_strong")

    class _FakeTransport:
        def __init__(self, name, ok=True):
            self.NAME = name
            self.ok = ok
            self.sent = []

        def can_reach(self, _envelope):
            return True

        def send(self, envelope, _credentials):
            self.sent.append(envelope)
            return TransportResult(self.ok, self.NAME, "sent" if self.ok else "stub")

    router = MeshRouter()
    router.aprs = _FakeTransport("aprs")
    router.meshtastic = _FakeTransport("meshtastic")
    router.tor_arti = _FakeTransport("tor_arti", ok=False)
    router.internet = _FakeTransport("internet", ok=True)
    router.transports = [router.aprs, router.meshtastic, router.tor_arti, router.internet]

    envelope = MeshEnvelope(
        sender_id="!sb_sender",
        destination="broadcast",
        priority=Priority.NORMAL,
        payload="private payload",
        trust_tier="private_strong",
    )

    results = router.route(envelope, {})

    assert [r.transport for r in results] == ["tor_arti", "policy"]
    assert router.aprs.sent == []
    assert router.meshtastic.sent == []
    assert len(router.tor_arti.sent) == 1
    assert len(router.internet.sent) == 0


def test_private_tier_blocks_meshtastic_transport_lock(monkeypatch):
    """C-2 fix: transport_lock=meshtastic must be rejected when trust_tier is private."""
    import main
    from services.mesh import mesh_router as mesh_router_mod
    from services import wormhole_supervisor
    from httpx import ASGITransport, AsyncClient

    fake_meshtastic = _FakeMeshtasticTransport(can_reach=True, send_ok=True)
    fake_router = _FakeMeshRouter(fake_meshtastic)

    monkeypatch.setattr(main, "_verify_signed_write", lambda **_: (True, "ok"))
    monkeypatch.setattr(main, "_check_throttle", lambda *_: (True, "ok"))
    monkeypatch.setattr(mesh_router_mod, "mesh_router", fake_router)
    monkeypatch.setattr(wormhole_supervisor, "get_transport_tier", lambda: "private_transitional")

    async def _run():
        async with AsyncClient(transport=ASGITransport(app=main.app), base_url="http://test") as ac:
            response = await ac.post("/api/mesh/send", json=_valid_body())
            return response.json()

    result = asyncio.run(_run())

    assert result["ok"] is False
    assert "Private-tier content cannot be sent over Meshtastic" in result["results"][0]["detail"]
    assert fake_meshtastic.sent == []
    assert fake_router.route_called is False


def test_envelope_trust_tier_set_from_wormhole_state(monkeypatch):
    """C-1 fix: MeshEnvelope.trust_tier must reflect actual Wormhole transport tier."""
    import main
    from services.mesh import mesh_router as mesh_router_mod
    from services import wormhole_supervisor
    from services.sigint_bridge import sigint_grid
    from httpx import ASGITransport, AsyncClient

    captured_envelopes = []

    class _CapturingRouter:
        def __init__(self):
            self.meshtastic = _FakeMeshtasticTransport(can_reach=True, send_ok=True)
            self.breakers = {"meshtastic": _DummyBreaker()}

        def route(self, envelope, _credentials):
            from services.mesh.mesh_router import TransportResult

            captured_envelopes.append(envelope)
            return [TransportResult(True, "internet", "sent")]

    fake_router = _CapturingRouter()
    fake_bridge = SimpleNamespace(messages=deque(maxlen=10))

    monkeypatch.setattr(main, "_verify_signed_write", lambda **_: (True, "ok"))
    monkeypatch.setattr(main, "_check_throttle", lambda *_: (True, "ok"))
    monkeypatch.setattr(mesh_router_mod, "mesh_router", fake_router)
    monkeypatch.setattr(sigint_grid, "mesh", fake_bridge)
    monkeypatch.setattr(wormhole_supervisor, "get_transport_tier", lambda: "private_transitional")

    body = _valid_body()
    del body["transport_lock"]  # no lock — use normal routing

    async def _run():
        async with AsyncClient(transport=ASGITransport(app=main.app), base_url="http://test") as ac:
            response = await ac.post("/api/mesh/send", json=body)
            return response.json()

    result = asyncio.run(_run())

    assert result["ok"] is True
    assert len(captured_envelopes) == 1
    assert captured_envelopes[0].trust_tier == "private_transitional"
