"""BaseSender and S3Sender — how responses are dispatched to the message bus
and how S3Sender handles upload success, failure, and missing URL.
"""

import json

from messagebus import Event

from boh.report.sender import BaseSender, S3Sender
from testutil import CapturingHttpClient


# --- BaseSender dispatch ---------------------------------------------------


def test_base_sender_publishes_event_for_async_dispatch(message_bus):
    sender = BaseSender()
    sender.send(message_bus, Event("E", None, b""), {"result": 1}, {"report-id": "r1"})

    assert len(message_bus.published_events) == 1
    assert len(message_bus.event_replies) == 0


def test_base_sender_replies_event_for_sync_dispatch(message_bus):
    sender = BaseSender()
    sender.send(message_bus, Event("E", None, b""), {"result": 1}, {"report-id": "r1", "sync": True})

    assert len(message_bus.event_replies) == 1
    assert len(message_bus.published_events) == 0


def test_base_sender_send_wraps_data_with_report_id(message_bus):
    sender = BaseSender()
    sender.send(message_bus, Event("E", None, b""), {"rows": [1, 2]}, {"report-id": "r42"})

    dispatched = json.loads(message_bus.published_events[0].data)
    assert dispatched == {"report-id": "r42", "data": {"rows": [1, 2]}}


def test_base_sender_send_error_dispatches_event_data_unchanged(message_bus):
    sender = BaseSender()
    error_data = {"error": True, "report-id": "r1", "report-error-message": "oops"}
    sender.send_error(message_bus, Event("E", None, b""), error_data, {"report-id": "r1"})

    dispatched = json.loads(message_bus.published_events[0].data)
    assert dispatched == error_data


# --- S3Sender upload behaviors --------------------------------------------


def test_s3_sender_uploads_data_and_clears_inline_payload_on_success(message_bus):
    http = CapturingHttpClient()
    sender = S3Sender(http_client=http)
    payload = {"report-id": "r1", "response-upload-url": "https://s3.example.com/upload"}

    sender.send(message_bus, Event("E", None, b""), {"rows": [1, 2, 3]}, payload)

    assert http.calls[0]["url"] == "https://s3.example.com/upload"
    assert http.calls[0]["json"] == {"rows": [1, 2, 3]}
    dispatched = json.loads(message_bus.published_events[0].data)
    assert dispatched["data"] == {}
    assert dispatched["extra-data"] == {"s3-response": True}


def test_s3_sender_falls_back_to_inline_when_upload_fails(message_bus):
    http = CapturingHttpClient(error=OSError("network error"))
    sender = S3Sender(http_client=http)
    payload = {"report-id": "r1", "response-upload-url": "https://s3.example.com/upload"}

    sender.send(message_bus, Event("E", None, b""), {"rows": [1, 2, 3]}, payload)

    dispatched = json.loads(message_bus.published_events[0].data)
    assert dispatched["data"] == {"rows": [1, 2, 3]}
    assert "extra-data" not in dispatched


def test_s3_sender_sends_inline_and_skips_upload_when_no_url(message_bus):
    http = CapturingHttpClient()
    sender = S3Sender(http_client=http)

    sender.send(message_bus, Event("E", None, b""), {"rows": [1, 2, 3]}, {"report-id": "r1"})

    assert http.calls == []
    dispatched = json.loads(message_bus.published_events[0].data)
    assert dispatched["data"] == {"rows": [1, 2, 3]}
