"""Tests for webhook/event dispatch primitives."""

from swarmpay.webhooks import WebhookDispatcher, serialize_event, sign_event


def test_dispatcher_collects_events_and_calls_handler(tmp_path):
    seen = []

    def handler(event):
        seen.append(event)

    dispatcher = WebhookDispatcher(handler=handler, storage_path=str(tmp_path / "webhooks.json"))
    event = dispatcher.emit("escrow.funded", {"escrow_id": "escrow:1"})

    assert event.event_type == "escrow.funded"
    assert dispatcher.events[-1] == event
    assert seen[0] == event


def test_dispatcher_delivers_matching_subscription_with_signature(tmp_path):
    dispatcher = WebhookDispatcher(storage_path=str(tmp_path / "webhooks.json"))
    subscription = dispatcher.subscribe(
        target_url="https://example.com/webhook",
        secret="secret123",
        event_types=["escrow.funded"],
        max_retries=1,
    )
    captured = {}

    def sender(sub, event):
        captured["subscription"] = sub
        captured["signature"] = sign_event(event, sub.secret)
        captured["body"] = serialize_event(event)
        return 200

    event = dispatcher.emit("escrow.funded", {"escrow_id": "escrow:1"})
    results = dispatcher.deliver_event(event, sender=sender)

    assert captured["subscription"] == subscription
    assert captured["signature"].startswith("sha256=")
    assert b'"event_type":"escrow.funded"' in captured["body"]
    assert results[0].success is True


def test_dispatcher_persists_subscriptions_and_events(tmp_path):
    storage_path = str(tmp_path / "webhooks.json")
    dispatcher = WebhookDispatcher(storage_path=storage_path)
    dispatcher.subscribe("https://example.com/webhook", "secret123", ["escrow.funded"])
    dispatcher.emit("escrow.funded", {"escrow_id": "escrow:1"})

    reloaded = WebhookDispatcher(storage_path=storage_path)

    assert len(reloaded.subscriptions) == 1
    assert len(reloaded.events) == 1
    assert reloaded.events[0].event_type == "escrow.funded"


def test_dispatcher_schedules_retry_with_backoff(tmp_path):
    dispatcher = WebhookDispatcher(storage_path=str(tmp_path / "webhooks.json"), retry_base_seconds=10)
    dispatcher.subscribe(
        target_url="https://example.com/webhook",
        secret="secret123",
        max_retries=2,
    )

    def sender(sub, event):
        raise RuntimeError("network down")

    event = dispatcher.emit("escrow.funded", {"escrow_id": "escrow:1"})
    results = dispatcher.deliver_event(event, sender=sender, now=100.0)

    assert results[0].success is False
    assert results[0].attempt == 1
    assert results[0].next_attempt_at == 110.0


def test_dispatcher_retries_pending_delivery(tmp_path):
    dispatcher = WebhookDispatcher(storage_path=str(tmp_path / "webhooks.json"), retry_base_seconds=5)
    dispatcher.subscribe(
        target_url="https://example.com/webhook",
        secret="secret123",
        max_retries=3,
    )
    attempts = {"count": 0}

    def failing_sender(sub, event):
        attempts["count"] += 1
        raise RuntimeError("network down")

    event = dispatcher.emit("escrow.funded", {"escrow_id": "escrow:1"})
    dispatcher.deliver_event(event, sender=failing_sender, now=100.0)

    def succeeding_sender(sub, event):
        attempts["count"] += 1
        return 202

    retried = dispatcher.retry_pending(sender=succeeding_sender, now=105.0)

    assert attempts["count"] == 2
    assert retried[0].attempt == 2
    assert retried[0].success is True
