import '@tidbcloud/uikit/style.css'
import 'virtual:uno.css'
import './global.css'

import * as Sentry from '@sentry/react'
import { useColorScheme } from '@tidbcloud/uikit'
import { HookFormProvider } from '@tidbcloud/uikit/biz'
import { useHotkeys } from '@tidbcloud/uikit/hooks'
import { FONT_FAMILY, ThemeProvider } from '@tidbcloud/uikit/theme'
import mixpanel from 'mixpanel-browser'
import { Provider } from 'mobx-react'
import { createRoot } from 'react-dom/client'
import { QueryClientProvider } from 'react-query'
import { matchPath } from 'react-router-dom'

import { log, LogErrorType } from 'common/log'
import config from 'config'
import App from 'dbaas/App'
import DbaaSBoundary from 'dbaas/components/ErrorBoundary'
import { heroTracing } from 'dbaas/components/Measurement'
import { modals } from 'dbaas/modals'
import { queryClient } from 'dbaas/reactQueryClient'
import { tracingRoutes } from 'dbaas/routes/tracing'
import { hostEnv } from 'dbaas/services/endpoint'
import { getErrorMessage } from 'dbaas/services/errorCodes'
import history from 'dbaas/services/history'
import { actions, store } from 'dbaas/stores'
import { shouldDowngrade, shouldSkipError } from 'dbaas/utils/errors'
import { getTraceSampleRate } from 'dbaas/utils/tracing/sampling'
import { isNavigationTransaction, stripNonCoreSpans } from 'dbaas/utils/tracing/span-filter'
import { enableAutoClickTracking } from 'dbaas/utils/tracking/enableAutoClickTracking'

import * as serviceWorkerRegistration from './registerServiceWorker'

window.APP_NAME = 'dbaas'

enableAutoClickTracking()

const enabledSentry = config.SENTRY_DEBUG || hostEnv === 'prod'

// SentryEnabled helps us doubl check that Sentry is enabled.
if (enabledSentry) {
  window.SentryEnabled = true
}

Sentry.init({
  enabled: enabledSentry,
  dsn: config.SENTRY_DSN,
  release: `${config.RELEASE_ENV}-${config.VERSION}-${config.DEPLOY_ID}`,
  environment: config.RELEASE_ENV,
  ignoreErrors: [
    'ResizeObserver loop limit exceeded',
    'An attempt was made to break through the security policy of the user agent',
    'Looks like your website URL has changed. To ensure the proper functioning of your banner',
    'ResizeObserver loop completed with undelivered notifications'
  ],
  denyUrls: [
    /ingesteer\.services-prod\.nsvcs\.net\/rum_collection/,
    /ws\.zoominfo\.com/,
    /static\.hotjar\.com/,
    /idsync\.rlcdn\.com/
  ],
  integrations: [
    Sentry.reactRouterV5BrowserTracingIntegration({
      history,
      routes: tracingRoutes,
      matchPath,
      // API requests are traced as independently sampled root spans by the Axios mutator.
      traceFetch: false,
      traceXHR: false
    })
  ],
  tracesSampler: (samplingContext) =>
    getTraceSampleRate(samplingContext, {
      pageload: config.PAGELOAD_TRACING_SAMPLING_RATE,
      customPageload: config.CUSTOM_PAGELOAD_TRACING_SAMPLING_RATE,
      navigation: config.NAVIGATION_TRACING_SAMPLING_RATE,
      default: config.DEFAULT_TRACING_SAMPLING_RATE,
      http: config.HTTP_TRACING_SAMPLING_RATE,
      pollingHttp: config.POLLING_HTTP_TRACING_SAMPLING_RATE
    }),
  beforeSendTransaction(event, hint) {
    if (isNavigationTransaction(event)) {
      return null
    }
    stripNonCoreSpans(event)
    const tags = event.tags || {}
    // only built-in transaction
    if (!tags.c_type && event.contexts?.trace?.op === 'pageload' && (heroTracing.apiError || heroTracing.feError)) {
      if (heroTracing.apiError) {
        event.tags = {
          ...tags,
          error_occurred: true,
          apiError: true
        }
      }
      if (heroTracing.feError) {
        event.tags = {
          ...tags,
          error_occurred: true,
          feError: true
        }
      }
      heroTracing.resetError()
    }
    if (hint?.originalException) {
      log.warning('Transaction Exception Hint: ', hint.originalException as any)
    }
    return event
  },
  beforeSend(event, hint) {
    const error = hint?.originalException

    if (event.tags?.errorBoundary === 'yes') {
      return event
    }

    let msg = ''
    // ignore ResizeObserver error
    if (error instanceof Error && error.message) {
      msg = error.message
    } else if (typeof error === 'string') {
      msg = error
    }

    // filter venfor fetch error
    if (shouldSkipError(msg)) {
      return null
    }

    if (shouldDowngrade(msg)) {
      // downgrade to warning
      event.level = 'warning'
    }

    if (event.level === 'error') {
      // only handle error
      const errorTriage = event.tags?.errorTriage
      if (errorTriage === LogErrorType.APIError) {
        heroTracing.apiError = true
      } else if (errorTriage !== LogErrorType.AuthError) {
        heroTracing.feError = true
      }
    }

    return event
  }
})
Sentry.setTag('version', config.VERSION)
Sentry.setTag('branch', config.BRANCH)

// setup mixpanel
mixpanel.init(config.MIXPANEL_TOKEN, { api_host: config.MIXPANEL_HOST || '', verbose: true, ignore_dnt: true }, '')

heroTracing.start()

const ProvideApp = () => {
  const { colorScheme, setColorScheme } = useColorScheme('light')

  const toggleColorScheme = () => {
    setColorScheme(colorScheme === 'dark' ? 'light' : 'dark')
  }

  useHotkeys([['mod+J', () => toggleColorScheme()]])

  return (
    // put store provider outside, because model need access store
    <QueryClientProvider client={queryClient}>
      <Provider store={store} actions={actions}>
        <ThemeProvider
          colorScheme={colorScheme}
          modals={{ modals }}
          notifications={{ position: 'top-center', zIndex: 9995 }}
          theme={{
            fontFamily: `'moderat', ${FONT_FAMILY}`
          }}
        >
          <DbaaSBoundary>
            <HookFormProvider value={{ onError: getErrorMessage }}>
              <App />
            </HookFormProvider>
          </DbaaSBoundary>
        </ThemeProvider>
      </Provider>
    </QueryClientProvider>
  )
}

const container = document.getElementById('app')
if (!container) {
  throw new Error(`Container element is ${container}`)
}
createRoot(container).render(<ProvideApp />)

// unregister service worker
serviceWorkerRegistration.unregister()
