import time
import logging

import carb.settings
import carb
import omni.ext
import omni.kit.app
import omni.structuredlog

import sentry_sdk
from sentry_sdk.integrations.logging import LoggingIntegration

logger = logging.getLogger(__name__)


# Link was generated by perflab Sentry instance:
DSN = "https://b61f14dfc36f4beabadf4d9dba8e492b@sentry.perflab.nvidia.com/834"


CARB_LOG_LEVEL_TO_EVENT_LEVEL = {
    carb.logging.LEVEL_VERBOSE: "debug",
    carb.logging.LEVEL_INFO: "info",
    carb.logging.LEVEL_WARN: "warning",
    carb.logging.LEVEL_ERROR: "error",
    carb.logging.LEVEL_FATAL: "fatal"
}

class Extension(omni.ext.IExt):
    def on_startup(self):

        s = carb.settings.get_settings()

        if s.get("/privacy/externalBuild"):
            logger.info("sentry is disabled for external build")
            return

        if not s.get("/telemetry/enableSentry"):
            return

        app = omni.kit.app.get_app()

        # Setup user using already formed crashreporter data
        data = s.get("/crashreporter/data")
        if not data:
            data = {}

        user_id = data.get("userId", None)
        if user_id:
            sentry_sdk.set_user({"id": user_id})

        sentry_sdk.set_tag("app.name", app.get_app_name())
        sentry_sdk.set_tag("app.version", app.get_app_version())
        sentry_sdk.set_tag("app.environment", app.get_app_environment())

        # Pass telemetry session to sentry
        structlog_settings = omni.structuredlog.IStructuredLogSettings()
        if structlog_settings:
            sentry_sdk.set_tag("session_id", str(structlog_settings.session_id))

        time_start = time.time()
        sentry_sdk.init(
            dsn=DSN,
            traces_sample_rate=1.0,
            auto_enabling_integrations=False,
            integrations=[LoggingIntegration(event_level=None, level=None)], # Disable builtin logging integration
            release=app.get_kit_version()
        )
        logger.info("enabled Sentry...(time took: {:.2f}s)".format(time.time()-time_start))

        # Subscribe to IApp log listener which outputs carbonite log errors each frame on the main thread
        # We could have created custom python logger here, but it is known to create deadlocks with GIL because of being
        # called from many threads.
        def on_log_event(e):
            level = e.payload["level"]
            if level >= carb.logging.LEVEL_ERROR:
                event = {}
                event["level"] = CARB_LOG_LEVEL_TO_EVENT_LEVEL.get(level, "notset")
                event["logger"] = e.payload["source"]
                event["logentry"] = {
                    "message": e.payload["message"],
                    "params": (),
                }
                event["extra"] = {
                    "filename": e.payload["filename"],
                    "functionName": e.payload["functionName"],
                    "lineNumber": e.payload["lineNumber"]
                }
                sentry_sdk.capture_event(event)

        log_stream = omni.kit.app.get_app().get_log_event_stream()
        self._log_sub = log_stream.create_subscription_to_pop(on_log_event, name="omni.kit.telemetry.sentry_extension")


