import hmac
import logging
from hashlib import sha256

from django.http import HttpResponse
from django.utils.crypto import constant_time_compare
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
from django.views.generic import View
from email_reply_parser import EmailReplyParser
from rest_framework.request import Request

from sentry import options
from sentry.tasks.email import process_inbound_email
from sentry.utils.email import email_to_group_id

logger = logging.getLogger("sentry.mailgun")


class MailgunInboundWebhookView(View):
    def verify(self, api_key, token, timestamp, signature):
        return constant_time_compare(
            signature,
            hmac.new(
                key=api_key.encode("utf-8"),
                msg=(f"{timestamp}{token}").encode(),
                digestmod=sha256,
            ).hexdigest(),
        )

    @method_decorator(csrf_exempt)
    def dispatch(self, *args, **kwargs):
        return super().dispatch(*args, **kwargs)

    def post(self, request: Request) -> HttpResponse:
        token = request.POST["token"]
        signature = request.POST["signature"]
        timestamp = request.POST["timestamp"]

        key = options.get("mail.mailgun-api-key")
        if not key:
            logger.error("mailgun.api-key-missing")
            return HttpResponse(status=500)

        if not self.verify(key, token, timestamp, signature):
            logger.info(
                "mailgun.invalid-signature",
                extra={"token": token, "timestamp": timestamp, "signature": signature},
            )
            return HttpResponse(status=200)

        to_email = request.POST["recipient"]
        from_email = request.POST["sender"]

        try:
            group_id = email_to_group_id(to_email)
        except Exception:
            logger.info("mailgun.invalid-email", extra={"email": to_email})
            return HttpResponse(status=200)

        payload = EmailReplyParser.parse_reply(request.POST["body-plain"]).strip()
        if not payload:
            # If there's no body, we don't need to go any further
            return HttpResponse(status=200)

        process_inbound_email.delay(from_email, group_id, payload)

        return HttpResponse(status=201)
