import json
from datetime import datetime

from django.utils.timezone import utc

from .base import AnymailBaseWebhookView
from ..signals import AnymailTrackingEvent, EventType, RejectReason, tracking


class SendinBlueTrackingWebhookView(AnymailBaseWebhookView):
    """Handler for SendinBlue delivery and engagement tracking webhooks"""

    esp_name = "SendinBlue"
    signal = tracking

    def parse_events(self, request):
        esp_event = json.loads(request.body.decode('utf-8'))
        return [self.esp_to_anymail_event(esp_event)]

    # SendinBlue's webhook payload data doesn't seem to be documented anywhere.
    # There's a list of webhook events at https://apidocs.sendinblue.com/webhooks/#3.
    event_types = {
        # Map SendinBlue event type: Anymail normalized (event type, reject reason)
        "request": (EventType.QUEUED, None),  # received even if message won't be sent (e.g., before "blocked")
        "delivered": (EventType.DELIVERED, None),
        "hard_bounce": (EventType.BOUNCED, RejectReason.BOUNCED),
        "soft_bounce": (EventType.BOUNCED, RejectReason.BOUNCED),
        "blocked": (EventType.REJECTED, RejectReason.BLOCKED),
        "spam": (EventType.COMPLAINED, RejectReason.SPAM),
        "invalid_email": (EventType.BOUNCED, RejectReason.INVALID),
        "deferred": (EventType.DEFERRED, None),
        "opened": (EventType.OPENED, None),  # see also unique_opened below
        "click": (EventType.CLICKED, None),
        "unsubscribe": (EventType.UNSUBSCRIBED, None),
        "list_addition": (EventType.SUBSCRIBED, None),  # shouldn't occur for transactional messages
        "unique_opened": (EventType.OPENED, None),  # you'll *also* receive an "opened"
    }

    def esp_to_anymail_event(self, esp_event):
        esp_type = esp_event.get("event")
        event_type, reject_reason = self.event_types.get(esp_type, (EventType.UNKNOWN, None))
        recipient = esp_event.get("email")

        try:
            # SendinBlue supplies "ts", "ts_event" and "date" fields, which seem to be based on the
            # timezone set in the account preferences (and possibly with inconsistent DST adjustment).
            # "ts_epoch" is the only field that seems to be consistently UTC; it's in milliseconds
            timestamp = datetime.fromtimestamp(esp_event["ts_epoch"] / 1000.0, tz=utc)
        except (KeyError, ValueError):
            timestamp = None

        try:
            tags = [esp_event["tag"]]
        except KeyError:
            tags = []

        try:
            metadata = json.loads(esp_event["X-Mailin-custom"])
        except (KeyError, TypeError):
            metadata = {}

        return AnymailTrackingEvent(
            description=None,
            esp_event=esp_event,
            event_id=None,  # SendinBlue doesn't provide a unique event id
            event_type=event_type,
            message_id=esp_event.get("message-id"),
            metadata=metadata,
            mta_response=esp_event.get("reason"),
            recipient=recipient,
            reject_reason=reject_reason,
            tags=tags,
            timestamp=timestamp,
            user_agent=None,
            click_url=esp_event.get("link"),
        )
