from logging import Logger, getLogger
from typing import Any

import requests
from messagebus import Event, MessageBus

from .base_sender import BaseSender


class S3Sender(BaseSender):
    """Sender that uploads the response to S3 when response-upload-url is present in the payload.

    Falls back to inline dispatch if the upload fails or if the URL is absent.
    """

    def __init__(self, logger: Logger | None = None, http_client: Any | None = None):
        """Creates an S3Sender with optional logger and HTTP client.

        Args:
            logger: Logger for upload, fallback and missing-URL events.
            http_client: HTTP client compatible with requests (put/raise_for_status). Defaults to requests.
        """
        self._logger = logger or getLogger(__name__)
        self._http = http_client or requests

    def send(self, message_bus: MessageBus, original_event: Event, data: dict, original_payload: dict) -> None:
        """Uploads data to S3 if a URL is provided, then dispatches the response.

        Args:
            message_bus: Bus used to dispatch the message.
            original_event: Event received by the processor.
            data: Already-serialized response data.
            original_payload: Original message payload (provides report-id, sync, response-upload-url).
        """
        upload_url = original_payload.get("response-upload-url")
        event_data = {
            "report-id": original_payload.get("report-id"),
            "data": data,
        }
        if upload_url:
            try:
                response = self._http.put(upload_url, json=data, timeout=60)
                response.raise_for_status()
                event_data["data"] = {}
                event_data["extra-data"] = {"s3-response": True}
            except Exception as e:
                self._logger.error(
                    "Failed to upload to S3 for report-id %s, falling back to inline data",
                    original_payload.get("report-id"),
                    exc_info=e,
                )
        else:
            self._logger.warning(
                "No response-upload-url in payload, sending inline data for report-id %s",
                original_payload.get("report-id"),
            )
        self._dispatch(message_bus, original_event, event_data, original_payload)
