from typing import TYPE_CHECKING, Optional

import sentry_sdk
from django.utils.translation import override as override_language
from sentry_sdk.integrations import Integration
from sentry_sdk.integrations.django import DjangoIntegration
from sentry_sdk.integrations.logging import ignore_logger
from sentry_sdk.integrations.redis import RedisIntegration
from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration
from sentry_sdk.utils import capture_internal_exceptions

from version import ZULIP_VERSION

from .config import PRODUCTION

if TYPE_CHECKING:
    from sentry_sdk._types import Event, Hint

def add_context(event: 'Event', hint: 'Hint') -> Optional['Event']:
    if "exc_info" in hint:
        _, exc_value, _ = hint["exc_info"]
        # Ignore GeneratorExit, KeyboardInterrupt, and SystemExit exceptions
        if not isinstance(exc_value, Exception):
            return None
    from django.conf import settings

    from zerver.models import get_user_profile_by_id
    with capture_internal_exceptions():
        user_info = event.get("user", {})
        if user_info.get("id"):
            user_profile = get_user_profile_by_id(user_info["id"])
            user_info["realm"] = user_profile.realm.string_id or 'root'
            with override_language(settings.LANGUAGE_CODE):
                # str() to force the lazy-translation to apply now,
                # since it won't serialize into json for Sentry otherwise
                user_info["role"] = str(user_profile.get_role_name())
    return event

def setup_sentry(dsn: Optional[str], *integrations: Integration) -> None:
    if not dsn:
        return
    sentry_sdk.init(
        dsn=dsn,
        environment="production" if PRODUCTION else "development",
        release=ZULIP_VERSION,
        integrations=[
            DjangoIntegration(),
            RedisIntegration(),
            SqlalchemyIntegration(),
            *integrations,
        ],
        before_send=add_context,
    )

    # Ignore all of the loggers from django.security that are for user
    # errors; see https://docs.djangoproject.com/en/3.0/ref/exceptions/#suspiciousoperation
    ignore_logger("django.security.SuspiciousOperation")
    ignore_logger("django.security.DisallowedHost")
    ignore_logger("django.security.DisallowedModelAdminLookup")
    ignore_logger("django.security.DisallowedModelAdminToField")
    ignore_logger("django.security.DisallowedRedirect")
    ignore_logger("django.security.InvalidSessionKey")
    ignore_logger("django.security.RequestDataTooBig")
    ignore_logger("django.security.SuspiciousFileOperation")
    ignore_logger("django.security.SuspiciousMultipartForm")
    ignore_logger("django.security.SuspiciousSession")
    ignore_logger("django.security.TooManyFieldsSent")

@@ -1026,16 +1026,263 @@
 ions():%0A
+        # event.user is the user context, from Sentry, which is%0A        # pre-populated with some keys via its Django integration:%0A        # https://docs.sentry.io/platforms/python/guides/django/enriching-error-data/additional-data/identify-user/%0A
         
@@ -1315,16 +1315,16 @@
 r%22, %7B%7D)%0A
-
         
@@ -1758,16 +1758,206 @@
 name())%0A
+        # These are PII, and should be scrubbed%0A        if %22username%22 in user_info:%0A            del user_info%5B%22username%22%5D%0A        if %22email%22 in user_info:%0A            del user_info%5B%22email%22%5D%0A
     retu
@@ -2370,16 +2370,16 @@
      %5D,%0A
-
         
@@ -2403,16 +2403,528 @@
 ontext,%0A
+        # Because we strip the email/username from the Sentry data%0A        # above, the effect of this flag is that the requests/users%0A        # involved in exceptions will be identified in Sentry only by%0A        # their IP address, user ID, realm, and role.  We consider%0A        # this an appropriate balance between avoiding Sentry getting%0A        # PII while having the identifiers needed to determine that an%0A        # exception only affects a small subset of users or realms.%0A        send_default_pii=True,%0A
     )%0A%0A 

Send user identifiers, but strip PII.